blob: 7b62da9d3384ea44c81470f3d4f1d1b6150a9d2c [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
ager@chromium.org65dad4b2009-04-23 08:48:43 +000052#define __ ACCESS_MASM(masm_)
53
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000054static void EmitIdenticalObjectComparison(MacroAssembler* masm,
55 Label* slow,
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000056 Condition cc,
57 bool never_nan_nan);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000058static void EmitSmiNonsmiComparison(MacroAssembler* masm,
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000059 Label* lhs_not_nan,
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000060 Label* slow,
61 bool strict);
62static void EmitTwoNonNanDoubleComparison(MacroAssembler* masm, Condition cc);
63static void EmitStrictTwoHeapObjectCompare(MacroAssembler* masm);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000064static void MultiplyByKnownInt(MacroAssembler* masm,
65 Register source,
66 Register destination,
67 int known_int);
68static bool IsEasyToMultiplyBy(int x);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000069
70
71
ager@chromium.orge2902be2009-06-08 12:21:35 +000072// -------------------------------------------------------------------------
73// Platform-specific DeferredCode functions.
74
75void DeferredCode::SaveRegisters() {
76 for (int i = 0; i < RegisterAllocator::kNumRegisters; i++) {
77 int action = registers_[i];
78 if (action == kPush) {
79 __ push(RegisterAllocator::ToRegister(i));
80 } else if (action != kIgnore && (action & kSyncedFlag) == 0) {
81 __ str(RegisterAllocator::ToRegister(i), MemOperand(fp, action));
82 }
83 }
84}
85
86
87void DeferredCode::RestoreRegisters() {
88 // Restore registers in reverse order due to the stack.
89 for (int i = RegisterAllocator::kNumRegisters - 1; i >= 0; i--) {
90 int action = registers_[i];
91 if (action == kPush) {
92 __ pop(RegisterAllocator::ToRegister(i));
93 } else if (action != kIgnore) {
94 action &= ~kSyncedFlag;
95 __ ldr(RegisterAllocator::ToRegister(i), MemOperand(fp, action));
96 }
97 }
98}
99
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000100
101// -------------------------------------------------------------------------
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000102// CodeGenState implementation.
103
ager@chromium.org7c537e22008-10-16 08:43:32 +0000104CodeGenState::CodeGenState(CodeGenerator* owner)
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000105 : owner_(owner),
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000106 true_target_(NULL),
107 false_target_(NULL),
108 previous_(NULL) {
109 owner_->set_state(this);
110}
111
112
ager@chromium.org7c537e22008-10-16 08:43:32 +0000113CodeGenState::CodeGenState(CodeGenerator* owner,
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000114 JumpTarget* true_target,
115 JumpTarget* false_target)
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000116 : owner_(owner),
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000117 true_target_(true_target),
118 false_target_(false_target),
119 previous_(owner->state()) {
120 owner_->set_state(this);
121}
122
123
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000124CodeGenState::~CodeGenState() {
125 ASSERT(owner_->state() == this);
126 owner_->set_state(previous_);
127}
128
129
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000130// -------------------------------------------------------------------------
ager@chromium.org7c537e22008-10-16 08:43:32 +0000131// CodeGenerator implementation
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000132
ager@chromium.org5c838252010-02-19 08:53:10 +0000133CodeGenerator::CodeGenerator(MacroAssembler* masm)
134 : deferred_(8),
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000135 masm_(masm),
ager@chromium.org5c838252010-02-19 08:53:10 +0000136 info_(NULL),
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000137 frame_(NULL),
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000138 allocator_(NULL),
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000139 cc_reg_(al),
140 state_(NULL),
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000141 loop_nesting_(0),
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +0000142 function_return_is_shadowed_(false) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000143}
144
145
146// Calling conventions:
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000147// fp: caller's frame pointer
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000148// sp: stack pointer
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000149// r1: called JS function
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000150// cp: callee's context
151
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000152void CodeGenerator::Generate(CompilationInfo* info) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000153 // Record the position for debugging purposes.
ager@chromium.org5c838252010-02-19 08:53:10 +0000154 CodeForFunctionPosition(info->function());
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000155 Comment cmnt(masm_, "[ function compiled by virtual frame code generator");
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000156
157 // Initialize state.
ager@chromium.org5c838252010-02-19 08:53:10 +0000158 info_ = info;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000159 ASSERT(allocator_ == NULL);
160 RegisterAllocator register_allocator(this);
161 allocator_ = &register_allocator;
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000162 ASSERT(frame_ == NULL);
kasperl@chromium.org71affb52009-05-26 05:44:31 +0000163 frame_ = new VirtualFrame();
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000164 cc_reg_ = al;
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000165
166 // Adjust for function-level loop nesting.
167 ASSERT_EQ(0, loop_nesting_);
168 loop_nesting_ = info->loop_nesting();
169
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000170 {
171 CodeGenState state(this);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000172
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000173 // Entry:
174 // Stack: receiver, arguments
175 // lr: return address
176 // fp: caller's frame pointer
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000177 // sp: stack pointer
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000178 // r1: called JS function
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000179 // cp: callee's context
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000180 allocator_->Initialize();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000181
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000182#ifdef DEBUG
183 if (strlen(FLAG_stop_at) > 0 &&
ager@chromium.org5c838252010-02-19 08:53:10 +0000184 info->function()->name()->IsEqualTo(CStrVector(FLAG_stop_at))) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000185 frame_->SpillAll();
kasper.lund7276f142008-07-30 08:49:36 +0000186 __ stop("stop-at");
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000187 }
188#endif
189
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000190 if (info->mode() == CompilationInfo::PRIMARY) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000191 frame_->Enter();
192 // tos: code slot
193
194 // Allocate space for locals and initialize them. This also checks
195 // for stack overflow.
196 frame_->AllocateStackSlots();
197
ager@chromium.org357bf652010-04-12 11:30:10 +0000198 VirtualFrame::SpilledScope spilled_scope(frame_);
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +0000199 int heap_slots = scope()->num_heap_slots() - Context::MIN_CONTEXT_SLOTS;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000200 if (heap_slots > 0) {
201 // Allocate local context.
202 // Get outer context and create a new context based on it.
203 __ ldr(r0, frame_->Function());
204 frame_->EmitPush(r0);
205 if (heap_slots <= FastNewContextStub::kMaximumSlots) {
206 FastNewContextStub stub(heap_slots);
207 frame_->CallStub(&stub, 1);
208 } else {
209 frame_->CallRuntime(Runtime::kNewContext, 1);
210 }
211
212#ifdef DEBUG
213 JumpTarget verified_true;
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000214 __ cmp(r0, cp);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000215 verified_true.Branch(eq);
216 __ stop("NewContext: r0 is expected to be the same as cp");
217 verified_true.Bind();
218#endif
219 // Update context local.
220 __ str(cp, frame_->Context());
221 }
222
223 // TODO(1241774): Improve this code:
224 // 1) only needed if we have a context
225 // 2) no need to recompute context ptr every single time
226 // 3) don't copy parameter operand code from SlotOperand!
227 {
228 Comment cmnt2(masm_, "[ copy context parameters into .context");
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000229 // Note that iteration order is relevant here! If we have the same
230 // parameter twice (e.g., function (x, y, x)), and that parameter
231 // needs to be copied into the context, it must be the last argument
232 // passed to the parameter that needs to be copied. This is a rare
233 // case so we don't check for it, instead we rely on the copying
234 // order: such a parameter is copied repeatedly into the same
235 // context location and thus the last value is what is seen inside
236 // the function.
ager@chromium.org5c838252010-02-19 08:53:10 +0000237 for (int i = 0; i < scope()->num_parameters(); i++) {
238 Variable* par = scope()->parameter(i);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000239 Slot* slot = par->slot();
240 if (slot != NULL && slot->type() == Slot::CONTEXT) {
ager@chromium.org5c838252010-02-19 08:53:10 +0000241 ASSERT(!scope()->is_global_scope()); // No params in global scope.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000242 __ ldr(r1, frame_->ParameterAt(i));
243 // Loads r2 with context; used below in RecordWrite.
244 __ str(r1, SlotOperand(slot, r2));
245 // Load the offset into r3.
246 int slot_offset =
247 FixedArray::kHeaderSize + slot->index() * kPointerSize;
248 __ mov(r3, Operand(slot_offset));
249 __ RecordWrite(r2, r3, r1);
250 }
251 }
252 }
253
254 // Store the arguments object. This must happen after context
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000255 // initialization because the arguments object may be stored in
256 // the context.
257 if (ArgumentsMode() != NO_ARGUMENTS_ALLOCATION) {
258 StoreArgumentsObject(true);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000259 }
260
261 // Initialize ThisFunction reference if present.
ager@chromium.org5c838252010-02-19 08:53:10 +0000262 if (scope()->is_function_scope() && scope()->function() != NULL) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000263 __ mov(ip, Operand(Factory::the_hole_value()));
264 frame_->EmitPush(ip);
ager@chromium.org5c838252010-02-19 08:53:10 +0000265 StoreToSlot(scope()->function()->slot(), NOT_CONST_INIT);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000266 }
267 } else {
268 // When used as the secondary compiler for splitting, r1, cp,
269 // fp, and lr have been pushed on the stack. Adjust the virtual
270 // frame to match this state.
271 frame_->Adjust(4);
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000272
273 // Bind all the bailout labels to the beginning of the function.
274 List<CompilationInfo::Bailout*>* bailouts = info->bailouts();
275 for (int i = 0; i < bailouts->length(); i++) {
276 __ bind(bailouts->at(i)->label());
277 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000278 }
279
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000280 // Initialize the function return target after the locals are set
281 // up, because it needs the expected frame height from the frame.
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000282 function_return_.SetExpectedHeight();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000283 function_return_is_shadowed_ = false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000284
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000285 // Generate code to 'execute' declarations and initialize functions
286 // (source elements). In case of an illegal redeclaration we need to
287 // handle that instead of processing the declarations.
ager@chromium.org5c838252010-02-19 08:53:10 +0000288 if (scope()->HasIllegalRedeclaration()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000289 Comment cmnt(masm_, "[ illegal redeclarations");
ager@chromium.org5c838252010-02-19 08:53:10 +0000290 scope()->VisitIllegalRedeclaration(this);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000291 } else {
292 Comment cmnt(masm_, "[ declarations");
ager@chromium.org5c838252010-02-19 08:53:10 +0000293 ProcessDeclarations(scope()->declarations());
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000294 // Bail out if a stack-overflow exception occurred when processing
295 // declarations.
kasper.lund212ac232008-07-16 07:07:30 +0000296 if (HasStackOverflow()) return;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000297 }
298
mads.s.ager31e71382008-08-13 09:32:07 +0000299 if (FLAG_trace) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000300 frame_->CallRuntime(Runtime::kTraceEnter, 0);
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000301 // Ignore the return value.
mads.s.ager31e71382008-08-13 09:32:07 +0000302 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000303
304 // Compile the body of the function in a vanilla state. Don't
305 // bother compiling all the code if the scope has an illegal
306 // redeclaration.
ager@chromium.org5c838252010-02-19 08:53:10 +0000307 if (!scope()->HasIllegalRedeclaration()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000308 Comment cmnt(masm_, "[ function body");
309#ifdef DEBUG
310 bool is_builtin = Bootstrapper::IsActive();
311 bool should_trace =
312 is_builtin ? FLAG_trace_builtin_calls : FLAG_trace_calls;
mads.s.ager31e71382008-08-13 09:32:07 +0000313 if (should_trace) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000314 frame_->CallRuntime(Runtime::kDebugTrace, 0);
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000315 // Ignore the return value.
mads.s.ager31e71382008-08-13 09:32:07 +0000316 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000317#endif
ager@chromium.org5c838252010-02-19 08:53:10 +0000318 VisitStatementsAndSpill(info->function()->body());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000319 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000320 }
321
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000322 // Generate the return sequence if necessary.
ager@chromium.org4af710e2009-09-15 12:20:11 +0000323 if (has_valid_frame() || function_return_.is_linked()) {
324 if (!function_return_.is_linked()) {
ager@chromium.org5c838252010-02-19 08:53:10 +0000325 CodeForReturnPosition(info->function());
ager@chromium.org4af710e2009-09-15 12:20:11 +0000326 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000327 // exit
328 // r0: result
329 // sp: stack pointer
330 // fp: frame pointer
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000331 // cp: callee's context
ager@chromium.orgab99eea2009-08-25 07:05:41 +0000332 __ LoadRoot(r0, Heap::kUndefinedValueRootIndex);
mads.s.ager31e71382008-08-13 09:32:07 +0000333
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000334 function_return_.Bind();
335 if (FLAG_trace) {
336 // Push the return value on the stack as the parameter.
337 // Runtime::TraceExit returns the parameter as it is.
338 frame_->EmitPush(r0);
339 frame_->CallRuntime(Runtime::kTraceExit, 1);
340 }
341
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000342#ifdef DEBUG
ager@chromium.org4af710e2009-09-15 12:20:11 +0000343 // Add a label for checking the size of the code used for returning.
344 Label check_exit_codesize;
345 masm_->bind(&check_exit_codesize);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000346#endif
347 // Make sure that the constant pool is not emitted inside of the return
348 // sequence.
349 { Assembler::BlockConstPoolScope block_const_pool(masm_);
350 // Tear down the frame which will restore the caller's frame pointer and
351 // the link register.
352 frame_->Exit();
ager@chromium.org4af710e2009-09-15 12:20:11 +0000353
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000354 // Here we use masm_-> instead of the __ macro to avoid the code coverage
355 // tool from instrumenting as we rely on the code size here.
356 int32_t sp_delta = (scope()->num_parameters() + 1) * kPointerSize;
357 masm_->add(sp, sp, Operand(sp_delta));
358 masm_->Jump(lr);
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000359
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000360#ifdef DEBUG
lrn@chromium.orgc34f5802010-04-28 12:53:43 +0000361 // Check that the size of the code used for returning matches what is
362 // expected by the debugger. If the sp_delts above cannot be encoded in
363 // the add instruction the add will generate two instructions.
364 int return_sequence_length =
365 masm_->InstructionsGeneratedSince(&check_exit_codesize);
366 CHECK(return_sequence_length == Assembler::kJSReturnSequenceLength ||
367 return_sequence_length == Assembler::kJSReturnSequenceLength + 1);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000368#endif
lrn@chromium.orgc34f5802010-04-28 12:53:43 +0000369 }
mads.s.ager31e71382008-08-13 09:32:07 +0000370 }
371
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000372 // Adjust for function-level loop nesting.
373 ASSERT(loop_nesting_ == info->loop_nesting());
374 loop_nesting_ = 0;
375
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000376 // Code generation state must be reset.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000377 ASSERT(!has_cc());
378 ASSERT(state_ == NULL);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000379 ASSERT(loop_nesting() == 0);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000380 ASSERT(!function_return_is_shadowed_);
381 function_return_.Unuse();
382 DeleteFrame();
383
384 // Process any deferred code using the register allocator.
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +0000385 if (!HasStackOverflow()) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000386 ProcessDeferred();
387 }
388
389 allocator_ = NULL;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000390}
391
392
ager@chromium.org7c537e22008-10-16 08:43:32 +0000393MemOperand CodeGenerator::SlotOperand(Slot* slot, Register tmp) {
394 // Currently, this assertion will fail if we try to assign to
395 // a constant variable that is constant because it is read-only
396 // (such as the variable referring to a named function expression).
397 // We need to implement assignments to read-only variables.
398 // Ideally, we should do this during AST generation (by converting
399 // such assignments into expression statements); however, in general
400 // we may not be able to make the decision until past AST generation,
401 // that is when the entire program is known.
402 ASSERT(slot != NULL);
403 int index = slot->index();
404 switch (slot->type()) {
405 case Slot::PARAMETER:
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000406 return frame_->ParameterAt(index);
ager@chromium.org7c537e22008-10-16 08:43:32 +0000407
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000408 case Slot::LOCAL:
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000409 return frame_->LocalAt(index);
ager@chromium.org7c537e22008-10-16 08:43:32 +0000410
411 case Slot::CONTEXT: {
412 // Follow the context chain if necessary.
413 ASSERT(!tmp.is(cp)); // do not overwrite context register
414 Register context = cp;
415 int chain_length = scope()->ContextChainLength(slot->var()->scope());
ager@chromium.org381abbb2009-02-25 13:23:22 +0000416 for (int i = 0; i < chain_length; i++) {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000417 // Load the closure.
418 // (All contexts, even 'with' contexts, have a closure,
419 // and it is the same for all contexts inside a function.
420 // There is no need to go to the function context first.)
421 __ ldr(tmp, ContextOperand(context, Context::CLOSURE_INDEX));
422 // Load the function context (which is the incoming, outer context).
423 __ ldr(tmp, FieldMemOperand(tmp, JSFunction::kContextOffset));
424 context = tmp;
425 }
426 // We may have a 'with' context now. Get the function context.
427 // (In fact this mov may never be the needed, since the scope analysis
428 // may not permit a direct context access in this case and thus we are
429 // always at a function context. However it is safe to dereference be-
430 // cause the function context of a function context is itself. Before
431 // deleting this mov we should try to create a counter-example first,
432 // though...)
433 __ ldr(tmp, ContextOperand(context, Context::FCONTEXT_INDEX));
434 return ContextOperand(tmp, index);
435 }
436
437 default:
438 UNREACHABLE();
439 return MemOperand(r0, 0);
440 }
441}
442
443
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000444MemOperand CodeGenerator::ContextSlotOperandCheckExtensions(
445 Slot* slot,
446 Register tmp,
447 Register tmp2,
448 JumpTarget* slow) {
ager@chromium.org381abbb2009-02-25 13:23:22 +0000449 ASSERT(slot->type() == Slot::CONTEXT);
ager@chromium.org381abbb2009-02-25 13:23:22 +0000450 Register context = cp;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000451
ager@chromium.org381abbb2009-02-25 13:23:22 +0000452 for (Scope* s = scope(); s != slot->var()->scope(); s = s->outer_scope()) {
453 if (s->num_heap_slots() > 0) {
454 if (s->calls_eval()) {
455 // Check that extension is NULL.
456 __ ldr(tmp2, ContextOperand(context, Context::EXTENSION_INDEX));
457 __ tst(tmp2, tmp2);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000458 slow->Branch(ne);
ager@chromium.org381abbb2009-02-25 13:23:22 +0000459 }
460 __ ldr(tmp, ContextOperand(context, Context::CLOSURE_INDEX));
461 __ ldr(tmp, FieldMemOperand(tmp, JSFunction::kContextOffset));
462 context = tmp;
463 }
464 }
465 // Check that last extension is NULL.
466 __ ldr(tmp2, ContextOperand(context, Context::EXTENSION_INDEX));
467 __ tst(tmp2, tmp2);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000468 slow->Branch(ne);
ager@chromium.org381abbb2009-02-25 13:23:22 +0000469 __ ldr(tmp, ContextOperand(context, Context::FCONTEXT_INDEX));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000470 return ContextOperand(tmp, slot->index());
ager@chromium.org381abbb2009-02-25 13:23:22 +0000471}
472
473
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000474// Loads a value on TOS. If it is a boolean value, the result may have been
475// (partially) translated into branches, or it may have set the condition
476// code register. If force_cc is set, the value is forced to set the
477// condition code register and no value is pushed. If the condition code
478// register was set, has_cc() is true and cc_reg_ contains the condition to
479// test for 'true'.
ager@chromium.org7c537e22008-10-16 08:43:32 +0000480void CodeGenerator::LoadCondition(Expression* x,
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000481 JumpTarget* true_target,
482 JumpTarget* false_target,
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000483 bool force_cc) {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000484 ASSERT(!has_cc());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000485 int original_height = frame_->height();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000486
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000487 { CodeGenState new_state(this, true_target, false_target);
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000488 Visit(x);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000489
490 // If we hit a stack overflow, we may not have actually visited
491 // the expression. In that case, we ensure that we have a
492 // valid-looking frame state because we will continue to generate
493 // code as we unwind the C++ stack.
494 //
495 // It's possible to have both a stack overflow and a valid frame
496 // state (eg, a subexpression overflowed, visiting it returned
497 // with a dummied frame state, and visiting this expression
498 // returned with a normal-looking state).
499 if (HasStackOverflow() &&
500 has_valid_frame() &&
501 !has_cc() &&
502 frame_->height() == original_height) {
ager@chromium.org357bf652010-04-12 11:30:10 +0000503 frame_->SpillAll();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000504 true_target->Jump();
505 }
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000506 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000507 if (force_cc && frame_ != NULL && !has_cc()) {
mads.s.ager31e71382008-08-13 09:32:07 +0000508 // Convert the TOS value to a boolean in the condition code register.
509 ToBoolean(true_target, false_target);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000510 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000511 ASSERT(!force_cc || !has_valid_frame() || has_cc());
512 ASSERT(!has_valid_frame() ||
513 (has_cc() && frame_->height() == original_height) ||
514 (!has_cc() && frame_->height() == original_height + 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000515}
516
517
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000518void CodeGenerator::Load(Expression* expr) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000519#ifdef DEBUG
520 int original_height = frame_->height();
521#endif
kasperl@chromium.org71affb52009-05-26 05:44:31 +0000522 JumpTarget true_target;
523 JumpTarget false_target;
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000524 LoadCondition(expr, &true_target, &false_target, false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000525
526 if (has_cc()) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000527 // Convert cc_reg_ into a boolean value.
ager@chromium.org357bf652010-04-12 11:30:10 +0000528 VirtualFrame::SpilledScope scope(frame_);
kasperl@chromium.org71affb52009-05-26 05:44:31 +0000529 JumpTarget loaded;
530 JumpTarget materialize_true;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000531 materialize_true.Branch(cc_reg_);
ager@chromium.orgab99eea2009-08-25 07:05:41 +0000532 __ LoadRoot(r0, Heap::kFalseValueRootIndex);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000533 frame_->EmitPush(r0);
534 loaded.Jump();
535 materialize_true.Bind();
ager@chromium.orgab99eea2009-08-25 07:05:41 +0000536 __ LoadRoot(r0, Heap::kTrueValueRootIndex);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000537 frame_->EmitPush(r0);
538 loaded.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000539 cc_reg_ = al;
540 }
541
542 if (true_target.is_linked() || false_target.is_linked()) {
ager@chromium.org357bf652010-04-12 11:30:10 +0000543 VirtualFrame::SpilledScope scope(frame_);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000544 // We have at least one condition value that has been "translated"
545 // into a branch, thus it needs to be loaded explicitly.
kasperl@chromium.org71affb52009-05-26 05:44:31 +0000546 JumpTarget loaded;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000547 if (frame_ != NULL) {
548 loaded.Jump(); // Don't lose the current TOS.
549 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000550 bool both = true_target.is_linked() && false_target.is_linked();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000551 // Load "true" if necessary.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000552 if (true_target.is_linked()) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000553 true_target.Bind();
ager@chromium.orgab99eea2009-08-25 07:05:41 +0000554 __ LoadRoot(r0, Heap::kTrueValueRootIndex);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000555 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000556 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000557 // If both "true" and "false" need to be loaded jump across the code for
558 // "false".
559 if (both) {
560 loaded.Jump();
561 }
562 // Load "false" if necessary.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000563 if (false_target.is_linked()) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000564 false_target.Bind();
ager@chromium.orgab99eea2009-08-25 07:05:41 +0000565 __ LoadRoot(r0, Heap::kFalseValueRootIndex);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000566 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000567 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000568 // A value is loaded on all paths reaching this point.
569 loaded.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000570 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000571 ASSERT(has_valid_frame());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000572 ASSERT(!has_cc());
ager@chromium.orgac091b72010-05-05 07:34:42 +0000573 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000574}
575
576
ager@chromium.org7c537e22008-10-16 08:43:32 +0000577void CodeGenerator::LoadGlobal() {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +0000578 Register reg = frame_->GetTOSRegister();
579 __ ldr(reg, GlobalObject());
580 frame_->EmitPush(reg);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000581}
582
583
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000584void CodeGenerator::LoadGlobalReceiver(Register scratch) {
ager@chromium.org357bf652010-04-12 11:30:10 +0000585 VirtualFrame::SpilledScope spilled_scope(frame_);
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000586 __ ldr(scratch, ContextOperand(cp, Context::GLOBAL_INDEX));
587 __ ldr(scratch,
588 FieldMemOperand(scratch, GlobalObject::kGlobalReceiverOffset));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000589 frame_->EmitPush(scratch);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000590}
591
592
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000593ArgumentsAllocationMode CodeGenerator::ArgumentsMode() {
594 if (scope()->arguments() == NULL) return NO_ARGUMENTS_ALLOCATION;
595 ASSERT(scope()->arguments_shadow() != NULL);
596 // We don't want to do lazy arguments allocation for functions that
597 // have heap-allocated contexts, because it interfers with the
598 // uninitialized const tracking in the context objects.
599 return (scope()->num_heap_slots() > 0)
600 ? EAGER_ARGUMENTS_ALLOCATION
601 : LAZY_ARGUMENTS_ALLOCATION;
602}
603
604
605void CodeGenerator::StoreArgumentsObject(bool initial) {
606 VirtualFrame::SpilledScope spilled_scope(frame_);
607
608 ArgumentsAllocationMode mode = ArgumentsMode();
609 ASSERT(mode != NO_ARGUMENTS_ALLOCATION);
610
611 Comment cmnt(masm_, "[ store arguments object");
612 if (mode == LAZY_ARGUMENTS_ALLOCATION && initial) {
613 // When using lazy arguments allocation, we store the hole value
614 // as a sentinel indicating that the arguments object hasn't been
615 // allocated yet.
616 __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
617 frame_->EmitPush(ip);
618 } else {
619 ArgumentsAccessStub stub(ArgumentsAccessStub::NEW_OBJECT);
620 __ ldr(r2, frame_->Function());
621 // The receiver is below the arguments, the return address, and the
622 // frame pointer on the stack.
623 const int kReceiverDisplacement = 2 + scope()->num_parameters();
624 __ add(r1, fp, Operand(kReceiverDisplacement * kPointerSize));
625 __ mov(r0, Operand(Smi::FromInt(scope()->num_parameters())));
626 frame_->Adjust(3);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +0000627 __ Push(r2, r1, r0);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000628 frame_->CallStub(&stub, 3);
629 frame_->EmitPush(r0);
630 }
631
632 Variable* arguments = scope()->arguments()->var();
633 Variable* shadow = scope()->arguments_shadow()->var();
634 ASSERT(arguments != NULL && arguments->slot() != NULL);
635 ASSERT(shadow != NULL && shadow->slot() != NULL);
636 JumpTarget done;
637 if (mode == LAZY_ARGUMENTS_ALLOCATION && !initial) {
638 // We have to skip storing into the arguments slot if it has
639 // already been written to. This can happen if the a function
640 // has a local variable named 'arguments'.
641 LoadFromSlot(scope()->arguments()->var()->slot(), NOT_INSIDE_TYPEOF);
642 frame_->EmitPop(r0);
643 __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
644 __ cmp(r0, ip);
645 done.Branch(ne);
646 }
647 StoreToSlot(arguments->slot(), NOT_CONST_INIT);
648 if (mode == LAZY_ARGUMENTS_ALLOCATION) done.Bind();
649 StoreToSlot(shadow->slot(), NOT_CONST_INIT);
650}
651
652
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000653void CodeGenerator::LoadTypeofExpression(Expression* expr) {
654 // Special handling of identifiers as subexpressions of typeof.
ager@chromium.org357bf652010-04-12 11:30:10 +0000655 VirtualFrame::SpilledScope spilled_scope(frame_);
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000656 Variable* variable = expr->AsVariableProxy()->AsVariable();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000657 if (variable != NULL && !variable->is_this() && variable->is_global()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000658 // For a global variable we build the property reference
659 // <global>.<variable> and perform a (regular non-contextual) property
660 // load to make sure we do not get reference errors.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000661 Slot global(variable, Slot::CONTEXT, Context::GLOBAL_INDEX);
662 Literal key(variable->name());
ager@chromium.org236ad962008-09-25 09:45:57 +0000663 Property property(&global, &key, RelocInfo::kNoPosition);
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000664 Reference ref(this, &property);
ager@chromium.org357bf652010-04-12 11:30:10 +0000665 ref.GetValue();
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000666 } else if (variable != NULL && variable->slot() != NULL) {
667 // For a variable that rewrites to a slot, we signal it is the immediate
668 // subexpression of a typeof.
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000669 LoadFromSlotCheckForArguments(variable->slot(), INSIDE_TYPEOF);
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000670 frame_->SpillAll();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000671 } else {
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000672 // Anything else can be handled normally.
673 LoadAndSpill(expr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000674 }
675}
676
677
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000678Reference::Reference(CodeGenerator* cgen,
679 Expression* expression,
680 bool persist_after_get)
681 : cgen_(cgen),
682 expression_(expression),
683 type_(ILLEGAL),
684 persist_after_get_(persist_after_get) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000685 cgen->LoadReference(this);
686}
687
688
689Reference::~Reference() {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000690 ASSERT(is_unloaded() || is_illegal());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000691}
692
693
ager@chromium.org7c537e22008-10-16 08:43:32 +0000694void CodeGenerator::LoadReference(Reference* ref) {
695 Comment cmnt(masm_, "[ LoadReference");
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000696 Expression* e = ref->expression();
697 Property* property = e->AsProperty();
698 Variable* var = e->AsVariableProxy()->AsVariable();
699
700 if (property != NULL) {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000701 // The expression is either a property or a variable proxy that rewrites
702 // to a property.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +0000703 Load(property->obj());
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000704 if (property->key()->IsPropertyName()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000705 ref->set_type(Reference::NAMED);
706 } else {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +0000707 Load(property->key());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000708 ref->set_type(Reference::KEYED);
709 }
710 } else if (var != NULL) {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000711 // The expression is a variable proxy that does not rewrite to a
712 // property. Global variables are treated as named property references.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000713 if (var->is_global()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000714 LoadGlobal();
715 ref->set_type(Reference::NAMED);
716 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000717 ASSERT(var->slot() != NULL);
718 ref->set_type(Reference::SLOT);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000719 }
720 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000721 // Anything else is a runtime error.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +0000722 VirtualFrame::SpilledScope spilled_scope(frame_);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000723 LoadAndSpill(e);
724 frame_->CallRuntime(Runtime::kThrowReferenceError, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000725 }
726}
727
728
ager@chromium.org7c537e22008-10-16 08:43:32 +0000729void CodeGenerator::UnloadReference(Reference* ref) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000730 int size = ref->size();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000731 ref->set_unloaded();
ager@chromium.org357bf652010-04-12 11:30:10 +0000732 if (size == 0) return;
733
734 // Pop a reference from the stack while preserving TOS.
735 VirtualFrame::RegisterAllocationScope scope(this);
736 Comment cmnt(masm_, "[ UnloadReference");
737 if (size > 0) {
738 Register tos = frame_->PopToRegister();
739 frame_->Drop(size);
740 frame_->EmitPush(tos);
741 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000742}
743
744
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000745// ECMA-262, section 9.2, page 30: ToBoolean(). Convert the given
746// register to a boolean in the condition code register. The code
747// may jump to 'false_target' in case the register converts to 'false'.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000748void CodeGenerator::ToBoolean(JumpTarget* true_target,
749 JumpTarget* false_target) {
ager@chromium.org357bf652010-04-12 11:30:10 +0000750 VirtualFrame::SpilledScope spilled_scope(frame_);
mads.s.ager31e71382008-08-13 09:32:07 +0000751 // Note: The generated code snippet does not change stack variables.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000752 // Only the condition code should be set.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000753 frame_->EmitPop(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000754
755 // Fast case checks
756
mads.s.ager31e71382008-08-13 09:32:07 +0000757 // Check if the value is 'false'.
ager@chromium.orgab99eea2009-08-25 07:05:41 +0000758 __ LoadRoot(ip, Heap::kFalseValueRootIndex);
759 __ cmp(r0, ip);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000760 false_target->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000761
mads.s.ager31e71382008-08-13 09:32:07 +0000762 // Check if the value is 'true'.
ager@chromium.orgab99eea2009-08-25 07:05:41 +0000763 __ LoadRoot(ip, Heap::kTrueValueRootIndex);
764 __ cmp(r0, ip);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000765 true_target->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000766
mads.s.ager31e71382008-08-13 09:32:07 +0000767 // Check if the value is 'undefined'.
ager@chromium.orgab99eea2009-08-25 07:05:41 +0000768 __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
769 __ cmp(r0, ip);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000770 false_target->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000771
mads.s.ager31e71382008-08-13 09:32:07 +0000772 // Check if the value is a smi.
773 __ cmp(r0, Operand(Smi::FromInt(0)));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000774 false_target->Branch(eq);
mads.s.ager31e71382008-08-13 09:32:07 +0000775 __ tst(r0, Operand(kSmiTagMask));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000776 true_target->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000777
778 // Slow case: call the runtime.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000779 frame_->EmitPush(r0);
780 frame_->CallRuntime(Runtime::kToBool, 1);
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000781 // Convert the result (r0) to a condition code.
ager@chromium.orgab99eea2009-08-25 07:05:41 +0000782 __ LoadRoot(ip, Heap::kFalseValueRootIndex);
783 __ cmp(r0, ip);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000784
785 cc_reg_ = ne;
786}
787
788
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000789void CodeGenerator::GenericBinaryOperation(Token::Value op,
kasperl@chromium.org86f77b72009-07-06 08:21:57 +0000790 OverwriteMode overwrite_mode,
791 int constant_rhs) {
ager@chromium.org357bf652010-04-12 11:30:10 +0000792 VirtualFrame::SpilledScope spilled_scope(frame_);
mads.s.ager31e71382008-08-13 09:32:07 +0000793 // sp[0] : y
794 // sp[1] : x
795 // result : r0
796
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000797 // Stub is entered with a call: 'return address' is in lr.
798 switch (op) {
ager@chromium.org357bf652010-04-12 11:30:10 +0000799 case Token::ADD:
800 case Token::SUB:
801 case Token::MUL:
802 case Token::DIV:
803 case Token::MOD:
804 case Token::BIT_OR:
805 case Token::BIT_AND:
806 case Token::BIT_XOR:
807 case Token::SHL:
808 case Token::SHR:
809 case Token::SAR: {
810 frame_->EmitPop(r0); // r0 : y
811 frame_->EmitPop(r1); // r1 : x
812 GenericBinaryOpStub stub(op, overwrite_mode, r1, r0, constant_rhs);
813 frame_->CallStub(&stub, 0);
814 break;
815 }
816
817 case Token::COMMA:
818 frame_->EmitPop(r0);
819 // Simply discard left value.
820 frame_->Drop();
821 break;
822
823 default:
824 // Other cases should have been handled before this point.
825 UNREACHABLE();
826 break;
827 }
828}
829
830
831void CodeGenerator::VirtualFrameBinaryOperation(Token::Value op,
832 OverwriteMode overwrite_mode,
833 int constant_rhs) {
834 // top of virtual frame: y
835 // 2nd elt. on virtual frame : x
836 // result : top of virtual frame
837
838 // Stub is entered with a call: 'return address' is in lr.
839 switch (op) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000840 case Token::ADD: // fall through.
841 case Token::SUB: // fall through.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000842 case Token::MUL:
kasperl@chromium.org86f77b72009-07-06 08:21:57 +0000843 case Token::DIV:
844 case Token::MOD:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000845 case Token::BIT_OR:
846 case Token::BIT_AND:
847 case Token::BIT_XOR:
848 case Token::SHL:
849 case Token::SHR:
850 case Token::SAR: {
ager@chromium.org357bf652010-04-12 11:30:10 +0000851 Register rhs = frame_->PopToRegister();
852 Register lhs = frame_->PopToRegister(rhs); // Don't pop to rhs register.
853 {
854 VirtualFrame::SpilledScope spilled_scope(frame_);
855 GenericBinaryOpStub stub(op, overwrite_mode, lhs, rhs, constant_rhs);
856 frame_->CallStub(&stub, 0);
857 }
858 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000859 break;
860 }
861
ager@chromium.org357bf652010-04-12 11:30:10 +0000862 case Token::COMMA: {
863 Register scratch = frame_->PopToRegister();
864 // Simply discard left value.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000865 frame_->Drop();
ager@chromium.org357bf652010-04-12 11:30:10 +0000866 frame_->EmitPush(scratch);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000867 break;
ager@chromium.org357bf652010-04-12 11:30:10 +0000868 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000869
870 default:
871 // Other cases should have been handled before this point.
872 UNREACHABLE();
873 break;
874 }
875}
876
877
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000878class DeferredInlineSmiOperation: public DeferredCode {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000879 public:
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +0000880 DeferredInlineSmiOperation(Token::Value op,
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000881 int value,
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000882 bool reversed,
ager@chromium.org357bf652010-04-12 11:30:10 +0000883 OverwriteMode overwrite_mode,
884 Register tos)
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +0000885 : op_(op),
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000886 value_(value),
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000887 reversed_(reversed),
ager@chromium.org357bf652010-04-12 11:30:10 +0000888 overwrite_mode_(overwrite_mode),
889 tos_register_(tos) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000890 set_comment("[ DeferredInlinedSmiOperation");
891 }
892
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000893 virtual void Generate();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000894
895 private:
896 Token::Value op_;
897 int value_;
898 bool reversed_;
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000899 OverwriteMode overwrite_mode_;
ager@chromium.org357bf652010-04-12 11:30:10 +0000900 Register tos_register_;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000901};
902
903
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000904void DeferredInlineSmiOperation::Generate() {
ager@chromium.org357bf652010-04-12 11:30:10 +0000905 Register lhs = r1;
906 Register rhs = r0;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000907 switch (op_) {
908 case Token::ADD: {
ager@chromium.orge2902be2009-06-08 12:21:35 +0000909 // Revert optimistic add.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000910 if (reversed_) {
ager@chromium.org357bf652010-04-12 11:30:10 +0000911 __ sub(r0, tos_register_, Operand(Smi::FromInt(value_)));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000912 __ mov(r1, Operand(Smi::FromInt(value_)));
913 } else {
ager@chromium.org357bf652010-04-12 11:30:10 +0000914 __ sub(r1, tos_register_, Operand(Smi::FromInt(value_)));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000915 __ mov(r0, Operand(Smi::FromInt(value_)));
916 }
917 break;
918 }
919
920 case Token::SUB: {
ager@chromium.orge2902be2009-06-08 12:21:35 +0000921 // Revert optimistic sub.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000922 if (reversed_) {
ager@chromium.org357bf652010-04-12 11:30:10 +0000923 __ rsb(r0, tos_register_, Operand(Smi::FromInt(value_)));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000924 __ mov(r1, Operand(Smi::FromInt(value_)));
925 } else {
ager@chromium.org357bf652010-04-12 11:30:10 +0000926 __ add(r1, tos_register_, Operand(Smi::FromInt(value_)));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000927 __ mov(r0, Operand(Smi::FromInt(value_)));
928 }
929 break;
930 }
931
kasperl@chromium.org86f77b72009-07-06 08:21:57 +0000932 // For these operations there is no optimistic operation that needs to be
933 // reverted.
934 case Token::MUL:
935 case Token::MOD:
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000936 case Token::BIT_OR:
937 case Token::BIT_XOR:
938 case Token::BIT_AND: {
939 if (reversed_) {
ager@chromium.org357bf652010-04-12 11:30:10 +0000940 if (tos_register_.is(r0)) {
941 __ mov(r1, Operand(Smi::FromInt(value_)));
942 } else {
943 ASSERT(tos_register_.is(r1));
944 __ mov(r0, Operand(Smi::FromInt(value_)));
945 lhs = r0;
946 rhs = r1;
947 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000948 } else {
ager@chromium.org357bf652010-04-12 11:30:10 +0000949 if (tos_register_.is(r1)) {
950 __ mov(r0, Operand(Smi::FromInt(value_)));
951 } else {
952 ASSERT(tos_register_.is(r0));
953 __ mov(r1, Operand(Smi::FromInt(value_)));
954 lhs = r0;
955 rhs = r1;
956 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000957 }
958 break;
959 }
960
961 case Token::SHL:
962 case Token::SHR:
963 case Token::SAR: {
964 if (!reversed_) {
ager@chromium.org357bf652010-04-12 11:30:10 +0000965 if (tos_register_.is(r1)) {
966 __ mov(r0, Operand(Smi::FromInt(value_)));
967 } else {
968 ASSERT(tos_register_.is(r0));
969 __ mov(r1, Operand(Smi::FromInt(value_)));
970 lhs = r0;
971 rhs = r1;
972 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000973 } else {
ager@chromium.orge2902be2009-06-08 12:21:35 +0000974 UNREACHABLE(); // Should have been handled in SmiOperation.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000975 }
976 break;
977 }
978
979 default:
ager@chromium.orge2902be2009-06-08 12:21:35 +0000980 // Other cases should have been handled before this point.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000981 UNREACHABLE();
982 break;
983 }
984
ager@chromium.org357bf652010-04-12 11:30:10 +0000985 GenericBinaryOpStub stub(op_, overwrite_mode_, lhs, rhs, value_);
ager@chromium.orge2902be2009-06-08 12:21:35 +0000986 __ CallStub(&stub);
ager@chromium.org357bf652010-04-12 11:30:10 +0000987 // The generic stub returns its value in r0, but that's not
988 // necessarily what we want. We want whatever the inlined code
989 // expected, which is that the answer is in the same register as
990 // the operand was.
991 __ Move(tos_register_, r0);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000992}
993
994
kasperl@chromium.org86f77b72009-07-06 08:21:57 +0000995static bool PopCountLessThanEqual2(unsigned int x) {
996 x &= x - 1;
997 return (x & (x - 1)) == 0;
998}
999
1000
1001// Returns the index of the lowest bit set.
1002static int BitPosition(unsigned x) {
1003 int bit_posn = 0;
1004 while ((x & 0xf) == 0) {
1005 bit_posn += 4;
1006 x >>= 4;
1007 }
1008 while ((x & 1) == 0) {
1009 bit_posn++;
1010 x >>= 1;
1011 }
1012 return bit_posn;
1013}
1014
1015
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00001016void CodeGenerator::SmiOperation(Token::Value op,
1017 Handle<Object> value,
1018 bool reversed,
1019 OverwriteMode mode) {
ager@chromium.org357bf652010-04-12 11:30:10 +00001020 int int_value = Smi::cast(*value)->value();
1021
1022 bool something_to_inline;
1023 switch (op) {
1024 case Token::ADD:
1025 case Token::SUB:
1026 case Token::BIT_AND:
1027 case Token::BIT_OR:
1028 case Token::BIT_XOR: {
1029 something_to_inline = true;
1030 break;
1031 }
1032 case Token::SHL:
1033 case Token::SHR:
1034 case Token::SAR: {
1035 if (reversed) {
1036 something_to_inline = false;
1037 } else {
1038 something_to_inline = true;
1039 }
1040 break;
1041 }
1042 case Token::MOD: {
1043 if (reversed || int_value < 2 || !IsPowerOf2(int_value)) {
1044 something_to_inline = false;
1045 } else {
1046 something_to_inline = true;
1047 }
1048 break;
1049 }
1050 case Token::MUL: {
1051 if (!IsEasyToMultiplyBy(int_value)) {
1052 something_to_inline = false;
1053 } else {
1054 something_to_inline = true;
1055 }
1056 break;
1057 }
1058 default: {
1059 something_to_inline = false;
1060 break;
1061 }
1062 }
1063
1064 if (!something_to_inline) {
1065 if (!reversed) {
1066 // Push the rhs onto the virtual frame by putting it in a TOS register.
1067 Register rhs = frame_->GetTOSRegister();
1068 __ mov(rhs, Operand(value));
1069 frame_->EmitPush(rhs);
1070 VirtualFrameBinaryOperation(op, mode, int_value);
1071 } else {
1072 // Pop the rhs, then push lhs and rhs in the right order. Only performs
1073 // at most one pop, the rest takes place in TOS registers.
1074 Register lhs = frame_->GetTOSRegister(); // Get reg for pushing.
1075 Register rhs = frame_->PopToRegister(lhs); // Don't use lhs for this.
1076 __ mov(lhs, Operand(value));
1077 frame_->EmitPush(lhs);
1078 frame_->EmitPush(rhs);
1079 VirtualFrameBinaryOperation(op, mode, kUnknownIntValue);
1080 }
1081 return;
1082 }
1083
1084 // We move the top of stack to a register (normally no move is invoved).
1085 Register tos = frame_->PopToRegister();
1086 // All other registers are spilled. The deferred code expects one argument
1087 // in a register and all other values are flushed to the stack. The
1088 // answer is returned in the same register that the top of stack argument was
1089 // in.
1090 frame_->SpillAll();
1091
1092 switch (op) {
1093 case Token::ADD: {
1094 DeferredCode* deferred =
1095 new DeferredInlineSmiOperation(op, int_value, reversed, mode, tos);
1096
1097 __ add(tos, tos, Operand(value), SetCC);
1098 deferred->Branch(vs);
1099 __ tst(tos, Operand(kSmiTagMask));
1100 deferred->Branch(ne);
1101 deferred->BindExit();
1102 frame_->EmitPush(tos);
1103 break;
1104 }
1105
1106 case Token::SUB: {
1107 DeferredCode* deferred =
1108 new DeferredInlineSmiOperation(op, int_value, reversed, mode, tos);
1109
1110 if (reversed) {
1111 __ rsb(tos, tos, Operand(value), SetCC);
1112 } else {
1113 __ sub(tos, tos, Operand(value), SetCC);
1114 }
1115 deferred->Branch(vs);
1116 __ tst(tos, Operand(kSmiTagMask));
1117 deferred->Branch(ne);
1118 deferred->BindExit();
1119 frame_->EmitPush(tos);
1120 break;
1121 }
1122
1123
1124 case Token::BIT_OR:
1125 case Token::BIT_XOR:
1126 case Token::BIT_AND: {
1127 DeferredCode* deferred =
1128 new DeferredInlineSmiOperation(op, int_value, reversed, mode, tos);
1129 __ tst(tos, Operand(kSmiTagMask));
1130 deferred->Branch(ne);
1131 switch (op) {
1132 case Token::BIT_OR: __ orr(tos, tos, Operand(value)); break;
1133 case Token::BIT_XOR: __ eor(tos, tos, Operand(value)); break;
1134 case Token::BIT_AND: __ and_(tos, tos, Operand(value)); break;
1135 default: UNREACHABLE();
1136 }
1137 deferred->BindExit();
1138 frame_->EmitPush(tos);
1139 break;
1140 }
1141
1142 case Token::SHL:
1143 case Token::SHR:
1144 case Token::SAR: {
1145 ASSERT(!reversed);
1146 Register scratch = VirtualFrame::scratch0();
1147 Register scratch2 = VirtualFrame::scratch1();
1148 int shift_value = int_value & 0x1f; // least significant 5 bits
1149 DeferredCode* deferred =
1150 new DeferredInlineSmiOperation(op, shift_value, false, mode, tos);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00001151 uint32_t problematic_mask = kSmiTagMask;
1152 // For unsigned shift by zero all negative smis are problematic.
1153 if (shift_value == 0 && op == Token::SHR) problematic_mask |= 0x80000000;
1154 __ tst(tos, Operand(problematic_mask));
1155 deferred->Branch(ne); // Go slow for problematic input.
ager@chromium.org357bf652010-04-12 11:30:10 +00001156 switch (op) {
1157 case Token::SHL: {
1158 if (shift_value != 0) {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00001159 int adjusted_shift = shift_value - kSmiTagSize;
1160 ASSERT(adjusted_shift >= 0);
1161 if (adjusted_shift != 0) {
1162 __ mov(scratch, Operand(tos, LSL, adjusted_shift));
1163 // Check that the *signed* result fits in a smi.
1164 __ add(scratch2, scratch, Operand(0x40000000), SetCC);
1165 deferred->Branch(mi);
1166 __ mov(tos, Operand(scratch, LSL, kSmiTagSize));
1167 } else {
1168 // Check that the *signed* result fits in a smi.
1169 __ add(scratch2, tos, Operand(0x40000000), SetCC);
1170 deferred->Branch(mi);
1171 __ mov(tos, Operand(tos, LSL, kSmiTagSize));
1172 }
ager@chromium.org357bf652010-04-12 11:30:10 +00001173 }
ager@chromium.org357bf652010-04-12 11:30:10 +00001174 break;
1175 }
1176 case Token::SHR: {
ager@chromium.org357bf652010-04-12 11:30:10 +00001177 if (shift_value != 0) {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00001178 __ mov(scratch, Operand(tos, ASR, kSmiTagSize)); // Remove tag.
1179 // LSR by immediate 0 means shifting 32 bits.
ager@chromium.org357bf652010-04-12 11:30:10 +00001180 __ mov(scratch, Operand(scratch, LSR, shift_value));
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00001181 if (shift_value == 1) {
1182 // check that the *unsigned* result fits in a smi
1183 // neither of the two high-order bits can be set:
1184 // - 0x80000000: high bit would be lost when smi tagging
1185 // - 0x40000000: this number would convert to negative when
1186 // smi tagging these two cases can only happen with shifts
1187 // by 0 or 1 when handed a valid smi
1188 __ tst(scratch, Operand(0xc0000000));
1189 deferred->Branch(ne);
1190 }
1191 __ mov(tos, Operand(scratch, LSL, kSmiTagSize));
ager@chromium.org357bf652010-04-12 11:30:10 +00001192 }
ager@chromium.org357bf652010-04-12 11:30:10 +00001193 break;
1194 }
1195 case Token::SAR: {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00001196 // In the ARM instructions set, ASR by immediate 0 means shifting 32
1197 // bits.
ager@chromium.org357bf652010-04-12 11:30:10 +00001198 if (shift_value != 0) {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00001199 // Do the shift and the tag removal in one operation. If the shift
1200 // is 31 bits (the highest possible value) then we emit the
1201 // instruction as a shift by 0 which means shift arithmetically by
1202 // 32.
1203 __ mov(tos, Operand(tos, ASR, (kSmiTagSize + shift_value) & 0x1f));
1204 // Put tag back.
1205 __ mov(tos, Operand(tos, LSL, kSmiTagSize));
ager@chromium.org357bf652010-04-12 11:30:10 +00001206 }
1207 break;
1208 }
1209 default: UNREACHABLE();
1210 }
ager@chromium.org357bf652010-04-12 11:30:10 +00001211 deferred->BindExit();
1212 frame_->EmitPush(tos);
1213 break;
1214 }
1215
1216 case Token::MOD: {
1217 ASSERT(!reversed);
1218 ASSERT(int_value >= 2);
1219 ASSERT(IsPowerOf2(int_value));
1220 DeferredCode* deferred =
1221 new DeferredInlineSmiOperation(op, int_value, reversed, mode, tos);
1222 unsigned mask = (0x80000000u | kSmiTagMask);
1223 __ tst(tos, Operand(mask));
1224 deferred->Branch(ne); // Go to deferred code on non-Smis and negative.
1225 mask = (int_value << kSmiTagSize) - 1;
1226 __ and_(tos, tos, Operand(mask));
1227 deferred->BindExit();
1228 frame_->EmitPush(tos);
1229 break;
1230 }
1231
1232 case Token::MUL: {
1233 ASSERT(IsEasyToMultiplyBy(int_value));
1234 DeferredCode* deferred =
1235 new DeferredInlineSmiOperation(op, int_value, reversed, mode, tos);
1236 unsigned max_smi_that_wont_overflow = Smi::kMaxValue / int_value;
1237 max_smi_that_wont_overflow <<= kSmiTagSize;
1238 unsigned mask = 0x80000000u;
1239 while ((mask & max_smi_that_wont_overflow) == 0) {
1240 mask |= mask >> 1;
1241 }
1242 mask |= kSmiTagMask;
1243 // This does a single mask that checks for a too high value in a
1244 // conservative way and for a non-Smi. It also filters out negative
1245 // numbers, unfortunately, but since this code is inline we prefer
1246 // brevity to comprehensiveness.
1247 __ tst(tos, Operand(mask));
1248 deferred->Branch(ne);
1249 MultiplyByKnownInt(masm_, tos, tos, int_value);
1250 deferred->BindExit();
1251 frame_->EmitPush(tos);
1252 break;
1253 }
1254
1255 default:
1256 UNREACHABLE();
1257 break;
1258 }
1259}
1260
1261
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001262void CodeGenerator::Comparison(Condition cc,
1263 Expression* left,
1264 Expression* right,
1265 bool strict) {
ager@chromium.org357bf652010-04-12 11:30:10 +00001266 VirtualFrame::RegisterAllocationScope scope(this);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001267
ager@chromium.org357bf652010-04-12 11:30:10 +00001268 if (left != NULL) Load(left);
1269 if (right != NULL) Load(right);
1270
mads.s.ager31e71382008-08-13 09:32:07 +00001271 // sp[0] : y
1272 // sp[1] : x
1273 // result : cc register
1274
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001275 // Strict only makes sense for equality comparisons.
1276 ASSERT(!strict || cc == eq);
1277
ager@chromium.org357bf652010-04-12 11:30:10 +00001278 Register lhs;
1279 Register rhs;
1280
1281 // We load the top two stack positions into registers chosen by the virtual
1282 // frame. This should keep the register shuffling to a minimum.
mads.s.ager@gmail.com769cc962008-08-06 10:02:49 +00001283 // Implement '>' and '<=' by reversal to obtain ECMA-262 conversion order.
1284 if (cc == gt || cc == le) {
1285 cc = ReverseCondition(cc);
ager@chromium.org357bf652010-04-12 11:30:10 +00001286 lhs = frame_->PopToRegister();
1287 rhs = frame_->PopToRegister(lhs); // Don't pop to the same register again!
mads.s.ager@gmail.com769cc962008-08-06 10:02:49 +00001288 } else {
ager@chromium.org357bf652010-04-12 11:30:10 +00001289 rhs = frame_->PopToRegister();
1290 lhs = frame_->PopToRegister(rhs); // Don't pop to the same register again!
mads.s.ager@gmail.com769cc962008-08-06 10:02:49 +00001291 }
ager@chromium.org357bf652010-04-12 11:30:10 +00001292
1293 ASSERT(rhs.is(r0) || rhs.is(r1));
1294 ASSERT(lhs.is(r0) || lhs.is(r1));
1295
1296 // Now we have the two sides in r0 and r1. We flush any other registers
1297 // because the stub doesn't know about register allocation.
1298 frame_->SpillAll();
1299 Register scratch = VirtualFrame::scratch0();
1300 __ orr(scratch, lhs, Operand(rhs));
1301 __ tst(scratch, Operand(kSmiTagMask));
1302 JumpTarget smi;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001303 smi.Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001304
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001305 // Perform non-smi comparison by stub.
1306 // CompareStub takes arguments in r0 and r1, returns <0, >0 or 0 in r0.
1307 // We call with 0 args because there are 0 on the stack.
ager@chromium.org357bf652010-04-12 11:30:10 +00001308 if (!rhs.is(r0)) {
1309 __ Swap(rhs, lhs, ip);
1310 }
1311
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001312 CompareStub stub(cc, strict);
1313 frame_->CallStub(&stub, 0);
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00001314 __ cmp(r0, Operand(0));
ager@chromium.org357bf652010-04-12 11:30:10 +00001315 JumpTarget exit;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001316 exit.Jump();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001317
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001318 // Do smi comparisons by pointer comparison.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001319 smi.Bind();
ager@chromium.org357bf652010-04-12 11:30:10 +00001320 __ cmp(lhs, Operand(rhs));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001321
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001322 exit.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001323 cc_reg_ = cc;
1324}
1325
1326
mads.s.ager31e71382008-08-13 09:32:07 +00001327// Call the function on the stack with the given arguments.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001328void CodeGenerator::CallWithArguments(ZoneList<Expression*>* args,
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001329 CallFunctionFlags flags,
1330 int position) {
ager@chromium.org357bf652010-04-12 11:30:10 +00001331 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001332 // Push the arguments ("left-to-right") on the stack.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001333 int arg_count = args->length();
1334 for (int i = 0; i < arg_count; i++) {
1335 LoadAndSpill(args->at(i));
mads.s.ager31e71382008-08-13 09:32:07 +00001336 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001337
kasper.lund7276f142008-07-30 08:49:36 +00001338 // Record the position for debugging purposes.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001339 CodeForSourcePosition(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001340
kasper.lund7276f142008-07-30 08:49:36 +00001341 // Use the shared code stub to call the function.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001342 InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001343 CallFunctionStub call_function(arg_count, in_loop, flags);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001344 frame_->CallStub(&call_function, arg_count + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001345
1346 // Restore context and pop function from the stack.
ager@chromium.org3bf7b912008-11-17 09:09:45 +00001347 __ ldr(cp, frame_->Context());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001348 frame_->Drop(); // discard the TOS
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001349}
1350
1351
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00001352void CodeGenerator::CallApplyLazy(Expression* applicand,
1353 Expression* receiver,
1354 VariableProxy* arguments,
1355 int position) {
1356 // An optimized implementation of expressions of the form
1357 // x.apply(y, arguments).
1358 // If the arguments object of the scope has not been allocated,
1359 // and x.apply is Function.prototype.apply, this optimization
1360 // just copies y and the arguments of the current function on the
1361 // stack, as receiver and arguments, and calls x.
1362 // In the implementation comments, we call x the applicand
1363 // and y the receiver.
1364 VirtualFrame::SpilledScope spilled_scope(frame_);
1365
1366 ASSERT(ArgumentsMode() == LAZY_ARGUMENTS_ALLOCATION);
1367 ASSERT(arguments->IsArguments());
1368
1369 // Load applicand.apply onto the stack. This will usually
1370 // give us a megamorphic load site. Not super, but it works.
1371 LoadAndSpill(applicand);
1372 Handle<String> name = Factory::LookupAsciiSymbol("apply");
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001373 frame_->Dup();
ager@chromium.orgac091b72010-05-05 07:34:42 +00001374 frame_->CallLoadIC(name, RelocInfo::CODE_TARGET);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00001375 frame_->EmitPush(r0);
1376
1377 // Load the receiver and the existing arguments object onto the
1378 // expression stack. Avoid allocating the arguments object here.
1379 LoadAndSpill(receiver);
1380 LoadFromSlot(scope()->arguments()->var()->slot(), NOT_INSIDE_TYPEOF);
1381
1382 // Emit the source position information after having loaded the
1383 // receiver and the arguments.
1384 CodeForSourcePosition(position);
1385 // Contents of the stack at this point:
1386 // sp[0]: arguments object of the current function or the hole.
1387 // sp[1]: receiver
1388 // sp[2]: applicand.apply
1389 // sp[3]: applicand.
1390
1391 // Check if the arguments object has been lazily allocated
1392 // already. If so, just use that instead of copying the arguments
1393 // from the stack. This also deals with cases where a local variable
1394 // named 'arguments' has been introduced.
1395 __ ldr(r0, MemOperand(sp, 0));
1396
1397 Label slow, done;
1398 __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
1399 __ cmp(ip, r0);
1400 __ b(ne, &slow);
1401
1402 Label build_args;
1403 // Get rid of the arguments object probe.
1404 frame_->Drop();
1405 // Stack now has 3 elements on it.
1406 // Contents of stack at this point:
1407 // sp[0]: receiver
1408 // sp[1]: applicand.apply
1409 // sp[2]: applicand.
1410
1411 // Check that the receiver really is a JavaScript object.
1412 __ ldr(r0, MemOperand(sp, 0));
1413 __ BranchOnSmi(r0, &build_args);
1414 // We allow all JSObjects including JSFunctions. As long as
1415 // JS_FUNCTION_TYPE is the last instance type and it is right
1416 // after LAST_JS_OBJECT_TYPE, we do not have to check the upper
1417 // bound.
1418 ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
1419 ASSERT(JS_FUNCTION_TYPE == LAST_JS_OBJECT_TYPE + 1);
1420 __ CompareObjectType(r0, r1, r2, FIRST_JS_OBJECT_TYPE);
1421 __ b(lt, &build_args);
1422
1423 // Check that applicand.apply is Function.prototype.apply.
1424 __ ldr(r0, MemOperand(sp, kPointerSize));
1425 __ BranchOnSmi(r0, &build_args);
1426 __ CompareObjectType(r0, r1, r2, JS_FUNCTION_TYPE);
1427 __ b(ne, &build_args);
1428 __ ldr(r0, FieldMemOperand(r0, JSFunction::kSharedFunctionInfoOffset));
1429 Handle<Code> apply_code(Builtins::builtin(Builtins::FunctionApply));
1430 __ ldr(r1, FieldMemOperand(r0, SharedFunctionInfo::kCodeOffset));
1431 __ cmp(r1, Operand(apply_code));
1432 __ b(ne, &build_args);
1433
1434 // Check that applicand is a function.
1435 __ ldr(r1, MemOperand(sp, 2 * kPointerSize));
1436 __ BranchOnSmi(r1, &build_args);
1437 __ CompareObjectType(r1, r2, r3, JS_FUNCTION_TYPE);
1438 __ b(ne, &build_args);
1439
1440 // Copy the arguments to this function possibly from the
1441 // adaptor frame below it.
1442 Label invoke, adapted;
1443 __ ldr(r2, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
1444 __ ldr(r3, MemOperand(r2, StandardFrameConstants::kContextOffset));
1445 __ cmp(r3, Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
1446 __ b(eq, &adapted);
1447
1448 // No arguments adaptor frame. Copy fixed number of arguments.
1449 __ mov(r0, Operand(scope()->num_parameters()));
1450 for (int i = 0; i < scope()->num_parameters(); i++) {
1451 __ ldr(r2, frame_->ParameterAt(i));
1452 __ push(r2);
1453 }
1454 __ jmp(&invoke);
1455
1456 // Arguments adaptor frame present. Copy arguments from there, but
1457 // avoid copying too many arguments to avoid stack overflows.
1458 __ bind(&adapted);
1459 static const uint32_t kArgumentsLimit = 1 * KB;
1460 __ ldr(r0, MemOperand(r2, ArgumentsAdaptorFrameConstants::kLengthOffset));
1461 __ mov(r0, Operand(r0, LSR, kSmiTagSize));
1462 __ mov(r3, r0);
1463 __ cmp(r0, Operand(kArgumentsLimit));
1464 __ b(gt, &build_args);
1465
1466 // Loop through the arguments pushing them onto the execution
1467 // stack. We don't inform the virtual frame of the push, so we don't
1468 // have to worry about getting rid of the elements from the virtual
1469 // frame.
1470 Label loop;
1471 // r3 is a small non-negative integer, due to the test above.
1472 __ cmp(r3, Operand(0));
1473 __ b(eq, &invoke);
1474 // Compute the address of the first argument.
1475 __ add(r2, r2, Operand(r3, LSL, kPointerSizeLog2));
1476 __ add(r2, r2, Operand(kPointerSize));
1477 __ bind(&loop);
1478 // Post-decrement argument address by kPointerSize on each iteration.
1479 __ ldr(r4, MemOperand(r2, kPointerSize, NegPostIndex));
1480 __ push(r4);
1481 __ sub(r3, r3, Operand(1), SetCC);
1482 __ b(gt, &loop);
1483
1484 // Invoke the function.
1485 __ bind(&invoke);
1486 ParameterCount actual(r0);
1487 __ InvokeFunction(r1, actual, CALL_FUNCTION);
1488 // Drop applicand.apply and applicand from the stack, and push
1489 // the result of the function call, but leave the spilled frame
1490 // unchanged, with 3 elements, so it is correct when we compile the
1491 // slow-case code.
1492 __ add(sp, sp, Operand(2 * kPointerSize));
1493 __ push(r0);
1494 // Stack now has 1 element:
1495 // sp[0]: result
1496 __ jmp(&done);
1497
1498 // Slow-case: Allocate the arguments object since we know it isn't
1499 // there, and fall-through to the slow-case where we call
1500 // applicand.apply.
1501 __ bind(&build_args);
1502 // Stack now has 3 elements, because we have jumped from where:
1503 // sp[0]: receiver
1504 // sp[1]: applicand.apply
1505 // sp[2]: applicand.
1506 StoreArgumentsObject(false);
1507
1508 // Stack and frame now have 4 elements.
1509 __ bind(&slow);
1510
1511 // Generic computation of x.apply(y, args) with no special optimization.
1512 // Flip applicand.apply and applicand on the stack, so
1513 // applicand looks like the receiver of the applicand.apply call.
1514 // Then process it as a normal function call.
1515 __ ldr(r0, MemOperand(sp, 3 * kPointerSize));
1516 __ ldr(r1, MemOperand(sp, 2 * kPointerSize));
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00001517 __ strd(r0, MemOperand(sp, 2 * kPointerSize));
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00001518
1519 CallFunctionStub call_function(2, NOT_IN_LOOP, NO_CALL_FUNCTION_FLAGS);
1520 frame_->CallStub(&call_function, 3);
1521 // The function and its two arguments have been dropped.
1522 frame_->Drop(); // Drop the receiver as well.
1523 frame_->EmitPush(r0);
1524 // Stack now has 1 element:
1525 // sp[0]: result
1526 __ bind(&done);
1527
1528 // Restore the context register after a call.
1529 __ ldr(cp, frame_->Context());
1530}
1531
1532
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001533void CodeGenerator::Branch(bool if_true, JumpTarget* target) {
ager@chromium.org357bf652010-04-12 11:30:10 +00001534 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001535 ASSERT(has_cc());
1536 Condition cc = if_true ? cc_reg_ : NegateCondition(cc_reg_);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001537 target->Branch(cc);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001538 cc_reg_ = al;
1539}
1540
1541
ager@chromium.org7c537e22008-10-16 08:43:32 +00001542void CodeGenerator::CheckStack() {
ager@chromium.org357bf652010-04-12 11:30:10 +00001543 VirtualFrame::SpilledScope spilled_scope(frame_);
ager@chromium.org3811b432009-10-28 14:53:37 +00001544 Comment cmnt(masm_, "[ check stack");
1545 __ LoadRoot(ip, Heap::kStackLimitRootIndex);
1546 // Put the lr setup instruction in the delay slot. kInstrSize is added to
1547 // the implicit 8 byte offset that always applies to operations with pc and
1548 // gives a return address 12 bytes down.
1549 masm_->add(lr, pc, Operand(Assembler::kInstrSize));
1550 masm_->cmp(sp, Operand(ip));
1551 StackCheckStub stub;
1552 // Call the stub if lower.
1553 masm_->mov(pc,
1554 Operand(reinterpret_cast<intptr_t>(stub.GetCode().location()),
1555 RelocInfo::CODE_TARGET),
1556 LeaveCC,
1557 lo);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001558}
1559
1560
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001561void CodeGenerator::VisitStatements(ZoneList<Statement*>* statements) {
1562#ifdef DEBUG
1563 int original_height = frame_->height();
1564#endif
ager@chromium.org357bf652010-04-12 11:30:10 +00001565 VirtualFrame::SpilledScope spilled_scope(frame_);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001566 for (int i = 0; frame_ != NULL && i < statements->length(); i++) {
1567 VisitAndSpill(statements->at(i));
1568 }
1569 ASSERT(!has_valid_frame() || frame_->height() == original_height);
1570}
1571
1572
ager@chromium.org7c537e22008-10-16 08:43:32 +00001573void CodeGenerator::VisitBlock(Block* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001574#ifdef DEBUG
1575 int original_height = frame_->height();
1576#endif
ager@chromium.org357bf652010-04-12 11:30:10 +00001577 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001578 Comment cmnt(masm_, "[ Block");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001579 CodeForStatementPosition(node);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00001580 node->break_target()->SetExpectedHeight();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001581 VisitStatementsAndSpill(node->statements());
1582 if (node->break_target()->is_linked()) {
1583 node->break_target()->Bind();
1584 }
1585 node->break_target()->Unuse();
1586 ASSERT(!has_valid_frame() || frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001587}
1588
1589
ager@chromium.org7c537e22008-10-16 08:43:32 +00001590void CodeGenerator::DeclareGlobals(Handle<FixedArray> pairs) {
ager@chromium.org3811b432009-10-28 14:53:37 +00001591 frame_->EmitPush(cp);
ager@chromium.orgac091b72010-05-05 07:34:42 +00001592 frame_->EmitPush(Operand(pairs));
1593 frame_->EmitPush(Operand(Smi::FromInt(is_eval() ? 1 : 0)));
1594
1595 VirtualFrame::SpilledScope spilled_scope(frame_);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001596 frame_->CallRuntime(Runtime::kDeclareGlobals, 3);
mads.s.ager31e71382008-08-13 09:32:07 +00001597 // The result is discarded.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001598}
1599
1600
ager@chromium.org7c537e22008-10-16 08:43:32 +00001601void CodeGenerator::VisitDeclaration(Declaration* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001602#ifdef DEBUG
1603 int original_height = frame_->height();
1604#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001605 Comment cmnt(masm_, "[ Declaration");
1606 Variable* var = node->proxy()->var();
1607 ASSERT(var != NULL); // must have been resolved
1608 Slot* slot = var->slot();
1609
1610 // If it was not possible to allocate the variable at compile time,
1611 // we need to "declare" it at runtime to make sure it actually
1612 // exists in the local context.
1613 if (slot != NULL && slot->type() == Slot::LOOKUP) {
1614 // Variables with a "LOOKUP" slot were introduced as non-locals
1615 // during variable resolution and must have mode DYNAMIC.
ager@chromium.org381abbb2009-02-25 13:23:22 +00001616 ASSERT(var->is_dynamic());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001617 // For now, just do a runtime call.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001618 frame_->EmitPush(cp);
ager@chromium.orgac091b72010-05-05 07:34:42 +00001619 frame_->EmitPush(Operand(var->name()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001620 // Declaration nodes are always declared in only two modes.
1621 ASSERT(node->mode() == Variable::VAR || node->mode() == Variable::CONST);
1622 PropertyAttributes attr = node->mode() == Variable::VAR ? NONE : READ_ONLY;
ager@chromium.orgac091b72010-05-05 07:34:42 +00001623 frame_->EmitPush(Operand(Smi::FromInt(attr)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001624 // Push initial value, if any.
1625 // Note: For variables we must not push an initial value (such as
1626 // 'undefined') because we may have a (legal) redeclaration and we
1627 // must not destroy the current value.
1628 if (node->mode() == Variable::CONST) {
ager@chromium.orgac091b72010-05-05 07:34:42 +00001629 frame_->EmitPushRoot(Heap::kTheHoleValueRootIndex);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001630 } else if (node->fun() != NULL) {
ager@chromium.orgac091b72010-05-05 07:34:42 +00001631 Load(node->fun());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001632 } else {
ager@chromium.orgac091b72010-05-05 07:34:42 +00001633 frame_->EmitPush(Operand(0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001634 }
ager@chromium.orgac091b72010-05-05 07:34:42 +00001635
1636 VirtualFrame::SpilledScope spilled_scope(frame_);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001637 frame_->CallRuntime(Runtime::kDeclareContextSlot, 4);
ager@chromium.org7c537e22008-10-16 08:43:32 +00001638 // Ignore the return value (declarations are statements).
ager@chromium.orgac091b72010-05-05 07:34:42 +00001639
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001640 ASSERT(frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001641 return;
1642 }
1643
1644 ASSERT(!var->is_global());
1645
1646 // If we have a function or a constant, we need to initialize the variable.
1647 Expression* val = NULL;
1648 if (node->mode() == Variable::CONST) {
1649 val = new Literal(Factory::the_hole_value());
1650 } else {
1651 val = node->fun(); // NULL if we don't have a function
1652 }
1653
1654 if (val != NULL) {
ager@chromium.orgac091b72010-05-05 07:34:42 +00001655 // Set initial value.
1656 Reference target(this, node->proxy());
1657 Load(val);
1658 target.SetValue(NOT_CONST_INIT);
1659
iposva@chromium.org245aa852009-02-10 00:49:54 +00001660 // Get rid of the assigned value (declarations are statements).
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001661 frame_->Drop();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001662 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001663 ASSERT(frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001664}
1665
1666
ager@chromium.org7c537e22008-10-16 08:43:32 +00001667void CodeGenerator::VisitExpressionStatement(ExpressionStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001668#ifdef DEBUG
1669 int original_height = frame_->height();
1670#endif
ager@chromium.org357bf652010-04-12 11:30:10 +00001671 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001672 Comment cmnt(masm_, "[ ExpressionStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001673 CodeForStatementPosition(node);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001674 Expression* expression = node->expression();
1675 expression->MarkAsStatement();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001676 LoadAndSpill(expression);
1677 frame_->Drop();
1678 ASSERT(frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001679}
1680
1681
ager@chromium.org7c537e22008-10-16 08:43:32 +00001682void CodeGenerator::VisitEmptyStatement(EmptyStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001683#ifdef DEBUG
1684 int original_height = frame_->height();
1685#endif
ager@chromium.org357bf652010-04-12 11:30:10 +00001686 VirtualFrame::SpilledScope spilled_scope(frame_);
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
ager@chromium.org357bf652010-04-12 11:30:10 +00001698 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001699 Comment cmnt(masm_, "[ IfStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001700 // Generate different code depending on which parts of the if statement
1701 // are present or not.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001702 bool has_then_stm = node->HasThenStatement();
1703 bool has_else_stm = node->HasElseStatement();
1704
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001705 CodeForStatementPosition(node);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001706
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001707 JumpTarget exit;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001708 if (has_then_stm && has_else_stm) {
mads.s.ager31e71382008-08-13 09:32:07 +00001709 Comment cmnt(masm_, "[ IfThenElse");
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001710 JumpTarget then;
1711 JumpTarget else_;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001712 // if (cond)
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001713 LoadConditionAndSpill(node->condition(), &then, &else_, true);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001714 if (frame_ != NULL) {
1715 Branch(false, &else_);
1716 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001717 // then
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001718 if (frame_ != NULL || then.is_linked()) {
1719 then.Bind();
1720 VisitAndSpill(node->then_statement());
1721 }
1722 if (frame_ != NULL) {
1723 exit.Jump();
1724 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001725 // else
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001726 if (else_.is_linked()) {
1727 else_.Bind();
1728 VisitAndSpill(node->else_statement());
1729 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001730
1731 } else if (has_then_stm) {
mads.s.ager31e71382008-08-13 09:32:07 +00001732 Comment cmnt(masm_, "[ IfThen");
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001733 ASSERT(!has_else_stm);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001734 JumpTarget then;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001735 // if (cond)
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001736 LoadConditionAndSpill(node->condition(), &then, &exit, true);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001737 if (frame_ != NULL) {
1738 Branch(false, &exit);
1739 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001740 // then
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001741 if (frame_ != NULL || then.is_linked()) {
1742 then.Bind();
1743 VisitAndSpill(node->then_statement());
1744 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001745
1746 } else if (has_else_stm) {
mads.s.ager31e71382008-08-13 09:32:07 +00001747 Comment cmnt(masm_, "[ IfElse");
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001748 ASSERT(!has_then_stm);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001749 JumpTarget else_;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001750 // if (!cond)
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001751 LoadConditionAndSpill(node->condition(), &exit, &else_, true);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001752 if (frame_ != NULL) {
1753 Branch(true, &exit);
1754 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001755 // else
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001756 if (frame_ != NULL || else_.is_linked()) {
1757 else_.Bind();
1758 VisitAndSpill(node->else_statement());
1759 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001760
1761 } else {
mads.s.ager31e71382008-08-13 09:32:07 +00001762 Comment cmnt(masm_, "[ If");
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001763 ASSERT(!has_then_stm && !has_else_stm);
1764 // if (cond)
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001765 LoadConditionAndSpill(node->condition(), &exit, &exit, false);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001766 if (frame_ != NULL) {
1767 if (has_cc()) {
1768 cc_reg_ = al;
1769 } else {
1770 frame_->Drop();
1771 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001772 }
1773 }
1774
1775 // end
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001776 if (exit.is_linked()) {
1777 exit.Bind();
1778 }
1779 ASSERT(!has_valid_frame() || frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001780}
1781
1782
ager@chromium.org7c537e22008-10-16 08:43:32 +00001783void CodeGenerator::VisitContinueStatement(ContinueStatement* node) {
ager@chromium.org357bf652010-04-12 11:30:10 +00001784 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001785 Comment cmnt(masm_, "[ ContinueStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001786 CodeForStatementPosition(node);
1787 node->target()->continue_target()->Jump();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001788}
1789
1790
ager@chromium.org7c537e22008-10-16 08:43:32 +00001791void CodeGenerator::VisitBreakStatement(BreakStatement* node) {
ager@chromium.org357bf652010-04-12 11:30:10 +00001792 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001793 Comment cmnt(masm_, "[ BreakStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001794 CodeForStatementPosition(node);
1795 node->target()->break_target()->Jump();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001796}
1797
1798
ager@chromium.org7c537e22008-10-16 08:43:32 +00001799void CodeGenerator::VisitReturnStatement(ReturnStatement* node) {
ager@chromium.org357bf652010-04-12 11:30:10 +00001800 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001801 Comment cmnt(masm_, "[ ReturnStatement");
mads.s.ager31e71382008-08-13 09:32:07 +00001802
ager@chromium.org4af710e2009-09-15 12:20:11 +00001803 CodeForStatementPosition(node);
1804 LoadAndSpill(node->expression());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001805 if (function_return_is_shadowed_) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001806 frame_->EmitPop(r0);
1807 function_return_.Jump();
1808 } else {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001809 // Pop the result from the frame and prepare the frame for
1810 // returning thus making it easier to merge.
1811 frame_->EmitPop(r0);
1812 frame_->PrepareForReturn();
1813
1814 function_return_.Jump();
1815 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001816}
1817
1818
ager@chromium.org7c537e22008-10-16 08:43:32 +00001819void CodeGenerator::VisitWithEnterStatement(WithEnterStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001820#ifdef DEBUG
1821 int original_height = frame_->height();
1822#endif
ager@chromium.org357bf652010-04-12 11:30:10 +00001823 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001824 Comment cmnt(masm_, "[ WithEnterStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001825 CodeForStatementPosition(node);
1826 LoadAndSpill(node->expression());
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001827 if (node->is_catch_block()) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001828 frame_->CallRuntime(Runtime::kPushCatchContext, 1);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001829 } else {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001830 frame_->CallRuntime(Runtime::kPushContext, 1);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001831 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00001832#ifdef DEBUG
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001833 JumpTarget verified_true;
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00001834 __ cmp(r0, cp);
ager@chromium.org65dad4b2009-04-23 08:48:43 +00001835 verified_true.Branch(eq);
1836 __ stop("PushContext: r0 is expected to be the same as cp");
1837 verified_true.Bind();
1838#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001839 // Update context local.
ager@chromium.org3bf7b912008-11-17 09:09:45 +00001840 __ str(cp, frame_->Context());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001841 ASSERT(frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001842}
1843
1844
ager@chromium.org7c537e22008-10-16 08:43:32 +00001845void CodeGenerator::VisitWithExitStatement(WithExitStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001846#ifdef DEBUG
1847 int original_height = frame_->height();
1848#endif
ager@chromium.org357bf652010-04-12 11:30:10 +00001849 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001850 Comment cmnt(masm_, "[ WithExitStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001851 CodeForStatementPosition(node);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001852 // Pop context.
1853 __ ldr(cp, ContextOperand(cp, Context::PREVIOUS_INDEX));
1854 // Update context local.
ager@chromium.org3bf7b912008-11-17 09:09:45 +00001855 __ str(cp, frame_->Context());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001856 ASSERT(frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001857}
1858
1859
ager@chromium.org7c537e22008-10-16 08:43:32 +00001860void CodeGenerator::VisitSwitchStatement(SwitchStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001861#ifdef DEBUG
1862 int original_height = frame_->height();
1863#endif
ager@chromium.org357bf652010-04-12 11:30:10 +00001864 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001865 Comment cmnt(masm_, "[ SwitchStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001866 CodeForStatementPosition(node);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00001867 node->break_target()->SetExpectedHeight();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001868
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001869 LoadAndSpill(node->tag());
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001870
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001871 JumpTarget next_test;
1872 JumpTarget fall_through;
1873 JumpTarget default_entry;
1874 JumpTarget default_exit(JumpTarget::BIDIRECTIONAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001875 ZoneList<CaseClause*>* cases = node->cases();
1876 int length = cases->length();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001877 CaseClause* default_clause = NULL;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001878
1879 for (int i = 0; i < length; i++) {
1880 CaseClause* clause = cases->at(i);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001881 if (clause->is_default()) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001882 // Remember the default clause and compile it at the end.
1883 default_clause = clause;
1884 continue;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001885 }
1886
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001887 Comment cmnt(masm_, "[ Case clause");
1888 // Compile the test.
1889 next_test.Bind();
1890 next_test.Unuse();
1891 // Duplicate TOS.
1892 __ ldr(r0, frame_->Top());
1893 frame_->EmitPush(r0);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001894 Comparison(eq, NULL, clause->label(), true);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001895 Branch(false, &next_test);
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00001896
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001897 // Before entering the body from the test, remove the switch value from
1898 // the stack.
1899 frame_->Drop();
1900
1901 // Label the body so that fall through is enabled.
1902 if (i > 0 && cases->at(i - 1)->is_default()) {
1903 default_exit.Bind();
1904 } else {
1905 fall_through.Bind();
1906 fall_through.Unuse();
1907 }
1908 VisitStatementsAndSpill(clause->statements());
1909
1910 // If control flow can fall through from the body, jump to the next body
1911 // or the end of the statement.
1912 if (frame_ != NULL) {
1913 if (i < length - 1 && cases->at(i + 1)->is_default()) {
1914 default_entry.Jump();
1915 } else {
1916 fall_through.Jump();
1917 }
1918 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001919 }
1920
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001921 // The final "test" removes the switch value.
1922 next_test.Bind();
1923 frame_->Drop();
1924
1925 // If there is a default clause, compile it.
1926 if (default_clause != NULL) {
1927 Comment cmnt(masm_, "[ Default clause");
1928 default_entry.Bind();
1929 VisitStatementsAndSpill(default_clause->statements());
1930 // If control flow can fall out of the default and there is a case after
1931 // it, jup to that case's body.
1932 if (frame_ != NULL && default_exit.is_bound()) {
1933 default_exit.Jump();
1934 }
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00001935 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001936
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001937 if (fall_through.is_linked()) {
1938 fall_through.Bind();
1939 }
1940
1941 if (node->break_target()->is_linked()) {
1942 node->break_target()->Bind();
1943 }
1944 node->break_target()->Unuse();
1945 ASSERT(!has_valid_frame() || frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001946}
1947
1948
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001949void CodeGenerator::VisitDoWhileStatement(DoWhileStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001950#ifdef DEBUG
1951 int original_height = frame_->height();
1952#endif
ager@chromium.org357bf652010-04-12 11:30:10 +00001953 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001954 Comment cmnt(masm_, "[ DoWhileStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001955 CodeForStatementPosition(node);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00001956 node->break_target()->SetExpectedHeight();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001957 JumpTarget body(JumpTarget::BIDIRECTIONAL);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00001958 IncrementLoopNesting();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001959
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001960 // Label the top of the loop for the backward CFG edge. If the test
1961 // is always true we can use the continue target, and if the test is
1962 // always false there is no need.
1963 ConditionAnalysis info = AnalyzeCondition(node->cond());
1964 switch (info) {
1965 case ALWAYS_TRUE:
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00001966 node->continue_target()->SetExpectedHeight();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001967 node->continue_target()->Bind();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001968 break;
1969 case ALWAYS_FALSE:
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00001970 node->continue_target()->SetExpectedHeight();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001971 break;
1972 case DONT_KNOW:
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00001973 node->continue_target()->SetExpectedHeight();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001974 body.Bind();
1975 break;
1976 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001977
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001978 CheckStack(); // TODO(1222600): ignore if body contains calls.
1979 VisitAndSpill(node->body());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001980
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001981 // Compile the test.
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001982 switch (info) {
1983 case ALWAYS_TRUE:
1984 // If control can fall off the end of the body, jump back to the
1985 // top.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001986 if (has_valid_frame()) {
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001987 node->continue_target()->Jump();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001988 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001989 break;
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001990 case ALWAYS_FALSE:
1991 // If we have a continue in the body, we only have to bind its
1992 // jump target.
1993 if (node->continue_target()->is_linked()) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001994 node->continue_target()->Bind();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001995 }
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001996 break;
1997 case DONT_KNOW:
1998 // We have to compile the test expression if it can be reached by
1999 // control flow falling out of the body or via continue.
2000 if (node->continue_target()->is_linked()) {
2001 node->continue_target()->Bind();
2002 }
2003 if (has_valid_frame()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002004 Comment cmnt(masm_, "[ DoWhileCondition");
2005 CodeForDoWhileConditionPosition(node);
2006 LoadConditionAndSpill(node->cond(), &body, node->break_target(), true);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002007 if (has_valid_frame()) {
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002008 // A invalid frame here indicates that control did not
2009 // fall out of the test expression.
2010 Branch(true, &body);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002011 }
2012 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002013 break;
2014 }
2015
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002016 if (node->break_target()->is_linked()) {
2017 node->break_target()->Bind();
2018 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00002019 DecrementLoopNesting();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002020 ASSERT(!has_valid_frame() || frame_->height() == original_height);
2021}
2022
2023
2024void CodeGenerator::VisitWhileStatement(WhileStatement* node) {
2025#ifdef DEBUG
2026 int original_height = frame_->height();
2027#endif
ager@chromium.org357bf652010-04-12 11:30:10 +00002028 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002029 Comment cmnt(masm_, "[ WhileStatement");
2030 CodeForStatementPosition(node);
2031
2032 // If the test is never true and has no side effects there is no need
2033 // to compile the test or body.
2034 ConditionAnalysis info = AnalyzeCondition(node->cond());
2035 if (info == ALWAYS_FALSE) return;
2036
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00002037 node->break_target()->SetExpectedHeight();
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00002038 IncrementLoopNesting();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002039
2040 // Label the top of the loop with the continue target for the backward
2041 // CFG edge.
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00002042 node->continue_target()->SetExpectedHeight();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002043 node->continue_target()->Bind();
2044
2045 if (info == DONT_KNOW) {
2046 JumpTarget body;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002047 LoadConditionAndSpill(node->cond(), &body, node->break_target(), true);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002048 if (has_valid_frame()) {
2049 // A NULL frame indicates that control did not fall out of the
2050 // test expression.
2051 Branch(false, node->break_target());
2052 }
2053 if (has_valid_frame() || body.is_linked()) {
2054 body.Bind();
2055 }
2056 }
2057
2058 if (has_valid_frame()) {
2059 CheckStack(); // TODO(1222600): ignore if body contains calls.
2060 VisitAndSpill(node->body());
2061
2062 // If control flow can fall out of the body, jump back to the top.
2063 if (has_valid_frame()) {
2064 node->continue_target()->Jump();
2065 }
2066 }
2067 if (node->break_target()->is_linked()) {
2068 node->break_target()->Bind();
2069 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00002070 DecrementLoopNesting();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002071 ASSERT(!has_valid_frame() || frame_->height() == original_height);
2072}
2073
2074
2075void CodeGenerator::VisitForStatement(ForStatement* node) {
2076#ifdef DEBUG
2077 int original_height = frame_->height();
2078#endif
ager@chromium.org357bf652010-04-12 11:30:10 +00002079 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002080 Comment cmnt(masm_, "[ ForStatement");
2081 CodeForStatementPosition(node);
2082 if (node->init() != NULL) {
2083 VisitAndSpill(node->init());
2084 }
2085
2086 // If the test is never true there is no need to compile the test or
2087 // body.
2088 ConditionAnalysis info = AnalyzeCondition(node->cond());
2089 if (info == ALWAYS_FALSE) return;
2090
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00002091 node->break_target()->SetExpectedHeight();
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00002092 IncrementLoopNesting();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002093
2094 // If there is no update statement, label the top of the loop with the
2095 // continue target, otherwise with the loop target.
2096 JumpTarget loop(JumpTarget::BIDIRECTIONAL);
2097 if (node->next() == NULL) {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00002098 node->continue_target()->SetExpectedHeight();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002099 node->continue_target()->Bind();
2100 } else {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00002101 node->continue_target()->SetExpectedHeight();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002102 loop.Bind();
2103 }
2104
2105 // If the test is always true, there is no need to compile it.
2106 if (info == DONT_KNOW) {
2107 JumpTarget body;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002108 LoadConditionAndSpill(node->cond(), &body, node->break_target(), true);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002109 if (has_valid_frame()) {
2110 Branch(false, node->break_target());
2111 }
2112 if (has_valid_frame() || body.is_linked()) {
2113 body.Bind();
2114 }
2115 }
2116
2117 if (has_valid_frame()) {
2118 CheckStack(); // TODO(1222600): ignore if body contains calls.
2119 VisitAndSpill(node->body());
2120
2121 if (node->next() == NULL) {
2122 // If there is no update statement and control flow can fall out
2123 // of the loop, jump directly to the continue label.
2124 if (has_valid_frame()) {
2125 node->continue_target()->Jump();
2126 }
2127 } else {
2128 // If there is an update statement and control flow can reach it
2129 // via falling out of the body of the loop or continuing, we
2130 // compile the update statement.
2131 if (node->continue_target()->is_linked()) {
2132 node->continue_target()->Bind();
2133 }
2134 if (has_valid_frame()) {
2135 // Record source position of the statement as this code which is
2136 // after the code for the body actually belongs to the loop
2137 // statement and not the body.
2138 CodeForStatementPosition(node);
2139 VisitAndSpill(node->next());
2140 loop.Jump();
2141 }
2142 }
2143 }
2144 if (node->break_target()->is_linked()) {
2145 node->break_target()->Bind();
2146 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00002147 DecrementLoopNesting();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002148 ASSERT(!has_valid_frame() || frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002149}
2150
2151
ager@chromium.org7c537e22008-10-16 08:43:32 +00002152void CodeGenerator::VisitForInStatement(ForInStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002153#ifdef DEBUG
2154 int original_height = frame_->height();
2155#endif
ager@chromium.org357bf652010-04-12 11:30:10 +00002156 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002157 Comment cmnt(masm_, "[ ForInStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002158 CodeForStatementPosition(node);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002159
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002160 JumpTarget primitive;
2161 JumpTarget jsobject;
2162 JumpTarget fixed_array;
2163 JumpTarget entry(JumpTarget::BIDIRECTIONAL);
2164 JumpTarget end_del_check;
2165 JumpTarget exit;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002166
2167 // Get the object to enumerate over (converted to JSObject).
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002168 LoadAndSpill(node->enumerable());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002169
2170 // Both SpiderMonkey and kjs ignore null and undefined in contrast
2171 // to the specification. 12.6.4 mandates a call to ToObject.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002172 frame_->EmitPop(r0);
ager@chromium.orgab99eea2009-08-25 07:05:41 +00002173 __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
2174 __ cmp(r0, ip);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002175 exit.Branch(eq);
ager@chromium.orgab99eea2009-08-25 07:05:41 +00002176 __ LoadRoot(ip, Heap::kNullValueRootIndex);
2177 __ cmp(r0, ip);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002178 exit.Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002179
2180 // Stack layout in body:
2181 // [iteration counter (Smi)]
2182 // [length of array]
2183 // [FixedArray]
2184 // [Map or 0]
2185 // [Object]
2186
2187 // Check if enumerable is already a JSObject
2188 __ tst(r0, Operand(kSmiTagMask));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002189 primitive.Branch(eq);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002190 __ CompareObjectType(r0, r1, r1, FIRST_JS_OBJECT_TYPE);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002191 jsobject.Branch(hs);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002192
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002193 primitive.Bind();
2194 frame_->EmitPush(r0);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00002195 frame_->InvokeBuiltin(Builtins::TO_OBJECT, CALL_JS, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002196
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002197 jsobject.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002198 // Get the set of properties (as a FixedArray or Map).
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00002199 // r0: value to be iterated over
2200 frame_->EmitPush(r0); // Push the object being iterated over.
2201
2202 // Check cache validity in generated code. This is a fast case for
2203 // the JSObject::IsSimpleEnum cache validity checks. If we cannot
2204 // guarantee cache validity, call the runtime system to check cache
2205 // validity or get the property names in a fixed array.
2206 JumpTarget call_runtime;
2207 JumpTarget loop(JumpTarget::BIDIRECTIONAL);
2208 JumpTarget check_prototype;
2209 JumpTarget use_cache;
2210 __ mov(r1, Operand(r0));
2211 loop.Bind();
2212 // Check that there are no elements.
2213 __ ldr(r2, FieldMemOperand(r1, JSObject::kElementsOffset));
2214 __ LoadRoot(r4, Heap::kEmptyFixedArrayRootIndex);
2215 __ cmp(r2, r4);
2216 call_runtime.Branch(ne);
2217 // Check that instance descriptors are not empty so that we can
2218 // check for an enum cache. Leave the map in r3 for the subsequent
2219 // prototype load.
2220 __ ldr(r3, FieldMemOperand(r1, HeapObject::kMapOffset));
2221 __ ldr(r2, FieldMemOperand(r3, Map::kInstanceDescriptorsOffset));
2222 __ LoadRoot(ip, Heap::kEmptyDescriptorArrayRootIndex);
2223 __ cmp(r2, ip);
2224 call_runtime.Branch(eq);
2225 // Check that there in an enum cache in the non-empty instance
2226 // descriptors. This is the case if the next enumeration index
2227 // field does not contain a smi.
2228 __ ldr(r2, FieldMemOperand(r2, DescriptorArray::kEnumerationIndexOffset));
2229 __ tst(r2, Operand(kSmiTagMask));
2230 call_runtime.Branch(eq);
2231 // For all objects but the receiver, check that the cache is empty.
2232 // r4: empty fixed array root.
2233 __ cmp(r1, r0);
2234 check_prototype.Branch(eq);
2235 __ ldr(r2, FieldMemOperand(r2, DescriptorArray::kEnumCacheBridgeCacheOffset));
2236 __ cmp(r2, r4);
2237 call_runtime.Branch(ne);
2238 check_prototype.Bind();
2239 // Load the prototype from the map and loop if non-null.
2240 __ ldr(r1, FieldMemOperand(r3, Map::kPrototypeOffset));
2241 __ LoadRoot(ip, Heap::kNullValueRootIndex);
2242 __ cmp(r1, ip);
2243 loop.Branch(ne);
2244 // The enum cache is valid. Load the map of the object being
2245 // iterated over and use the cache for the iteration.
2246 __ ldr(r0, FieldMemOperand(r0, HeapObject::kMapOffset));
2247 use_cache.Jump();
2248
2249 call_runtime.Bind();
2250 // Call the runtime to get the property names for the object.
2251 frame_->EmitPush(r0); // push the object (slot 4) for the runtime call
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002252 frame_->CallRuntime(Runtime::kGetPropertyNamesFast, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002253
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00002254 // If we got a map from the runtime call, we can do a fast
2255 // modification check. Otherwise, we got a fixed array, and we have
2256 // to do a slow check.
2257 // r0: map or fixed array (result from call to
2258 // Runtime::kGetPropertyNamesFast)
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002259 __ mov(r2, Operand(r0));
2260 __ ldr(r1, FieldMemOperand(r2, HeapObject::kMapOffset));
ager@chromium.orgab99eea2009-08-25 07:05:41 +00002261 __ LoadRoot(ip, Heap::kMetaMapRootIndex);
2262 __ cmp(r1, ip);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002263 fixed_array.Branch(ne);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002264
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00002265 use_cache.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002266 // Get enum cache
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00002267 // r0: map (either the result from a call to
2268 // Runtime::kGetPropertyNamesFast or has been fetched directly from
2269 // the object)
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002270 __ mov(r1, Operand(r0));
2271 __ ldr(r1, FieldMemOperand(r1, Map::kInstanceDescriptorsOffset));
2272 __ ldr(r1, FieldMemOperand(r1, DescriptorArray::kEnumerationIndexOffset));
2273 __ ldr(r2,
2274 FieldMemOperand(r1, DescriptorArray::kEnumCacheBridgeCacheOffset));
2275
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002276 frame_->EmitPush(r0); // map
2277 frame_->EmitPush(r2); // enum cache bridge cache
mads.s.ager31e71382008-08-13 09:32:07 +00002278 __ ldr(r0, FieldMemOperand(r2, FixedArray::kLengthOffset));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002279 __ mov(r0, Operand(r0, LSL, kSmiTagSize));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002280 frame_->EmitPush(r0);
mads.s.ager31e71382008-08-13 09:32:07 +00002281 __ mov(r0, Operand(Smi::FromInt(0)));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002282 frame_->EmitPush(r0);
2283 entry.Jump();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002284
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002285 fixed_array.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002286 __ mov(r1, Operand(Smi::FromInt(0)));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002287 frame_->EmitPush(r1); // insert 0 in place of Map
2288 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002289
2290 // Push the length of the array and the initial index onto the stack.
mads.s.ager31e71382008-08-13 09:32:07 +00002291 __ ldr(r0, FieldMemOperand(r0, FixedArray::kLengthOffset));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002292 __ mov(r0, Operand(r0, LSL, kSmiTagSize));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002293 frame_->EmitPush(r0);
mads.s.ager31e71382008-08-13 09:32:07 +00002294 __ mov(r0, Operand(Smi::FromInt(0))); // init index
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002295 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002296
2297 // Condition.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002298 entry.Bind();
mads.s.ager31e71382008-08-13 09:32:07 +00002299 // sp[0] : index
2300 // sp[1] : array/enum cache length
2301 // sp[2] : array or enum cache
2302 // sp[3] : 0 or map
2303 // sp[4] : enumerable
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002304 // Grab the current frame's height for the break and continue
2305 // targets only after all the state is pushed on the frame.
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00002306 node->break_target()->SetExpectedHeight();
2307 node->continue_target()->SetExpectedHeight();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002308
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00002309 // Load the current count to r0, load the length to r1.
2310 __ ldrd(r0, frame_->ElementAt(0));
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00002311 __ cmp(r0, r1); // compare to the array length
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002312 node->break_target()->Branch(hs);
2313
2314 __ ldr(r0, frame_->ElementAt(0));
mads.s.ager31e71382008-08-13 09:32:07 +00002315
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002316 // Get the i'th entry of the array.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002317 __ ldr(r2, frame_->ElementAt(2));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002318 __ add(r2, r2, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
2319 __ ldr(r3, MemOperand(r2, r0, LSL, kPointerSizeLog2 - kSmiTagSize));
2320
2321 // Get Map or 0.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002322 __ ldr(r2, frame_->ElementAt(3));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002323 // Check if this (still) matches the map of the enumerable.
2324 // If not, we have to filter the key.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002325 __ ldr(r1, frame_->ElementAt(4));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002326 __ ldr(r1, FieldMemOperand(r1, HeapObject::kMapOffset));
2327 __ cmp(r1, Operand(r2));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002328 end_del_check.Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002329
2330 // Convert the entry to a string (or null if it isn't a property anymore).
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002331 __ ldr(r0, frame_->ElementAt(4)); // push enumerable
2332 frame_->EmitPush(r0);
2333 frame_->EmitPush(r3); // push entry
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00002334 frame_->InvokeBuiltin(Builtins::FILTER_KEY, CALL_JS, 2);
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00002335 __ mov(r3, Operand(r0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002336
2337 // If the property has been removed while iterating, we just skip it.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00002338 __ LoadRoot(ip, Heap::kNullValueRootIndex);
2339 __ cmp(r3, ip);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002340 node->continue_target()->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002341
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002342 end_del_check.Bind();
2343 // Store the entry in the 'each' expression and take another spin in the
2344 // loop. r3: i'th entry of the enum cache (or string there of)
2345 frame_->EmitPush(r3); // push entry
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002346 { Reference each(this, node->each());
2347 if (!each.is_illegal()) {
mads.s.ager31e71382008-08-13 09:32:07 +00002348 if (each.size() > 0) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002349 __ ldr(r0, frame_->ElementAt(each.size()));
2350 frame_->EmitPush(r0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002351 each.SetValue(NOT_CONST_INIT);
2352 frame_->Drop(2);
2353 } else {
2354 // If the reference was to a slot we rely on the convenient property
2355 // that it doesn't matter whether a value (eg, r3 pushed above) is
2356 // right on top of or right underneath a zero-sized reference.
2357 each.SetValue(NOT_CONST_INIT);
2358 frame_->Drop();
mads.s.ager31e71382008-08-13 09:32:07 +00002359 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002360 }
2361 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002362 // Body.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002363 CheckStack(); // TODO(1222600): ignore if body contains calls.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002364 VisitAndSpill(node->body());
2365
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002366 // Next. Reestablish a spilled frame in case we are coming here via
2367 // a continue in the body.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002368 node->continue_target()->Bind();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002369 frame_->SpillAll();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002370 frame_->EmitPop(r0);
2371 __ add(r0, r0, Operand(Smi::FromInt(1)));
2372 frame_->EmitPush(r0);
2373 entry.Jump();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002374
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002375 // Cleanup. No need to spill because VirtualFrame::Drop is safe for
2376 // any frame.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002377 node->break_target()->Bind();
ager@chromium.org3bf7b912008-11-17 09:09:45 +00002378 frame_->Drop(5);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002379
2380 // Exit.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002381 exit.Bind();
2382 node->continue_target()->Unuse();
2383 node->break_target()->Unuse();
2384 ASSERT(frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002385}
2386
2387
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002388void CodeGenerator::VisitTryCatchStatement(TryCatchStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002389#ifdef DEBUG
2390 int original_height = frame_->height();
2391#endif
ager@chromium.org357bf652010-04-12 11:30:10 +00002392 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002393 Comment cmnt(masm_, "[ TryCatchStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002394 CodeForStatementPosition(node);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002395
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002396 JumpTarget try_block;
2397 JumpTarget exit;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002398
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002399 try_block.Call();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002400 // --- Catch block ---
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002401 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002402
2403 // Store the caught exception in the catch variable.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002404 Variable* catch_var = node->catch_var()->var();
2405 ASSERT(catch_var != NULL && catch_var->slot() != NULL);
2406 StoreToSlot(catch_var->slot(), NOT_CONST_INIT);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002407
2408 // Remove the exception from the stack.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002409 frame_->Drop();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002410
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002411 VisitStatementsAndSpill(node->catch_block()->statements());
2412 if (frame_ != NULL) {
2413 exit.Jump();
2414 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002415
2416
2417 // --- Try block ---
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002418 try_block.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002419
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002420 frame_->PushTryHandler(TRY_CATCH_HANDLER);
2421 int handler_height = frame_->height();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002422
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002423 // Shadow the labels for all escapes from the try block, including
2424 // returns. During shadowing, the original label is hidden as the
2425 // LabelShadow and operations on the original actually affect the
2426 // shadowing label.
2427 //
2428 // We should probably try to unify the escaping labels and the return
2429 // label.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002430 int nof_escapes = node->escaping_targets()->length();
2431 List<ShadowTarget*> shadows(1 + nof_escapes);
2432
2433 // Add the shadow target for the function return.
2434 static const int kReturnShadowIndex = 0;
2435 shadows.Add(new ShadowTarget(&function_return_));
2436 bool function_return_was_shadowed = function_return_is_shadowed_;
2437 function_return_is_shadowed_ = true;
2438 ASSERT(shadows[kReturnShadowIndex]->other_target() == &function_return_);
2439
2440 // Add the remaining shadow targets.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002441 for (int i = 0; i < nof_escapes; i++) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002442 shadows.Add(new ShadowTarget(node->escaping_targets()->at(i)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002443 }
2444
2445 // Generate code for the statements in the try block.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002446 VisitStatementsAndSpill(node->try_block()->statements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002447
2448 // Stop the introduced shadowing and count the number of required unlinks.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002449 // After shadowing stops, the original labels are unshadowed and the
2450 // LabelShadows represent the formerly shadowing labels.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002451 bool has_unlinks = false;
2452 for (int i = 0; i < shadows.length(); i++) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002453 shadows[i]->StopShadowing();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002454 has_unlinks = has_unlinks || shadows[i]->is_linked();
2455 }
2456 function_return_is_shadowed_ = function_return_was_shadowed;
2457
2458 // Get an external reference to the handler address.
2459 ExternalReference handler_address(Top::k_handler_address);
2460
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002461 // If we can fall off the end of the try block, unlink from try chain.
2462 if (has_valid_frame()) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002463 // The next handler address is on top of the frame. Unlink from
2464 // the handler list and drop the rest of this handler from the
2465 // frame.
2466 ASSERT(StackHandlerConstants::kNextOffset == 0);
2467 frame_->EmitPop(r1);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002468 __ mov(r3, Operand(handler_address));
2469 __ str(r1, MemOperand(r3));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002470 frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002471 if (has_unlinks) {
2472 exit.Jump();
2473 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002474 }
2475
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002476 // Generate unlink code for the (formerly) shadowing labels that have been
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002477 // jumped to. Deallocate each shadow target.
2478 for (int i = 0; i < shadows.length(); i++) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002479 if (shadows[i]->is_linked()) {
mads.s.ager31e71382008-08-13 09:32:07 +00002480 // Unlink from try chain;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002481 shadows[i]->Bind();
2482 // Because we can be jumping here (to spilled code) from unspilled
2483 // code, we need to reestablish a spilled frame at this block.
2484 frame_->SpillAll();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002485
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002486 // Reload sp from the top handler, because some statements that we
2487 // break from (eg, for...in) may have left stuff on the stack.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002488 __ mov(r3, Operand(handler_address));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002489 __ ldr(sp, MemOperand(r3));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002490 frame_->Forget(frame_->height() - handler_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002491
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002492 ASSERT(StackHandlerConstants::kNextOffset == 0);
2493 frame_->EmitPop(r1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002494 __ str(r1, MemOperand(r3));
ager@chromium.org3bf7b912008-11-17 09:09:45 +00002495 frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002496
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002497 if (!function_return_is_shadowed_ && i == kReturnShadowIndex) {
2498 frame_->PrepareForReturn();
2499 }
2500 shadows[i]->other_target()->Jump();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002501 }
2502 }
2503
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002504 exit.Bind();
2505 ASSERT(!has_valid_frame() || frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002506}
2507
2508
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002509void CodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002510#ifdef DEBUG
2511 int original_height = frame_->height();
2512#endif
ager@chromium.org357bf652010-04-12 11:30:10 +00002513 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002514 Comment cmnt(masm_, "[ TryFinallyStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002515 CodeForStatementPosition(node);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002516
2517 // State: Used to keep track of reason for entering the finally
2518 // block. Should probably be extended to hold information for
2519 // break/continue from within the try block.
2520 enum { FALLING, THROWING, JUMPING };
2521
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002522 JumpTarget try_block;
2523 JumpTarget finally_block;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002524
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002525 try_block.Call();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002526
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002527 frame_->EmitPush(r0); // save exception object on the stack
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002528 // In case of thrown exceptions, this is where we continue.
2529 __ mov(r2, Operand(Smi::FromInt(THROWING)));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002530 finally_block.Jump();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002531
2532 // --- Try block ---
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002533 try_block.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002534
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002535 frame_->PushTryHandler(TRY_FINALLY_HANDLER);
2536 int handler_height = frame_->height();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002537
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002538 // Shadow the labels for all escapes from the try block, including
2539 // returns. Shadowing hides the original label as the LabelShadow and
2540 // operations on the original actually affect the shadowing label.
2541 //
2542 // We should probably try to unify the escaping labels and the return
2543 // label.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002544 int nof_escapes = node->escaping_targets()->length();
2545 List<ShadowTarget*> shadows(1 + nof_escapes);
2546
2547 // Add the shadow target for the function return.
2548 static const int kReturnShadowIndex = 0;
2549 shadows.Add(new ShadowTarget(&function_return_));
2550 bool function_return_was_shadowed = function_return_is_shadowed_;
2551 function_return_is_shadowed_ = true;
2552 ASSERT(shadows[kReturnShadowIndex]->other_target() == &function_return_);
2553
2554 // Add the remaining shadow targets.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002555 for (int i = 0; i < nof_escapes; i++) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002556 shadows.Add(new ShadowTarget(node->escaping_targets()->at(i)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002557 }
2558
2559 // Generate code for the statements in the try block.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002560 VisitStatementsAndSpill(node->try_block()->statements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002561
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002562 // Stop the introduced shadowing and count the number of required unlinks.
2563 // After shadowing stops, the original labels are unshadowed and the
2564 // LabelShadows represent the formerly shadowing labels.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002565 int nof_unlinks = 0;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002566 for (int i = 0; i < shadows.length(); i++) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002567 shadows[i]->StopShadowing();
2568 if (shadows[i]->is_linked()) nof_unlinks++;
2569 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002570 function_return_is_shadowed_ = function_return_was_shadowed;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002571
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002572 // Get an external reference to the handler address.
2573 ExternalReference handler_address(Top::k_handler_address);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002574
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002575 // If we can fall off the end of the try block, unlink from the try
2576 // chain and set the state on the frame to FALLING.
2577 if (has_valid_frame()) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002578 // The next handler address is on top of the frame.
2579 ASSERT(StackHandlerConstants::kNextOffset == 0);
2580 frame_->EmitPop(r1);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002581 __ mov(r3, Operand(handler_address));
2582 __ str(r1, MemOperand(r3));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002583 frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002584
2585 // Fake a top of stack value (unneeded when FALLING) and set the
2586 // state in r2, then jump around the unlink blocks if any.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00002587 __ LoadRoot(r0, Heap::kUndefinedValueRootIndex);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002588 frame_->EmitPush(r0);
2589 __ mov(r2, Operand(Smi::FromInt(FALLING)));
2590 if (nof_unlinks > 0) {
2591 finally_block.Jump();
2592 }
2593 }
2594
2595 // Generate code to unlink and set the state for the (formerly)
2596 // shadowing targets that have been jumped to.
2597 for (int i = 0; i < shadows.length(); i++) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002598 if (shadows[i]->is_linked()) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002599 // If we have come from the shadowed return, the return value is
2600 // in (a non-refcounted reference to) r0. We must preserve it
2601 // until it is pushed.
2602 //
2603 // Because we can be jumping here (to spilled code) from
2604 // unspilled code, we need to reestablish a spilled frame at
2605 // this block.
2606 shadows[i]->Bind();
2607 frame_->SpillAll();
2608
2609 // Reload sp from the top handler, because some statements that
2610 // we break from (eg, for...in) may have left stuff on the
2611 // stack.
2612 __ mov(r3, Operand(handler_address));
2613 __ ldr(sp, MemOperand(r3));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002614 frame_->Forget(frame_->height() - handler_height);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002615
2616 // Unlink this handler and drop it from the frame. The next
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002617 // handler address is currently on top of the frame.
2618 ASSERT(StackHandlerConstants::kNextOffset == 0);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002619 frame_->EmitPop(r1);
2620 __ str(r1, MemOperand(r3));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002621 frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002622
2623 if (i == kReturnShadowIndex) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002624 // If this label shadowed the function return, materialize the
2625 // return value on the stack.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002626 frame_->EmitPush(r0);
mads.s.ager31e71382008-08-13 09:32:07 +00002627 } else {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002628 // Fake TOS for targets that shadowed breaks and continues.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00002629 __ LoadRoot(r0, Heap::kUndefinedValueRootIndex);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002630 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002631 }
2632 __ mov(r2, Operand(Smi::FromInt(JUMPING + i)));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002633 if (--nof_unlinks > 0) {
2634 // If this is not the last unlink block, jump around the next.
2635 finally_block.Jump();
2636 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002637 }
2638 }
2639
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002640 // --- Finally block ---
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002641 finally_block.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002642
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002643 // Push the state on the stack.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002644 frame_->EmitPush(r2);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002645
2646 // We keep two elements on the stack - the (possibly faked) result
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002647 // and the state - while evaluating the finally block.
2648 //
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002649 // Generate code for the statements in the finally block.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002650 VisitStatementsAndSpill(node->finally_block()->statements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002651
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002652 if (has_valid_frame()) {
2653 // Restore state and return value or faked TOS.
2654 frame_->EmitPop(r2);
2655 frame_->EmitPop(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002656 }
2657
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002658 // Generate code to jump to the right destination for all used
2659 // formerly shadowing targets. Deallocate each shadow target.
2660 for (int i = 0; i < shadows.length(); i++) {
2661 if (has_valid_frame() && shadows[i]->is_bound()) {
2662 JumpTarget* original = shadows[i]->other_target();
2663 __ cmp(r2, Operand(Smi::FromInt(JUMPING + i)));
2664 if (!function_return_is_shadowed_ && i == kReturnShadowIndex) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002665 JumpTarget skip;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002666 skip.Branch(ne);
2667 frame_->PrepareForReturn();
2668 original->Jump();
2669 skip.Bind();
2670 } else {
2671 original->Branch(eq);
2672 }
2673 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002674 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002675
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002676 if (has_valid_frame()) {
2677 // Check if we need to rethrow the exception.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002678 JumpTarget exit;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002679 __ cmp(r2, Operand(Smi::FromInt(THROWING)));
2680 exit.Branch(ne);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002681
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002682 // Rethrow exception.
2683 frame_->EmitPush(r0);
2684 frame_->CallRuntime(Runtime::kReThrow, 1);
2685
2686 // Done.
2687 exit.Bind();
2688 }
2689 ASSERT(!has_valid_frame() || frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002690}
2691
2692
ager@chromium.org7c537e22008-10-16 08:43:32 +00002693void CodeGenerator::VisitDebuggerStatement(DebuggerStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002694#ifdef DEBUG
2695 int original_height = frame_->height();
2696#endif
ager@chromium.org357bf652010-04-12 11:30:10 +00002697 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002698 Comment cmnt(masm_, "[ DebuggerStatament");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002699 CodeForStatementPosition(node);
ager@chromium.org65dad4b2009-04-23 08:48:43 +00002700#ifdef ENABLE_DEBUGGER_SUPPORT
ager@chromium.org5c838252010-02-19 08:53:10 +00002701 frame_->DebugBreak();
ager@chromium.org65dad4b2009-04-23 08:48:43 +00002702#endif
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00002703 // Ignore the return value.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002704 ASSERT(frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002705}
2706
2707
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00002708void CodeGenerator::InstantiateFunction(
2709 Handle<SharedFunctionInfo> function_info) {
ager@chromium.org357bf652010-04-12 11:30:10 +00002710 VirtualFrame::SpilledScope spilled_scope(frame_);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00002711 __ mov(r0, Operand(function_info));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002712 // Use the fast case closure allocation code that allocates in new
2713 // space for nested functions that don't need literals cloning.
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00002714 if (scope()->is_function_scope() && function_info->num_literals() == 0) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002715 FastNewClosureStub stub;
2716 frame_->EmitPush(r0);
2717 frame_->CallStub(&stub, 1);
2718 frame_->EmitPush(r0);
2719 } else {
2720 // Create a new closure.
2721 frame_->EmitPush(cp);
2722 frame_->EmitPush(r0);
2723 frame_->CallRuntime(Runtime::kNewClosure, 2);
2724 frame_->EmitPush(r0);
2725 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002726}
2727
2728
ager@chromium.org7c537e22008-10-16 08:43:32 +00002729void CodeGenerator::VisitFunctionLiteral(FunctionLiteral* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002730#ifdef DEBUG
2731 int original_height = frame_->height();
2732#endif
ager@chromium.org357bf652010-04-12 11:30:10 +00002733 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002734 Comment cmnt(masm_, "[ FunctionLiteral");
2735
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00002736 // Build the function info and instantiate it.
2737 Handle<SharedFunctionInfo> function_info =
2738 Compiler::BuildFunctionInfo(node, script(), this);
kasper.lund212ac232008-07-16 07:07:30 +00002739 // Check for stack-overflow exception.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002740 if (HasStackOverflow()) {
2741 ASSERT(frame_->height() == original_height);
2742 return;
2743 }
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00002744 InstantiateFunction(function_info);
ager@chromium.orgac091b72010-05-05 07:34:42 +00002745 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002746}
2747
2748
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00002749void CodeGenerator::VisitSharedFunctionInfoLiteral(
2750 SharedFunctionInfoLiteral* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002751#ifdef DEBUG
2752 int original_height = frame_->height();
2753#endif
ager@chromium.org357bf652010-04-12 11:30:10 +00002754 VirtualFrame::SpilledScope spilled_scope(frame_);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00002755 Comment cmnt(masm_, "[ SharedFunctionInfoLiteral");
2756 InstantiateFunction(node->shared_function_info());
ager@chromium.orgac091b72010-05-05 07:34:42 +00002757 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002758}
2759
2760
ager@chromium.org7c537e22008-10-16 08:43:32 +00002761void CodeGenerator::VisitConditional(Conditional* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002762#ifdef DEBUG
2763 int original_height = frame_->height();
2764#endif
ager@chromium.org357bf652010-04-12 11:30:10 +00002765 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002766 Comment cmnt(masm_, "[ Conditional");
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002767 JumpTarget then;
2768 JumpTarget else_;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002769 LoadConditionAndSpill(node->condition(), &then, &else_, true);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00002770 if (has_valid_frame()) {
2771 Branch(false, &else_);
2772 }
2773 if (has_valid_frame() || then.is_linked()) {
2774 then.Bind();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002775 LoadAndSpill(node->then_expression());
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00002776 }
2777 if (else_.is_linked()) {
2778 JumpTarget exit;
2779 if (has_valid_frame()) exit.Jump();
2780 else_.Bind();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002781 LoadAndSpill(node->else_expression());
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00002782 if (exit.is_linked()) exit.Bind();
2783 }
ager@chromium.orgac091b72010-05-05 07:34:42 +00002784 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002785}
2786
2787
ager@chromium.org7c537e22008-10-16 08:43:32 +00002788void CodeGenerator::LoadFromSlot(Slot* slot, TypeofState typeof_state) {
2789 if (slot->type() == Slot::LOOKUP) {
ager@chromium.org381abbb2009-02-25 13:23:22 +00002790 ASSERT(slot->var()->is_dynamic());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002791
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00002792 // JumpTargets do not yet support merging frames so the frame must be
2793 // spilled when jumping to these targets.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002794 JumpTarget slow;
2795 JumpTarget done;
ager@chromium.org381abbb2009-02-25 13:23:22 +00002796
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00002797 // Generate fast case for loading from slots that correspond to
2798 // local/global variables or arguments unless they are shadowed by
2799 // eval-introduced bindings.
2800 EmitDynamicLoadFromSlotFastCase(slot,
2801 typeof_state,
2802 &slow,
2803 &done);
ager@chromium.org381abbb2009-02-25 13:23:22 +00002804
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002805 slow.Bind();
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00002806 VirtualFrame::SpilledScope spilled_scope(frame_);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002807 frame_->EmitPush(cp);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002808 __ mov(r0, Operand(slot->var()->name()));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002809 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002810
ager@chromium.org7c537e22008-10-16 08:43:32 +00002811 if (typeof_state == INSIDE_TYPEOF) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002812 frame_->CallRuntime(Runtime::kLoadContextSlotNoReferenceError, 2);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002813 } else {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002814 frame_->CallRuntime(Runtime::kLoadContextSlot, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002815 }
ager@chromium.org381abbb2009-02-25 13:23:22 +00002816
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002817 done.Bind();
2818 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002819
2820 } else {
ager@chromium.org357bf652010-04-12 11:30:10 +00002821 Register scratch = VirtualFrame::scratch0();
2822 frame_->EmitPush(SlotOperand(slot, scratch));
ager@chromium.org7c537e22008-10-16 08:43:32 +00002823 if (slot->var()->mode() == Variable::CONST) {
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00002824 // Const slots may contain 'the hole' value (the constant hasn't been
2825 // initialized yet) which needs to be converted into the 'undefined'
2826 // value.
2827 Comment cmnt(masm_, "[ Unhole const");
ager@chromium.org357bf652010-04-12 11:30:10 +00002828 frame_->EmitPop(scratch);
ager@chromium.orgab99eea2009-08-25 07:05:41 +00002829 __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
ager@chromium.org357bf652010-04-12 11:30:10 +00002830 __ cmp(scratch, ip);
2831 __ LoadRoot(scratch, Heap::kUndefinedValueRootIndex, eq);
2832 frame_->EmitPush(scratch);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002833 }
2834 }
2835}
2836
2837
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00002838void CodeGenerator::LoadFromSlotCheckForArguments(Slot* slot,
2839 TypeofState state) {
2840 LoadFromSlot(slot, state);
2841
2842 // Bail out quickly if we're not using lazy arguments allocation.
2843 if (ArgumentsMode() != LAZY_ARGUMENTS_ALLOCATION) return;
2844
2845 // ... or if the slot isn't a non-parameter arguments slot.
2846 if (slot->type() == Slot::PARAMETER || !slot->is_arguments()) return;
2847
2848 VirtualFrame::SpilledScope spilled_scope(frame_);
2849
2850 // Load the loaded value from the stack into r0 but leave it on the
2851 // stack.
2852 __ ldr(r0, MemOperand(sp, 0));
2853
2854 // If the loaded value is the sentinel that indicates that we
2855 // haven't loaded the arguments object yet, we need to do it now.
2856 JumpTarget exit;
2857 __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
2858 __ cmp(r0, ip);
2859 exit.Branch(ne);
2860 frame_->Drop();
2861 StoreArgumentsObject(false);
2862 exit.Bind();
2863}
2864
2865
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002866void CodeGenerator::StoreToSlot(Slot* slot, InitState init_state) {
2867 ASSERT(slot != NULL);
2868 if (slot->type() == Slot::LOOKUP) {
ager@chromium.org357bf652010-04-12 11:30:10 +00002869 VirtualFrame::SpilledScope spilled_scope(frame_);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002870 ASSERT(slot->var()->is_dynamic());
2871
2872 // For now, just do a runtime call.
2873 frame_->EmitPush(cp);
2874 __ mov(r0, Operand(slot->var()->name()));
2875 frame_->EmitPush(r0);
2876
2877 if (init_state == CONST_INIT) {
2878 // Same as the case for a normal store, but ignores attribute
2879 // (e.g. READ_ONLY) of context slot so that we can initialize
2880 // const properties (introduced via eval("const foo = (some
2881 // expr);")). Also, uses the current function context instead of
2882 // the top context.
2883 //
2884 // Note that we must declare the foo upon entry of eval(), via a
2885 // context slot declaration, but we cannot initialize it at the
2886 // same time, because the const declaration may be at the end of
2887 // the eval code (sigh...) and the const variable may have been
2888 // used before (where its value is 'undefined'). Thus, we can only
2889 // do the initialization when we actually encounter the expression
2890 // and when the expression operands are defined and valid, and
2891 // thus we need the split into 2 operations: declaration of the
2892 // context slot followed by initialization.
2893 frame_->CallRuntime(Runtime::kInitializeConstContextSlot, 3);
2894 } else {
2895 frame_->CallRuntime(Runtime::kStoreContextSlot, 3);
2896 }
2897 // Storing a variable must keep the (new) value on the expression
2898 // stack. This is necessary for compiling assignment expressions.
2899 frame_->EmitPush(r0);
2900
2901 } else {
2902 ASSERT(!slot->var()->is_dynamic());
ager@chromium.org357bf652010-04-12 11:30:10 +00002903 Register scratch = VirtualFrame::scratch0();
2904 VirtualFrame::RegisterAllocationScope scope(this);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002905
ager@chromium.org357bf652010-04-12 11:30:10 +00002906 // The frame must be spilled when branching to this target.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002907 JumpTarget exit;
ager@chromium.org357bf652010-04-12 11:30:10 +00002908
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002909 if (init_state == CONST_INIT) {
2910 ASSERT(slot->var()->mode() == Variable::CONST);
2911 // Only the first const initialization must be executed (the slot
2912 // still contains 'the hole' value). When the assignment is
2913 // executed, the code is identical to a normal store (see below).
2914 Comment cmnt(masm_, "[ Init const");
ager@chromium.org357bf652010-04-12 11:30:10 +00002915 __ ldr(scratch, SlotOperand(slot, scratch));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002916 __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
ager@chromium.org357bf652010-04-12 11:30:10 +00002917 __ cmp(scratch, ip);
2918 frame_->SpillAll();
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002919 exit.Branch(ne);
2920 }
2921
2922 // We must execute the store. Storing a variable must keep the
2923 // (new) value on the stack. This is necessary for compiling
2924 // assignment expressions.
2925 //
2926 // Note: We will reach here even with slot->var()->mode() ==
2927 // Variable::CONST because of const declarations which will
2928 // initialize consts to 'the hole' value and by doing so, end up
2929 // calling this code. r2 may be loaded with context; used below in
2930 // RecordWrite.
ager@chromium.org357bf652010-04-12 11:30:10 +00002931 Register tos = frame_->Peek();
2932 __ str(tos, SlotOperand(slot, scratch));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002933 if (slot->type() == Slot::CONTEXT) {
2934 // Skip write barrier if the written value is a smi.
ager@chromium.org357bf652010-04-12 11:30:10 +00002935 __ tst(tos, Operand(kSmiTagMask));
2936 // We don't use tos any more after here.
2937 VirtualFrame::SpilledScope spilled_scope(frame_);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002938 exit.Branch(eq);
ager@chromium.org357bf652010-04-12 11:30:10 +00002939 // scratch is loaded with context when calling SlotOperand above.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002940 int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize;
2941 __ mov(r3, Operand(offset));
ager@chromium.org357bf652010-04-12 11:30:10 +00002942 // r1 could be identical with tos, but that doesn't matter.
2943 __ RecordWrite(scratch, r3, r1);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002944 }
2945 // If we definitely did not jump over the assignment, we do not need
2946 // to bind the exit label. Doing so can defeat peephole
2947 // optimization.
2948 if (init_state == CONST_INIT || slot->type() == Slot::CONTEXT) {
ager@chromium.org357bf652010-04-12 11:30:10 +00002949 frame_->SpillAll();
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002950 exit.Bind();
2951 }
2952 }
2953}
2954
2955
ager@chromium.org381abbb2009-02-25 13:23:22 +00002956void CodeGenerator::LoadFromGlobalSlotCheckExtensions(Slot* slot,
2957 TypeofState typeof_state,
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002958 JumpTarget* slow) {
ager@chromium.org381abbb2009-02-25 13:23:22 +00002959 // Check that no extension objects have been created by calls to
2960 // eval from the current scope to the global scope.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00002961 Register tmp = frame_->scratch0();
2962 Register tmp2 = frame_->scratch1();
ager@chromium.org381abbb2009-02-25 13:23:22 +00002963 Register context = cp;
2964 Scope* s = scope();
2965 while (s != NULL) {
2966 if (s->num_heap_slots() > 0) {
2967 if (s->calls_eval()) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00002968 frame_->SpillAll();
ager@chromium.org381abbb2009-02-25 13:23:22 +00002969 // Check that extension is NULL.
2970 __ ldr(tmp2, ContextOperand(context, Context::EXTENSION_INDEX));
2971 __ tst(tmp2, tmp2);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002972 slow->Branch(ne);
ager@chromium.org381abbb2009-02-25 13:23:22 +00002973 }
2974 // Load next context in chain.
2975 __ ldr(tmp, ContextOperand(context, Context::CLOSURE_INDEX));
2976 __ ldr(tmp, FieldMemOperand(tmp, JSFunction::kContextOffset));
2977 context = tmp;
2978 }
2979 // If no outer scope calls eval, we do not need to check more
2980 // context extensions.
2981 if (!s->outer_scope_calls_eval() || s->is_eval_scope()) break;
2982 s = s->outer_scope();
2983 }
2984
2985 if (s->is_eval_scope()) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00002986 frame_->SpillAll();
ager@chromium.org381abbb2009-02-25 13:23:22 +00002987 Label next, fast;
ager@chromium.org357bf652010-04-12 11:30:10 +00002988 __ Move(tmp, context);
ager@chromium.org381abbb2009-02-25 13:23:22 +00002989 __ bind(&next);
2990 // Terminate at global context.
2991 __ ldr(tmp2, FieldMemOperand(tmp, HeapObject::kMapOffset));
ager@chromium.orgab99eea2009-08-25 07:05:41 +00002992 __ LoadRoot(ip, Heap::kGlobalContextMapRootIndex);
2993 __ cmp(tmp2, ip);
ager@chromium.org381abbb2009-02-25 13:23:22 +00002994 __ b(eq, &fast);
2995 // Check that extension is NULL.
2996 __ ldr(tmp2, ContextOperand(tmp, Context::EXTENSION_INDEX));
2997 __ tst(tmp2, tmp2);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002998 slow->Branch(ne);
ager@chromium.org381abbb2009-02-25 13:23:22 +00002999 // Load next context in chain.
3000 __ ldr(tmp, ContextOperand(tmp, Context::CLOSURE_INDEX));
3001 __ ldr(tmp, FieldMemOperand(tmp, JSFunction::kContextOffset));
3002 __ b(&next);
3003 __ bind(&fast);
3004 }
3005
ager@chromium.org381abbb2009-02-25 13:23:22 +00003006 // Load the global object.
3007 LoadGlobal();
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00003008 // Setup the name register and call load IC.
ager@chromium.orgac091b72010-05-05 07:34:42 +00003009 frame_->CallLoadIC(slot->var()->name(),
3010 typeof_state == INSIDE_TYPEOF
3011 ? RelocInfo::CODE_TARGET
3012 : RelocInfo::CODE_TARGET_CONTEXT);
ager@chromium.org381abbb2009-02-25 13:23:22 +00003013}
3014
3015
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00003016void CodeGenerator::EmitDynamicLoadFromSlotFastCase(Slot* slot,
3017 TypeofState typeof_state,
3018 JumpTarget* slow,
3019 JumpTarget* done) {
3020 // Generate fast-case code for variables that might be shadowed by
3021 // eval-introduced variables. Eval is used a lot without
3022 // introducing variables. In those cases, we do not want to
3023 // perform a runtime call for all variables in the scope
3024 // containing the eval.
3025 if (slot->var()->mode() == Variable::DYNAMIC_GLOBAL) {
3026 LoadFromGlobalSlotCheckExtensions(slot, typeof_state, slow);
3027 frame_->SpillAll();
3028 done->Jump();
3029
3030 } else if (slot->var()->mode() == Variable::DYNAMIC_LOCAL) {
3031 frame_->SpillAll();
3032 Slot* potential_slot = slot->var()->local_if_not_shadowed()->slot();
3033 Expression* rewrite = slot->var()->local_if_not_shadowed()->rewrite();
3034 if (potential_slot != NULL) {
3035 // Generate fast case for locals that rewrite to slots.
3036 __ ldr(r0,
3037 ContextSlotOperandCheckExtensions(potential_slot,
3038 r1,
3039 r2,
3040 slow));
3041 if (potential_slot->var()->mode() == Variable::CONST) {
3042 __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
3043 __ cmp(r0, ip);
3044 __ LoadRoot(r0, Heap::kUndefinedValueRootIndex, eq);
3045 }
3046 done->Jump();
3047 } else if (rewrite != NULL) {
3048 // Generate fast case for argument loads.
3049 Property* property = rewrite->AsProperty();
3050 if (property != NULL) {
3051 VariableProxy* obj_proxy = property->obj()->AsVariableProxy();
3052 Literal* key_literal = property->key()->AsLiteral();
3053 if (obj_proxy != NULL &&
3054 key_literal != NULL &&
3055 obj_proxy->IsArguments() &&
3056 key_literal->handle()->IsSmi()) {
3057 // Load arguments object if there are no eval-introduced
3058 // variables. Then load the argument from the arguments
3059 // object using keyed load.
3060 __ ldr(r0,
3061 ContextSlotOperandCheckExtensions(obj_proxy->var()->slot(),
3062 r1,
3063 r2,
3064 slow));
3065 frame_->EmitPush(r0);
3066 __ mov(r1, Operand(key_literal->handle()));
3067 frame_->EmitPush(r1);
3068 EmitKeyedLoad();
3069 done->Jump();
3070 }
3071 }
3072 }
3073 }
3074}
3075
3076
ager@chromium.org7c537e22008-10-16 08:43:32 +00003077void CodeGenerator::VisitSlot(Slot* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003078#ifdef DEBUG
3079 int original_height = frame_->height();
3080#endif
ager@chromium.org7c537e22008-10-16 08:43:32 +00003081 Comment cmnt(masm_, "[ Slot");
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00003082 LoadFromSlotCheckForArguments(node, NOT_INSIDE_TYPEOF);
ager@chromium.orgac091b72010-05-05 07:34:42 +00003083 ASSERT_EQ(original_height + 1, frame_->height());
ager@chromium.org7c537e22008-10-16 08:43:32 +00003084}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003085
ager@chromium.org7c537e22008-10-16 08:43:32 +00003086
3087void CodeGenerator::VisitVariableProxy(VariableProxy* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003088#ifdef DEBUG
3089 int original_height = frame_->height();
3090#endif
ager@chromium.org7c537e22008-10-16 08:43:32 +00003091 Comment cmnt(masm_, "[ VariableProxy");
3092
3093 Variable* var = node->var();
3094 Expression* expr = var->rewrite();
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00003095 if (expr != NULL) {
3096 Visit(expr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003097 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +00003098 ASSERT(var->is_global());
3099 Reference ref(this, node);
ager@chromium.org357bf652010-04-12 11:30:10 +00003100 ref.GetValue();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003101 }
ager@chromium.orgac091b72010-05-05 07:34:42 +00003102 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003103}
3104
3105
ager@chromium.org7c537e22008-10-16 08:43:32 +00003106void CodeGenerator::VisitLiteral(Literal* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003107#ifdef DEBUG
3108 int original_height = frame_->height();
3109#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003110 Comment cmnt(masm_, "[ Literal");
ager@chromium.org357bf652010-04-12 11:30:10 +00003111 Register reg = frame_->GetTOSRegister();
3112 __ mov(reg, Operand(node->handle()));
3113 frame_->EmitPush(reg);
ager@chromium.orgac091b72010-05-05 07:34:42 +00003114 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003115}
3116
3117
ager@chromium.org7c537e22008-10-16 08:43:32 +00003118void CodeGenerator::VisitRegExpLiteral(RegExpLiteral* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003119#ifdef DEBUG
3120 int original_height = frame_->height();
3121#endif
ager@chromium.org357bf652010-04-12 11:30:10 +00003122 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003123 Comment cmnt(masm_, "[ RexExp Literal");
3124
3125 // Retrieve the literal array and check the allocated entry.
3126
3127 // Load the function of this activation.
ager@chromium.org3bf7b912008-11-17 09:09:45 +00003128 __ ldr(r1, frame_->Function());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003129
3130 // Load the literals array of the function.
3131 __ ldr(r1, FieldMemOperand(r1, JSFunction::kLiteralsOffset));
3132
3133 // Load the literal at the ast saved index.
3134 int literal_offset =
3135 FixedArray::kHeaderSize + node->literal_index() * kPointerSize;
3136 __ ldr(r2, FieldMemOperand(r1, literal_offset));
3137
kasperl@chromium.org71affb52009-05-26 05:44:31 +00003138 JumpTarget done;
ager@chromium.orgab99eea2009-08-25 07:05:41 +00003139 __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
3140 __ cmp(r2, ip);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003141 done.Branch(ne);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003142
3143 // If the entry is undefined we call the runtime system to computed
3144 // the literal.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003145 frame_->EmitPush(r1); // literal array (0)
mads.s.ager31e71382008-08-13 09:32:07 +00003146 __ mov(r0, Operand(Smi::FromInt(node->literal_index())));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003147 frame_->EmitPush(r0); // literal index (1)
mads.s.ager31e71382008-08-13 09:32:07 +00003148 __ mov(r0, Operand(node->pattern())); // RegExp pattern (2)
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003149 frame_->EmitPush(r0);
mads.s.ager31e71382008-08-13 09:32:07 +00003150 __ mov(r0, Operand(node->flags())); // RegExp flags (3)
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003151 frame_->EmitPush(r0);
3152 frame_->CallRuntime(Runtime::kMaterializeRegExpLiteral, 4);
mads.s.ager31e71382008-08-13 09:32:07 +00003153 __ mov(r2, Operand(r0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003154
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003155 done.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003156 // Push the literal.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003157 frame_->EmitPush(r2);
ager@chromium.orgac091b72010-05-05 07:34:42 +00003158 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003159}
3160
3161
ager@chromium.org7c537e22008-10-16 08:43:32 +00003162void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003163#ifdef DEBUG
3164 int original_height = frame_->height();
3165#endif
ager@chromium.org357bf652010-04-12 11:30:10 +00003166 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003167 Comment cmnt(masm_, "[ ObjectLiteral");
3168
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003169 // Load the function of this activation.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00003170 __ ldr(r3, frame_->Function());
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003171 // Literal array.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00003172 __ ldr(r3, FieldMemOperand(r3, JSFunction::kLiteralsOffset));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003173 // Literal index.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00003174 __ mov(r2, Operand(Smi::FromInt(node->literal_index())));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003175 // Constant properties.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00003176 __ mov(r1, Operand(node->constant_properties()));
3177 // Should the object literal have fast elements?
3178 __ mov(r0, Operand(Smi::FromInt(node->fast_elements() ? 1 : 0)));
3179 frame_->EmitPushMultiple(4, r3.bit() | r2.bit() | r1.bit() | r0.bit());
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003180 if (node->depth() > 1) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00003181 frame_->CallRuntime(Runtime::kCreateObjectLiteral, 4);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003182 } else {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00003183 frame_->CallRuntime(Runtime::kCreateObjectLiteralShallow, 4);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003184 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003185 frame_->EmitPush(r0); // save the result
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003186 for (int i = 0; i < node->properties()->length(); i++) {
ager@chromium.org5c838252010-02-19 08:53:10 +00003187 // At the start of each iteration, the top of stack contains
3188 // the newly created object literal.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003189 ObjectLiteral::Property* property = node->properties()->at(i);
3190 Literal* key = property->key();
3191 Expression* value = property->value();
3192 switch (property->kind()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003193 case ObjectLiteral::Property::CONSTANT:
3194 break;
3195 case ObjectLiteral::Property::MATERIALIZED_LITERAL:
3196 if (CompileTimeValue::IsCompileTimeValue(property->value())) break;
3197 // else fall through
ager@chromium.org5c838252010-02-19 08:53:10 +00003198 case ObjectLiteral::Property::COMPUTED:
3199 if (key->handle()->IsSymbol()) {
3200 Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
3201 LoadAndSpill(value);
3202 frame_->EmitPop(r0);
3203 __ mov(r2, Operand(key->handle()));
3204 __ ldr(r1, frame_->Top()); // Load the receiver.
3205 frame_->CallCodeObject(ic, RelocInfo::CODE_TARGET, 0);
3206 break;
3207 }
3208 // else fall through
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003209 case ObjectLiteral::Property::PROTOTYPE: {
ager@chromium.org5c838252010-02-19 08:53:10 +00003210 __ ldr(r0, frame_->Top());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003211 frame_->EmitPush(r0); // dup the result
3212 LoadAndSpill(key);
3213 LoadAndSpill(value);
3214 frame_->CallRuntime(Runtime::kSetProperty, 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003215 break;
3216 }
3217 case ObjectLiteral::Property::SETTER: {
ager@chromium.org5c838252010-02-19 08:53:10 +00003218 __ ldr(r0, frame_->Top());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003219 frame_->EmitPush(r0);
3220 LoadAndSpill(key);
mads.s.ager31e71382008-08-13 09:32:07 +00003221 __ mov(r0, Operand(Smi::FromInt(1)));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003222 frame_->EmitPush(r0);
3223 LoadAndSpill(value);
3224 frame_->CallRuntime(Runtime::kDefineAccessor, 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003225 break;
3226 }
3227 case ObjectLiteral::Property::GETTER: {
ager@chromium.org5c838252010-02-19 08:53:10 +00003228 __ ldr(r0, frame_->Top());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003229 frame_->EmitPush(r0);
3230 LoadAndSpill(key);
mads.s.ager31e71382008-08-13 09:32:07 +00003231 __ mov(r0, Operand(Smi::FromInt(0)));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003232 frame_->EmitPush(r0);
3233 LoadAndSpill(value);
3234 frame_->CallRuntime(Runtime::kDefineAccessor, 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003235 break;
3236 }
3237 }
3238 }
ager@chromium.orgac091b72010-05-05 07:34:42 +00003239 ASSERT_EQ(original_height + 1, frame_->height());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003240}
3241
3242
ager@chromium.org7c537e22008-10-16 08:43:32 +00003243void CodeGenerator::VisitArrayLiteral(ArrayLiteral* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003244#ifdef DEBUG
3245 int original_height = frame_->height();
3246#endif
ager@chromium.org357bf652010-04-12 11:30:10 +00003247 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003248 Comment cmnt(masm_, "[ ArrayLiteral");
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003249
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003250 // Load the function of this activation.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003251 __ ldr(r2, frame_->Function());
ager@chromium.org5c838252010-02-19 08:53:10 +00003252 // Load the literals array of the function.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003253 __ ldr(r2, FieldMemOperand(r2, JSFunction::kLiteralsOffset));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003254 __ mov(r1, Operand(Smi::FromInt(node->literal_index())));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003255 __ mov(r0, Operand(node->constant_elements()));
3256 frame_->EmitPushMultiple(3, r2.bit() | r1.bit() | r0.bit());
ager@chromium.org5c838252010-02-19 08:53:10 +00003257 int length = node->values()->length();
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003258 if (node->depth() > 1) {
3259 frame_->CallRuntime(Runtime::kCreateArrayLiteral, 3);
ager@chromium.org5c838252010-02-19 08:53:10 +00003260 } else if (length > FastCloneShallowArrayStub::kMaximumLength) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003261 frame_->CallRuntime(Runtime::kCreateArrayLiteralShallow, 3);
ager@chromium.org5c838252010-02-19 08:53:10 +00003262 } else {
3263 FastCloneShallowArrayStub stub(length);
3264 frame_->CallStub(&stub, 3);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003265 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003266 frame_->EmitPush(r0); // save the result
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003267 // r0: created object literal
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003268
3269 // Generate code to set the elements in the array that are not
3270 // literals.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003271 for (int i = 0; i < node->values()->length(); i++) {
3272 Expression* value = node->values()->at(i);
3273
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003274 // If value is a literal the property value is already set in the
3275 // boilerplate object.
3276 if (value->AsLiteral() != NULL) continue;
3277 // If value is a materialized literal the property value is already set
3278 // in the boilerplate object if it is simple.
3279 if (CompileTimeValue::IsCompileTimeValue(value)) continue;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003280
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003281 // The property must be set by generated code.
3282 LoadAndSpill(value);
3283 frame_->EmitPop(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003284
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003285 // Fetch the object literal.
3286 __ ldr(r1, frame_->Top());
3287 // Get the elements array.
3288 __ ldr(r1, FieldMemOperand(r1, JSObject::kElementsOffset));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003289
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003290 // Write to the indexed properties array.
kasperl@chromium.orge959c182009-07-27 08:59:04 +00003291 int offset = i * kPointerSize + FixedArray::kHeaderSize;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003292 __ str(r0, FieldMemOperand(r1, offset));
3293
3294 // Update the write barrier for the array address.
3295 __ mov(r3, Operand(offset));
3296 __ RecordWrite(r1, r3, r2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003297 }
ager@chromium.orgac091b72010-05-05 07:34:42 +00003298 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003299}
3300
3301
ager@chromium.org32912102009-01-16 10:38:43 +00003302void CodeGenerator::VisitCatchExtensionObject(CatchExtensionObject* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003303#ifdef DEBUG
3304 int original_height = frame_->height();
3305#endif
ager@chromium.org357bf652010-04-12 11:30:10 +00003306 VirtualFrame::SpilledScope spilled_scope(frame_);
ager@chromium.org32912102009-01-16 10:38:43 +00003307 // Call runtime routine to allocate the catch extension object and
3308 // assign the exception value to the catch variable.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003309 Comment cmnt(masm_, "[ CatchExtensionObject");
3310 LoadAndSpill(node->key());
3311 LoadAndSpill(node->value());
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00003312 frame_->CallRuntime(Runtime::kCreateCatchExtensionObject, 2);
3313 frame_->EmitPush(r0);
ager@chromium.orgac091b72010-05-05 07:34:42 +00003314 ASSERT_EQ(original_height + 1, frame_->height());
3315}
3316
3317
3318void CodeGenerator::EmitSlotAssignment(Assignment* node) {
3319#ifdef DEBUG
3320 int original_height = frame_->height();
3321#endif
3322 Comment cmnt(masm(), "[ Variable Assignment");
3323 Variable* var = node->target()->AsVariableProxy()->AsVariable();
3324 ASSERT(var != NULL);
3325 Slot* slot = var->slot();
3326 ASSERT(slot != NULL);
3327
3328 // Evaluate the right-hand side.
3329 if (node->is_compound()) {
3330 // For a compound assignment the right-hand side is a binary operation
3331 // between the current property value and the actual right-hand side.
3332 LoadFromSlotCheckForArguments(slot, NOT_INSIDE_TYPEOF);
3333
3334 // Perform the binary operation.
3335 Literal* literal = node->value()->AsLiteral();
3336 bool overwrite_value =
3337 (node->value()->AsBinaryOperation() != NULL &&
3338 node->value()->AsBinaryOperation()->ResultOverwriteAllowed());
3339 if (literal != NULL && literal->handle()->IsSmi()) {
3340 SmiOperation(node->binary_op(),
3341 literal->handle(),
3342 false,
3343 overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE);
3344 } else {
3345 Load(node->value());
3346 VirtualFrameBinaryOperation(
3347 node->binary_op(), overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE);
3348 }
3349 } else {
3350 Load(node->value());
3351 }
3352
3353 // Perform the assignment.
3354 if (var->mode() != Variable::CONST || node->op() == Token::INIT_CONST) {
3355 CodeForSourcePosition(node->position());
3356 StoreToSlot(slot,
3357 node->op() == Token::INIT_CONST ? CONST_INIT : NOT_CONST_INIT);
3358 }
3359 ASSERT_EQ(original_height + 1, frame_->height());
3360}
3361
3362
3363void CodeGenerator::EmitNamedPropertyAssignment(Assignment* node) {
3364#ifdef DEBUG
3365 int original_height = frame_->height();
3366#endif
3367 Comment cmnt(masm(), "[ Named Property Assignment");
3368 Variable* var = node->target()->AsVariableProxy()->AsVariable();
3369 Property* prop = node->target()->AsProperty();
3370 ASSERT(var == NULL || (prop == NULL && var->is_global()));
3371
3372 // Initialize name and evaluate the receiver sub-expression if necessary. If
3373 // the receiver is trivial it is not placed on the stack at this point, but
3374 // loaded whenever actually needed.
3375 Handle<String> name;
3376 bool is_trivial_receiver = false;
3377 if (var != NULL) {
3378 name = var->name();
3379 } else {
3380 Literal* lit = prop->key()->AsLiteral();
3381 ASSERT_NOT_NULL(lit);
3382 name = Handle<String>::cast(lit->handle());
3383 // Do not materialize the receiver on the frame if it is trivial.
3384 is_trivial_receiver = prop->obj()->IsTrivial();
3385 if (!is_trivial_receiver) Load(prop->obj());
3386 }
3387
3388 // Change to slow case in the beginning of an initialization block to
3389 // avoid the quadratic behavior of repeatedly adding fast properties.
3390 if (node->starts_initialization_block()) {
3391 // Initialization block consists of assignments of the form expr.x = ..., so
3392 // this will never be an assignment to a variable, so there must be a
3393 // receiver object.
3394 ASSERT_EQ(NULL, var);
3395 if (is_trivial_receiver) {
3396 Load(prop->obj());
3397 } else {
3398 frame_->Dup();
3399 }
3400 frame_->CallRuntime(Runtime::kToSlowProperties, 1);
3401 }
3402
3403 // Change to fast case at the end of an initialization block. To prepare for
3404 // that add an extra copy of the receiver to the frame, so that it can be
3405 // converted back to fast case after the assignment.
3406 if (node->ends_initialization_block() && !is_trivial_receiver) {
3407 frame_->Dup();
3408 }
3409
3410 // Stack layout:
3411 // [tos] : receiver (only materialized if non-trivial)
3412 // [tos+1] : receiver if at the end of an initialization block
3413
3414 // Evaluate the right-hand side.
3415 if (node->is_compound()) {
3416 // For a compound assignment the right-hand side is a binary operation
3417 // between the current property value and the actual right-hand side.
3418 if (is_trivial_receiver) {
3419 Load(prop->obj());
3420 } else if (var != NULL) {
3421 LoadGlobal();
3422 } else {
3423 frame_->Dup();
3424 }
3425 EmitNamedLoad(name, var != NULL);
ager@chromium.orgac091b72010-05-05 07:34:42 +00003426 frame_->EmitPush(r0);
3427
3428 // Perform the binary operation.
3429 Literal* literal = node->value()->AsLiteral();
3430 bool overwrite_value =
3431 (node->value()->AsBinaryOperation() != NULL &&
3432 node->value()->AsBinaryOperation()->ResultOverwriteAllowed());
3433 if (literal != NULL && literal->handle()->IsSmi()) {
3434 SmiOperation(node->binary_op(),
3435 literal->handle(),
3436 false,
3437 overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE);
3438 } else {
3439 Load(node->value());
3440 VirtualFrameBinaryOperation(
3441 node->binary_op(), overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE);
3442 }
3443 } else {
3444 // For non-compound assignment just load the right-hand side.
3445 Load(node->value());
3446 }
3447
3448 // Stack layout:
3449 // [tos] : value
3450 // [tos+1] : receiver (only materialized if non-trivial)
3451 // [tos+2] : receiver if at the end of an initialization block
3452
3453 // Perform the assignment. It is safe to ignore constants here.
3454 ASSERT(var == NULL || var->mode() != Variable::CONST);
3455 ASSERT_NE(Token::INIT_CONST, node->op());
3456 if (is_trivial_receiver) {
3457 // Load the receiver and swap with the value.
3458 Load(prop->obj());
3459 Register t0 = frame_->PopToRegister();
3460 Register t1 = frame_->PopToRegister(t0);
3461 frame_->EmitPush(t0);
3462 frame_->EmitPush(t1);
3463 }
3464 CodeForSourcePosition(node->position());
3465 bool is_contextual = (var != NULL);
3466 EmitNamedStore(name, is_contextual);
3467 frame_->EmitPush(r0);
3468
3469 // Change to fast case at the end of an initialization block.
3470 if (node->ends_initialization_block()) {
3471 ASSERT_EQ(NULL, var);
3472 // The argument to the runtime call is the receiver.
3473 if (is_trivial_receiver) {
3474 Load(prop->obj());
3475 } else {
3476 // A copy of the receiver is below the value of the assignment. Swap
3477 // the receiver and the value of the assignment expression.
3478 Register t0 = frame_->PopToRegister();
3479 Register t1 = frame_->PopToRegister(t0);
3480 frame_->EmitPush(t0);
3481 frame_->EmitPush(t1);
3482 }
3483 frame_->CallRuntime(Runtime::kToFastProperties, 1);
3484 }
3485
3486 // Stack layout:
3487 // [tos] : result
3488
3489 ASSERT_EQ(original_height + 1, frame_->height());
3490}
3491
3492
3493void CodeGenerator::EmitKeyedPropertyAssignment(Assignment* node) {
3494#ifdef DEBUG
3495 int original_height = frame_->height();
3496#endif
3497 Comment cmnt(masm_, "[ Keyed Property Assignment");
3498 Property* prop = node->target()->AsProperty();
3499 ASSERT_NOT_NULL(prop);
3500
3501 // Evaluate the receiver subexpression.
3502 Load(prop->obj());
3503
3504 // Change to slow case in the beginning of an initialization block to
3505 // avoid the quadratic behavior of repeatedly adding fast properties.
3506 if (node->starts_initialization_block()) {
3507 frame_->Dup();
3508 frame_->CallRuntime(Runtime::kToSlowProperties, 1);
3509 }
3510
3511 // Change to fast case at the end of an initialization block. To prepare for
3512 // that add an extra copy of the receiver to the frame, so that it can be
3513 // converted back to fast case after the assignment.
3514 if (node->ends_initialization_block()) {
3515 frame_->Dup();
3516 }
3517
3518 // Evaluate the key subexpression.
3519 Load(prop->key());
3520
3521 // Stack layout:
3522 // [tos] : key
3523 // [tos+1] : receiver
3524 // [tos+2] : receiver if at the end of an initialization block
3525
3526 // Evaluate the right-hand side.
3527 if (node->is_compound()) {
3528 // For a compound assignment the right-hand side is a binary operation
3529 // between the current property value and the actual right-hand side.
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00003530 // Duplicate receiver and key for loading the current property value.
3531 frame_->Dup2();
ager@chromium.orgac091b72010-05-05 07:34:42 +00003532 EmitKeyedLoad();
3533 frame_->EmitPush(r0);
3534
3535 // Perform the binary operation.
3536 Literal* literal = node->value()->AsLiteral();
3537 bool overwrite_value =
3538 (node->value()->AsBinaryOperation() != NULL &&
3539 node->value()->AsBinaryOperation()->ResultOverwriteAllowed());
3540 if (literal != NULL && literal->handle()->IsSmi()) {
3541 SmiOperation(node->binary_op(),
3542 literal->handle(),
3543 false,
3544 overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE);
3545 } else {
3546 Load(node->value());
3547 VirtualFrameBinaryOperation(
3548 node->binary_op(), overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE);
3549 }
3550 } else {
3551 // For non-compound assignment just load the right-hand side.
3552 Load(node->value());
3553 }
3554
3555 // Stack layout:
3556 // [tos] : value
3557 // [tos+1] : key
3558 // [tos+2] : receiver
3559 // [tos+3] : receiver if at the end of an initialization block
3560
3561 // Perform the assignment. It is safe to ignore constants here.
3562 ASSERT(node->op() != Token::INIT_CONST);
3563 CodeForSourcePosition(node->position());
ager@chromium.orgac091b72010-05-05 07:34:42 +00003564 EmitKeyedStore(prop->key()->type());
ager@chromium.orgac091b72010-05-05 07:34:42 +00003565 frame_->EmitPush(r0);
3566
3567 // Stack layout:
3568 // [tos] : result
3569 // [tos+1] : receiver if at the end of an initialization block
3570
3571 // Change to fast case at the end of an initialization block.
3572 if (node->ends_initialization_block()) {
3573 // The argument to the runtime call is the extra copy of the receiver,
3574 // which is below the value of the assignment. Swap the receiver and
3575 // the value of the assignment expression.
3576 Register t0 = frame_->PopToRegister();
3577 Register t1 = frame_->PopToRegister(t0);
3578 frame_->EmitPush(t1);
3579 frame_->EmitPush(t0);
3580 frame_->CallRuntime(Runtime::kToFastProperties, 1);
3581 }
3582
3583 // Stack layout:
3584 // [tos] : result
3585
3586 ASSERT_EQ(original_height + 1, frame_->height());
ager@chromium.org32912102009-01-16 10:38:43 +00003587}
3588
3589
ager@chromium.org7c537e22008-10-16 08:43:32 +00003590void CodeGenerator::VisitAssignment(Assignment* node) {
ager@chromium.org357bf652010-04-12 11:30:10 +00003591 VirtualFrame::RegisterAllocationScope scope(this);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003592#ifdef DEBUG
3593 int original_height = frame_->height();
3594#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003595 Comment cmnt(masm_, "[ Assignment");
mads.s.ager31e71382008-08-13 09:32:07 +00003596
ager@chromium.orgac091b72010-05-05 07:34:42 +00003597 Variable* var = node->target()->AsVariableProxy()->AsVariable();
3598 Property* prop = node->target()->AsProperty();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003599
ager@chromium.orgac091b72010-05-05 07:34:42 +00003600 if (var != NULL && !var->is_global()) {
3601 EmitSlotAssignment(node);
mads.s.ager31e71382008-08-13 09:32:07 +00003602
ager@chromium.orgac091b72010-05-05 07:34:42 +00003603 } else if ((prop != NULL && prop->key()->IsPropertyName()) ||
3604 (var != NULL && var->is_global())) {
3605 // Properties whose keys are property names and global variables are
3606 // treated as named property references. We do not need to consider
3607 // global 'this' because it is not a valid left-hand side.
3608 EmitNamedPropertyAssignment(node);
3609
3610 } else if (prop != NULL) {
3611 // Other properties (including rewritten parameters for a function that
3612 // uses arguments) are keyed property assignments.
3613 EmitKeyedPropertyAssignment(node);
3614
3615 } else {
3616 // Invalid left-hand side.
3617 Load(node->target());
3618 frame_->CallRuntime(Runtime::kThrowReferenceError, 1);
3619 // The runtime call doesn't actually return but the code generator will
3620 // still generate code and expects a certain frame height.
3621 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003622 }
ager@chromium.orgac091b72010-05-05 07:34:42 +00003623 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003624}
3625
3626
ager@chromium.org7c537e22008-10-16 08:43:32 +00003627void CodeGenerator::VisitThrow(Throw* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003628#ifdef DEBUG
3629 int original_height = frame_->height();
3630#endif
ager@chromium.org357bf652010-04-12 11:30:10 +00003631 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003632 Comment cmnt(masm_, "[ Throw");
3633
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003634 LoadAndSpill(node->exception());
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00003635 CodeForSourcePosition(node->position());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003636 frame_->CallRuntime(Runtime::kThrow, 1);
3637 frame_->EmitPush(r0);
ager@chromium.orgac091b72010-05-05 07:34:42 +00003638 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003639}
3640
3641
ager@chromium.org7c537e22008-10-16 08:43:32 +00003642void CodeGenerator::VisitProperty(Property* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003643#ifdef DEBUG
3644 int original_height = frame_->height();
3645#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003646 Comment cmnt(masm_, "[ Property");
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00003647
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003648 { Reference property(this, node);
ager@chromium.org357bf652010-04-12 11:30:10 +00003649 property.GetValue();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003650 }
ager@chromium.orgac091b72010-05-05 07:34:42 +00003651 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003652}
3653
3654
ager@chromium.org7c537e22008-10-16 08:43:32 +00003655void CodeGenerator::VisitCall(Call* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003656#ifdef DEBUG
3657 int original_height = frame_->height();
3658#endif
ager@chromium.org357bf652010-04-12 11:30:10 +00003659 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003660 Comment cmnt(masm_, "[ Call");
3661
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003662 Expression* function = node->expression();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003663 ZoneList<Expression*>* args = node->arguments();
3664
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003665 // Standard function call.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003666 // Check if the function is a variable or a property.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003667 Variable* var = function->AsVariableProxy()->AsVariable();
3668 Property* property = function->AsProperty();
3669
3670 // ------------------------------------------------------------------------
3671 // Fast-case: Use inline caching.
3672 // ---
3673 // According to ECMA-262, section 11.2.3, page 44, the function to call
3674 // must be resolved after the arguments have been evaluated. The IC code
3675 // automatically handles this by loading the arguments before the function
3676 // is resolved in cache misses (this also holds for megamorphic calls).
3677 // ------------------------------------------------------------------------
3678
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003679 if (var != NULL && var->is_possibly_eval()) {
3680 // ----------------------------------
3681 // JavaScript example: 'eval(arg)' // eval is not known to be shadowed
3682 // ----------------------------------
3683
3684 // In a call to eval, we first call %ResolvePossiblyDirectEval to
3685 // resolve the function we need to call and the receiver of the
3686 // call. Then we call the resolved function using the given
3687 // arguments.
3688 // Prepare stack for call to resolved function.
3689 LoadAndSpill(function);
3690 __ LoadRoot(r2, Heap::kUndefinedValueRootIndex);
3691 frame_->EmitPush(r2); // Slot for receiver
3692 int arg_count = args->length();
3693 for (int i = 0; i < arg_count; i++) {
3694 LoadAndSpill(args->at(i));
3695 }
3696
3697 // Prepare stack for call to ResolvePossiblyDirectEval.
3698 __ ldr(r1, MemOperand(sp, arg_count * kPointerSize + kPointerSize));
3699 frame_->EmitPush(r1);
3700 if (arg_count > 0) {
3701 __ ldr(r1, MemOperand(sp, arg_count * kPointerSize));
3702 frame_->EmitPush(r1);
3703 } else {
3704 frame_->EmitPush(r2);
3705 }
3706
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003707 // Push the receiver.
3708 __ ldr(r1, frame_->Receiver());
3709 frame_->EmitPush(r1);
3710
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003711 // Resolve the call.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003712 frame_->CallRuntime(Runtime::kResolvePossiblyDirectEval, 3);
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003713
3714 // Touch up stack with the right values for the function and the receiver.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003715 __ str(r0, MemOperand(sp, (arg_count + 1) * kPointerSize));
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003716 __ str(r1, MemOperand(sp, arg_count * kPointerSize));
3717
3718 // Call the function.
3719 CodeForSourcePosition(node->position());
3720
3721 InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003722 CallFunctionStub call_function(arg_count, in_loop, RECEIVER_MIGHT_BE_VALUE);
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003723 frame_->CallStub(&call_function, arg_count + 1);
3724
3725 __ ldr(cp, frame_->Context());
3726 // Remove the function from the stack.
3727 frame_->Drop();
3728 frame_->EmitPush(r0);
3729
3730 } else if (var != NULL && !var->is_this() && var->is_global()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003731 // ----------------------------------
3732 // JavaScript example: 'foo(1, 2, 3)' // foo is global
3733 // ----------------------------------
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003734 // Pass the global object as the receiver and let the IC stub
3735 // patch the stack to use the global proxy as 'this' in the
3736 // invoked function.
3737 LoadGlobal();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003738
3739 // Load the arguments.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003740 int arg_count = args->length();
3741 for (int i = 0; i < arg_count; i++) {
3742 LoadAndSpill(args->at(i));
3743 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003744
ager@chromium.org5c838252010-02-19 08:53:10 +00003745 // Setup the name register and call the IC initialization code.
3746 __ mov(r2, Operand(var->name()));
kasperl@chromium.org71affb52009-05-26 05:44:31 +00003747 InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP;
3748 Handle<Code> stub = ComputeCallInitialize(arg_count, in_loop);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00003749 CodeForSourcePosition(node->position());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003750 frame_->CallCodeObject(stub, RelocInfo::CODE_TARGET_CONTEXT,
3751 arg_count + 1);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00003752 __ ldr(cp, frame_->Context());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003753 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003754
3755 } else if (var != NULL && var->slot() != NULL &&
3756 var->slot()->type() == Slot::LOOKUP) {
3757 // ----------------------------------
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00003758 // JavaScript examples:
3759 //
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00003760 // with (obj) foo(1, 2, 3) // foo may be in obj.
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00003761 //
3762 // function f() {};
3763 // function g() {
3764 // eval(...);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00003765 // f(); // f could be in extension object.
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00003766 // }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003767 // ----------------------------------
3768
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00003769 // JumpTargets do not yet support merging frames so the frame must be
3770 // spilled when jumping to these targets.
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00003771 JumpTarget slow, done;
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00003772
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00003773 // Generate fast case for loading functions from slots that
3774 // correspond to local/global variables or arguments unless they
3775 // are shadowed by eval-introduced bindings.
3776 EmitDynamicLoadFromSlotFastCase(var->slot(),
3777 NOT_INSIDE_TYPEOF,
3778 &slow,
3779 &done);
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00003780
3781 slow.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003782 // Load the function
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003783 frame_->EmitPush(cp);
mads.s.ager31e71382008-08-13 09:32:07 +00003784 __ mov(r0, Operand(var->name()));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003785 frame_->EmitPush(r0);
3786 frame_->CallRuntime(Runtime::kLoadContextSlot, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003787 // r0: slot value; r1: receiver
3788
3789 // Load the receiver.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003790 frame_->EmitPush(r0); // function
3791 frame_->EmitPush(r1); // receiver
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003792
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00003793 // If fast case code has been generated, emit code to push the
3794 // function and receiver and have the slow path jump around this
3795 // code.
3796 if (done.is_linked()) {
3797 JumpTarget call;
3798 call.Jump();
3799 done.Bind();
3800 frame_->EmitPush(r0); // function
3801 LoadGlobalReceiver(r1); // receiver
3802 call.Bind();
3803 }
3804
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00003805 // Call the function. At this point, everything is spilled but the
3806 // function and receiver are in r0 and r1.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003807 CallWithArguments(args, NO_CALL_FUNCTION_FLAGS, node->position());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003808 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003809
3810 } else if (property != NULL) {
3811 // Check if the key is a literal string.
3812 Literal* literal = property->key()->AsLiteral();
3813
3814 if (literal != NULL && literal->handle()->IsSymbol()) {
3815 // ------------------------------------------------------------------
3816 // JavaScript example: 'object.foo(1, 2, 3)' or 'map["key"](1, 2, 3)'
3817 // ------------------------------------------------------------------
3818
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00003819 Handle<String> name = Handle<String>::cast(literal->handle());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003820
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00003821 if (ArgumentsMode() == LAZY_ARGUMENTS_ALLOCATION &&
3822 name->IsEqualTo(CStrVector("apply")) &&
3823 args->length() == 2 &&
3824 args->at(1)->AsVariableProxy() != NULL &&
3825 args->at(1)->AsVariableProxy()->IsArguments()) {
3826 // Use the optimized Function.prototype.apply that avoids
3827 // allocating lazily allocated arguments objects.
3828 CallApplyLazy(property->obj(),
3829 args->at(0),
3830 args->at(1)->AsVariableProxy(),
3831 node->position());
3832
3833 } else {
3834 LoadAndSpill(property->obj()); // Receiver.
3835 // Load the arguments.
3836 int arg_count = args->length();
3837 for (int i = 0; i < arg_count; i++) {
3838 LoadAndSpill(args->at(i));
3839 }
3840
3841 // Set the name register and call the IC initialization code.
3842 __ mov(r2, Operand(name));
3843 InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP;
3844 Handle<Code> stub = ComputeCallInitialize(arg_count, in_loop);
3845 CodeForSourcePosition(node->position());
3846 frame_->CallCodeObject(stub, RelocInfo::CODE_TARGET, arg_count + 1);
3847 __ ldr(cp, frame_->Context());
3848 frame_->EmitPush(r0);
3849 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003850
3851 } else {
3852 // -------------------------------------------
3853 // JavaScript example: 'array[index](1, 2, 3)'
3854 // -------------------------------------------
3855
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003856 LoadAndSpill(property->obj());
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00003857 if (!property->is_synthetic()) {
3858 // Duplicate receiver for later use.
3859 __ ldr(r0, MemOperand(sp, 0));
3860 frame_->EmitPush(r0);
3861 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003862 LoadAndSpill(property->key());
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003863 EmitKeyedLoad();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003864 // Put the function below the receiver.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003865 if (property->is_synthetic()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003866 // Use the global receiver.
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00003867 frame_->EmitPush(r0); // Function.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003868 LoadGlobalReceiver(r0);
3869 } else {
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00003870 // Switch receiver and function.
3871 frame_->EmitPop(r1); // Receiver.
3872 frame_->EmitPush(r0); // Function.
3873 frame_->EmitPush(r1); // Receiver.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003874 }
3875
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003876 // Call the function.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003877 CallWithArguments(args, RECEIVER_MIGHT_BE_VALUE, node->position());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003878 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003879 }
3880
3881 } else {
3882 // ----------------------------------
3883 // JavaScript example: 'foo(1, 2, 3)' // foo is not global
3884 // ----------------------------------
3885
3886 // Load the function.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003887 LoadAndSpill(function);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003888
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003889 // Pass the global proxy as the receiver.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003890 LoadGlobalReceiver(r0);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003891
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003892 // Call the function.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003893 CallWithArguments(args, NO_CALL_FUNCTION_FLAGS, node->position());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003894 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003895 }
ager@chromium.orgac091b72010-05-05 07:34:42 +00003896 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003897}
3898
3899
ager@chromium.org7c537e22008-10-16 08:43:32 +00003900void CodeGenerator::VisitCallNew(CallNew* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003901#ifdef DEBUG
3902 int original_height = frame_->height();
3903#endif
ager@chromium.org357bf652010-04-12 11:30:10 +00003904 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003905 Comment cmnt(masm_, "[ CallNew");
3906
3907 // According to ECMA-262, section 11.2.2, page 44, the function
3908 // expression in new calls must be evaluated before the
3909 // arguments. This is different from ordinary calls, where the
3910 // actual function to call is resolved after the arguments have been
3911 // evaluated.
3912
3913 // Compute function to call and use the global object as the
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003914 // receiver. There is no need to use the global proxy here because
3915 // it will always be replaced with a newly allocated object.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003916 LoadAndSpill(node->expression());
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003917 LoadGlobal();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003918
3919 // Push the arguments ("left-to-right") on the stack.
3920 ZoneList<Expression*>* args = node->arguments();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003921 int arg_count = args->length();
3922 for (int i = 0; i < arg_count; i++) {
3923 LoadAndSpill(args->at(i));
3924 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003925
mads.s.ager31e71382008-08-13 09:32:07 +00003926 // r0: the number of arguments.
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00003927 __ mov(r0, Operand(arg_count));
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00003928 // Load the function into r1 as per calling convention.
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00003929 __ ldr(r1, frame_->ElementAt(arg_count + 1));
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00003930
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003931 // Call the construct call builtin that handles allocation and
3932 // constructor invocation.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00003933 CodeForSourcePosition(node->position());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003934 Handle<Code> ic(Builtins::builtin(Builtins::JSConstructCall));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003935 frame_->CallCodeObject(ic, RelocInfo::CONSTRUCT_CALL, arg_count + 1);
mads.s.ager31e71382008-08-13 09:32:07 +00003936
3937 // Discard old TOS value and push r0 on the stack (same as Pop(), push(r0)).
ager@chromium.org3bf7b912008-11-17 09:09:45 +00003938 __ str(r0, frame_->Top());
ager@chromium.orgac091b72010-05-05 07:34:42 +00003939 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003940}
3941
3942
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00003943void CodeGenerator::GenerateClassOf(ZoneList<Expression*>* args) {
ager@chromium.org357bf652010-04-12 11:30:10 +00003944 VirtualFrame::SpilledScope spilled_scope(frame_);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00003945 ASSERT(args->length() == 1);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00003946 JumpTarget leave, null, function, non_function_constructor;
3947
3948 // Load the object into r0.
3949 LoadAndSpill(args->at(0));
3950 frame_->EmitPop(r0);
3951
3952 // If the object is a smi, we return null.
3953 __ tst(r0, Operand(kSmiTagMask));
3954 null.Branch(eq);
3955
3956 // Check that the object is a JS object but take special care of JS
3957 // functions to make sure they have 'Function' as their class.
3958 __ CompareObjectType(r0, r0, r1, FIRST_JS_OBJECT_TYPE);
3959 null.Branch(lt);
3960
3961 // As long as JS_FUNCTION_TYPE is the last instance type and it is
3962 // right after LAST_JS_OBJECT_TYPE, we can avoid checking for
3963 // LAST_JS_OBJECT_TYPE.
3964 ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
3965 ASSERT(JS_FUNCTION_TYPE == LAST_JS_OBJECT_TYPE + 1);
3966 __ cmp(r1, Operand(JS_FUNCTION_TYPE));
3967 function.Branch(eq);
3968
3969 // Check if the constructor in the map is a function.
3970 __ ldr(r0, FieldMemOperand(r0, Map::kConstructorOffset));
3971 __ CompareObjectType(r0, r1, r1, JS_FUNCTION_TYPE);
3972 non_function_constructor.Branch(ne);
3973
3974 // The r0 register now contains the constructor function. Grab the
3975 // instance class name from there.
3976 __ ldr(r0, FieldMemOperand(r0, JSFunction::kSharedFunctionInfoOffset));
3977 __ ldr(r0, FieldMemOperand(r0, SharedFunctionInfo::kInstanceClassNameOffset));
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00003978 frame_->EmitPush(r0);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00003979 leave.Jump();
3980
3981 // Functions have class 'Function'.
3982 function.Bind();
3983 __ mov(r0, Operand(Factory::function_class_symbol()));
3984 frame_->EmitPush(r0);
3985 leave.Jump();
3986
3987 // Objects with a non-function constructor have class 'Object'.
3988 non_function_constructor.Bind();
3989 __ mov(r0, Operand(Factory::Object_symbol()));
3990 frame_->EmitPush(r0);
3991 leave.Jump();
3992
3993 // Non-JS objects have class null.
3994 null.Bind();
ager@chromium.orgab99eea2009-08-25 07:05:41 +00003995 __ LoadRoot(r0, Heap::kNullValueRootIndex);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00003996 frame_->EmitPush(r0);
3997
3998 // All done.
3999 leave.Bind();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004000}
4001
4002
ager@chromium.org7c537e22008-10-16 08:43:32 +00004003void CodeGenerator::GenerateValueOf(ZoneList<Expression*>* args) {
ager@chromium.org357bf652010-04-12 11:30:10 +00004004 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004005 ASSERT(args->length() == 1);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004006 JumpTarget leave;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004007 LoadAndSpill(args->at(0));
4008 frame_->EmitPop(r0); // r0 contains object.
mads.s.ager31e71382008-08-13 09:32:07 +00004009 // if (object->IsSmi()) return the object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004010 __ tst(r0, Operand(kSmiTagMask));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004011 leave.Branch(eq);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00004012 // It is a heap object - get map. If (!object->IsJSValue()) return the object.
4013 __ CompareObjectType(r0, r1, r1, JS_VALUE_TYPE);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004014 leave.Branch(ne);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004015 // Load the value.
4016 __ ldr(r0, FieldMemOperand(r0, JSValue::kValueOffset));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004017 leave.Bind();
4018 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004019}
4020
4021
ager@chromium.org7c537e22008-10-16 08:43:32 +00004022void CodeGenerator::GenerateSetValueOf(ZoneList<Expression*>* args) {
ager@chromium.org357bf652010-04-12 11:30:10 +00004023 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004024 ASSERT(args->length() == 2);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004025 JumpTarget leave;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004026 LoadAndSpill(args->at(0)); // Load the object.
4027 LoadAndSpill(args->at(1)); // Load the value.
4028 frame_->EmitPop(r0); // r0 contains value
4029 frame_->EmitPop(r1); // r1 contains object
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004030 // if (object->IsSmi()) return object.
4031 __ tst(r1, Operand(kSmiTagMask));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004032 leave.Branch(eq);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00004033 // It is a heap object - get map. If (!object->IsJSValue()) return the object.
4034 __ CompareObjectType(r1, r2, r2, JS_VALUE_TYPE);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004035 leave.Branch(ne);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004036 // Store the value.
4037 __ str(r0, FieldMemOperand(r1, JSValue::kValueOffset));
4038 // Update the write barrier.
4039 __ mov(r2, Operand(JSValue::kValueOffset - kHeapObjectTag));
4040 __ RecordWrite(r1, r2, r3);
4041 // Leave.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004042 leave.Bind();
4043 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004044}
4045
4046
ager@chromium.org7c537e22008-10-16 08:43:32 +00004047void CodeGenerator::GenerateIsSmi(ZoneList<Expression*>* args) {
ager@chromium.org357bf652010-04-12 11:30:10 +00004048 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004049 ASSERT(args->length() == 1);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004050 LoadAndSpill(args->at(0));
4051 frame_->EmitPop(r0);
mads.s.ager31e71382008-08-13 09:32:07 +00004052 __ tst(r0, Operand(kSmiTagMask));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004053 cc_reg_ = eq;
4054}
4055
4056
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004057void CodeGenerator::GenerateLog(ZoneList<Expression*>* args) {
ager@chromium.org357bf652010-04-12 11:30:10 +00004058 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004059 // See comment in CodeGenerator::GenerateLog in codegen-ia32.cc.
4060 ASSERT_EQ(args->length(), 3);
christian.plesner.hansen@gmail.comaca49682009-01-07 14:29:04 +00004061#ifdef ENABLE_LOGGING_AND_PROFILING
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004062 if (ShouldGenerateLog(args->at(0))) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004063 LoadAndSpill(args->at(1));
4064 LoadAndSpill(args->at(2));
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004065 __ CallRuntime(Runtime::kLog, 2);
4066 }
christian.plesner.hansen@gmail.comaca49682009-01-07 14:29:04 +00004067#endif
ager@chromium.orgab99eea2009-08-25 07:05:41 +00004068 __ LoadRoot(r0, Heap::kUndefinedValueRootIndex);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004069 frame_->EmitPush(r0);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004070}
4071
4072
ager@chromium.org7c537e22008-10-16 08:43:32 +00004073void CodeGenerator::GenerateIsNonNegativeSmi(ZoneList<Expression*>* args) {
ager@chromium.org357bf652010-04-12 11:30:10 +00004074 VirtualFrame::SpilledScope spilled_scope(frame_);
ager@chromium.orgc27e4e72008-09-04 13:52:27 +00004075 ASSERT(args->length() == 1);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004076 LoadAndSpill(args->at(0));
4077 frame_->EmitPop(r0);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00004078 __ tst(r0, Operand(kSmiTagMask | 0x80000000u));
ager@chromium.orgc27e4e72008-09-04 13:52:27 +00004079 cc_reg_ = eq;
4080}
4081
4082
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004083// Generates the Math.pow method - currently just calls runtime.
4084void CodeGenerator::GenerateMathPow(ZoneList<Expression*>* args) {
4085 ASSERT(args->length() == 2);
4086 Load(args->at(0));
4087 Load(args->at(1));
4088 frame_->CallRuntime(Runtime::kMath_pow, 2);
4089 frame_->EmitPush(r0);
4090}
4091
4092
4093// Generates the Math.sqrt method - currently just calls runtime.
4094void CodeGenerator::GenerateMathSqrt(ZoneList<Expression*>* args) {
4095 ASSERT(args->length() == 1);
4096 Load(args->at(0));
4097 frame_->CallRuntime(Runtime::kMath_sqrt, 1);
4098 frame_->EmitPush(r0);
4099}
4100
4101
ager@chromium.orgac091b72010-05-05 07:34:42 +00004102// This generates code that performs a charCodeAt() call or returns
kasper.lund7276f142008-07-30 08:49:36 +00004103// undefined in order to trigger the slow case, Runtime_StringCharCodeAt.
ager@chromium.orgac091b72010-05-05 07:34:42 +00004104// It can handle flat, 8 and 16 bit characters and cons strings where the
4105// answer is found in the left hand branch of the cons. The slow case will
4106// flatten the string, which will ensure that the answer is in the left hand
4107// side the next time around.
ager@chromium.org7c537e22008-10-16 08:43:32 +00004108void CodeGenerator::GenerateFastCharCodeAt(ZoneList<Expression*>* args) {
ager@chromium.org357bf652010-04-12 11:30:10 +00004109 VirtualFrame::SpilledScope spilled_scope(frame_);
kasper.lund7276f142008-07-30 08:49:36 +00004110 ASSERT(args->length() == 2);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004111 Comment(masm_, "[ GenerateFastCharCodeAt");
4112
4113 LoadAndSpill(args->at(0));
4114 LoadAndSpill(args->at(1));
ager@chromium.orgac091b72010-05-05 07:34:42 +00004115 frame_->EmitPop(r1); // Index.
4116 frame_->EmitPop(r2); // String.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004117
ager@chromium.orgac091b72010-05-05 07:34:42 +00004118 Label slow_case;
4119 Label exit;
4120 StringHelper::GenerateFastCharCodeAt(masm_,
4121 r2,
4122 r1,
4123 r3,
4124 r0,
4125 &slow_case,
4126 &slow_case,
4127 &slow_case,
4128 &slow_case);
4129 __ jmp(&exit);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004130
ager@chromium.orgac091b72010-05-05 07:34:42 +00004131 __ bind(&slow_case);
4132 // Move the undefined value into the result register, which will
4133 // trigger the slow case.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00004134 __ LoadRoot(r0, Heap::kUndefinedValueRootIndex);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004135
ager@chromium.orgac091b72010-05-05 07:34:42 +00004136 __ bind(&exit);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004137 frame_->EmitPush(r0);
kasper.lund7276f142008-07-30 08:49:36 +00004138}
4139
4140
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004141void CodeGenerator::GenerateCharFromCode(ZoneList<Expression*>* args) {
4142 Comment(masm_, "[ GenerateCharFromCode");
4143 ASSERT(args->length() == 1);
4144
ager@chromium.orgac091b72010-05-05 07:34:42 +00004145 Register code = r1;
4146 Register scratch = ip;
4147 Register result = r0;
4148
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004149 LoadAndSpill(args->at(0));
ager@chromium.orgac091b72010-05-05 07:34:42 +00004150 frame_->EmitPop(code);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004151
ager@chromium.orgac091b72010-05-05 07:34:42 +00004152 StringHelper::GenerateCharFromCode(masm_,
4153 code,
4154 scratch,
4155 result,
4156 CALL_FUNCTION);
4157 frame_->EmitPush(result);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004158}
4159
4160
ager@chromium.org7c537e22008-10-16 08:43:32 +00004161void CodeGenerator::GenerateIsArray(ZoneList<Expression*>* args) {
ager@chromium.org357bf652010-04-12 11:30:10 +00004162 VirtualFrame::SpilledScope spilled_scope(frame_);
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00004163 ASSERT(args->length() == 1);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004164 LoadAndSpill(args->at(0));
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004165 JumpTarget answer;
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00004166 // We need the CC bits to come out as not_equal in the case where the
4167 // object is a smi. This can't be done with the usual test opcode so
4168 // we use XOR to get the right CC bits.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004169 frame_->EmitPop(r0);
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00004170 __ and_(r1, r0, Operand(kSmiTagMask));
4171 __ eor(r1, r1, Operand(kSmiTagMask), SetCC);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004172 answer.Branch(ne);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00004173 // It is a heap object - get the map. Check if the object is a JS array.
4174 __ CompareObjectType(r0, r1, r1, JS_ARRAY_TYPE);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004175 answer.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004176 cc_reg_ = eq;
4177}
4178
4179
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00004180void CodeGenerator::GenerateIsRegExp(ZoneList<Expression*>* args) {
ager@chromium.org357bf652010-04-12 11:30:10 +00004181 VirtualFrame::SpilledScope spilled_scope(frame_);
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00004182 ASSERT(args->length() == 1);
4183 LoadAndSpill(args->at(0));
4184 JumpTarget answer;
4185 // We need the CC bits to come out as not_equal in the case where the
4186 // object is a smi. This can't be done with the usual test opcode so
4187 // we use XOR to get the right CC bits.
4188 frame_->EmitPop(r0);
4189 __ and_(r1, r0, Operand(kSmiTagMask));
4190 __ eor(r1, r1, Operand(kSmiTagMask), SetCC);
4191 answer.Branch(ne);
4192 // It is a heap object - get the map. Check if the object is a regexp.
4193 __ CompareObjectType(r0, r1, r1, JS_REGEXP_TYPE);
4194 answer.Bind();
4195 cc_reg_ = eq;
4196}
4197
4198
ager@chromium.org6141cbe2009-11-20 12:14:52 +00004199void CodeGenerator::GenerateIsObject(ZoneList<Expression*>* args) {
4200 // This generates a fast version of:
4201 // (typeof(arg) === 'object' || %_ClassOf(arg) == 'RegExp')
ager@chromium.org357bf652010-04-12 11:30:10 +00004202 VirtualFrame::SpilledScope spilled_scope(frame_);
ager@chromium.org6141cbe2009-11-20 12:14:52 +00004203 ASSERT(args->length() == 1);
4204 LoadAndSpill(args->at(0));
4205 frame_->EmitPop(r1);
4206 __ tst(r1, Operand(kSmiTagMask));
4207 false_target()->Branch(eq);
4208
4209 __ LoadRoot(ip, Heap::kNullValueRootIndex);
4210 __ cmp(r1, ip);
4211 true_target()->Branch(eq);
4212
4213 Register map_reg = r2;
4214 __ ldr(map_reg, FieldMemOperand(r1, HeapObject::kMapOffset));
4215 // Undetectable objects behave like undefined when tested with typeof.
4216 __ ldrb(r1, FieldMemOperand(map_reg, Map::kBitFieldOffset));
4217 __ and_(r1, r1, Operand(1 << Map::kIsUndetectable));
4218 __ cmp(r1, Operand(1 << Map::kIsUndetectable));
4219 false_target()->Branch(eq);
4220
4221 __ ldrb(r1, FieldMemOperand(map_reg, Map::kInstanceTypeOffset));
4222 __ cmp(r1, Operand(FIRST_JS_OBJECT_TYPE));
4223 false_target()->Branch(lt);
4224 __ cmp(r1, Operand(LAST_JS_OBJECT_TYPE));
4225 cc_reg_ = le;
4226}
4227
4228
4229void CodeGenerator::GenerateIsFunction(ZoneList<Expression*>* args) {
4230 // This generates a fast version of:
4231 // (%_ClassOf(arg) === 'Function')
ager@chromium.org357bf652010-04-12 11:30:10 +00004232 VirtualFrame::SpilledScope spilled_scope(frame_);
ager@chromium.org6141cbe2009-11-20 12:14:52 +00004233 ASSERT(args->length() == 1);
4234 LoadAndSpill(args->at(0));
4235 frame_->EmitPop(r0);
4236 __ tst(r0, Operand(kSmiTagMask));
4237 false_target()->Branch(eq);
4238 Register map_reg = r2;
4239 __ CompareObjectType(r0, map_reg, r1, JS_FUNCTION_TYPE);
4240 cc_reg_ = eq;
4241}
4242
4243
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004244void CodeGenerator::GenerateIsUndetectableObject(ZoneList<Expression*>* args) {
ager@chromium.org357bf652010-04-12 11:30:10 +00004245 VirtualFrame::SpilledScope spilled_scope(frame_);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004246 ASSERT(args->length() == 1);
4247 LoadAndSpill(args->at(0));
4248 frame_->EmitPop(r0);
4249 __ tst(r0, Operand(kSmiTagMask));
4250 false_target()->Branch(eq);
4251 __ ldr(r1, FieldMemOperand(r0, HeapObject::kMapOffset));
4252 __ ldrb(r1, FieldMemOperand(r1, Map::kBitFieldOffset));
4253 __ tst(r1, Operand(1 << Map::kIsUndetectable));
4254 cc_reg_ = ne;
4255}
4256
4257
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004258void CodeGenerator::GenerateIsConstructCall(ZoneList<Expression*>* args) {
ager@chromium.org357bf652010-04-12 11:30:10 +00004259 VirtualFrame::SpilledScope spilled_scope(frame_);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004260 ASSERT(args->length() == 0);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00004261
4262 // Get the frame pointer for the calling frame.
4263 __ ldr(r2, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
4264
4265 // Skip the arguments adaptor frame if it exists.
4266 Label check_frame_marker;
4267 __ ldr(r1, MemOperand(r2, StandardFrameConstants::kContextOffset));
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004268 __ cmp(r1, Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00004269 __ b(ne, &check_frame_marker);
4270 __ ldr(r2, MemOperand(r2, StandardFrameConstants::kCallerFPOffset));
4271
4272 // Check the marker in the calling frame.
4273 __ bind(&check_frame_marker);
4274 __ ldr(r1, MemOperand(r2, StandardFrameConstants::kMarkerOffset));
4275 __ cmp(r1, Operand(Smi::FromInt(StackFrame::CONSTRUCT)));
4276 cc_reg_ = eq;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004277}
4278
4279
ager@chromium.org7c537e22008-10-16 08:43:32 +00004280void CodeGenerator::GenerateArgumentsLength(ZoneList<Expression*>* args) {
ager@chromium.org357bf652010-04-12 11:30:10 +00004281 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004282 ASSERT(args->length() == 0);
4283
lrn@chromium.org25156de2010-04-06 13:10:27 +00004284 Label exit;
4285
4286 // Get the number of formal parameters.
ager@chromium.org5c838252010-02-19 08:53:10 +00004287 __ mov(r0, Operand(Smi::FromInt(scope()->num_parameters())));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004288
lrn@chromium.org25156de2010-04-06 13:10:27 +00004289 // Check if the calling frame is an arguments adaptor frame.
4290 __ ldr(r2, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
4291 __ ldr(r3, MemOperand(r2, StandardFrameConstants::kContextOffset));
4292 __ cmp(r3, Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
4293 __ b(ne, &exit);
4294
4295 // Arguments adaptor case: Read the arguments length from the
4296 // adaptor frame.
4297 __ ldr(r0, MemOperand(r2, ArgumentsAdaptorFrameConstants::kLengthOffset));
4298
4299 __ bind(&exit);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004300 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004301}
4302
4303
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00004304void CodeGenerator::GenerateArguments(ZoneList<Expression*>* args) {
ager@chromium.org357bf652010-04-12 11:30:10 +00004305 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004306 ASSERT(args->length() == 1);
4307
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00004308 // Satisfy contract with ArgumentsAccessStub:
4309 // Load the key into r1 and the formal parameters count into r0.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004310 LoadAndSpill(args->at(0));
4311 frame_->EmitPop(r1);
ager@chromium.org5c838252010-02-19 08:53:10 +00004312 __ mov(r0, Operand(Smi::FromInt(scope()->num_parameters())));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004313
4314 // Call the shared stub to get to arguments[key].
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004315 ArgumentsAccessStub stub(ArgumentsAccessStub::READ_ELEMENT);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004316 frame_->CallStub(&stub, 0);
4317 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004318}
4319
4320
ager@chromium.org357bf652010-04-12 11:30:10 +00004321void CodeGenerator::GenerateRandomHeapNumber(
4322 ZoneList<Expression*>* args) {
4323 VirtualFrame::SpilledScope spilled_scope(frame_);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00004324 ASSERT(args->length() == 0);
ager@chromium.org357bf652010-04-12 11:30:10 +00004325
4326 Label slow_allocate_heapnumber;
4327 Label heapnumber_allocated;
4328
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00004329 __ AllocateHeapNumber(r4, r1, r2, &slow_allocate_heapnumber);
ager@chromium.org357bf652010-04-12 11:30:10 +00004330 __ jmp(&heapnumber_allocated);
4331
4332 __ bind(&slow_allocate_heapnumber);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00004333 // To allocate a heap number, and ensure that it is not a smi, we
4334 // call the runtime function FUnaryMinus on 0, returning the double
4335 // -0.0. A new, distinct heap number is returned each time.
ager@chromium.org357bf652010-04-12 11:30:10 +00004336 __ mov(r0, Operand(Smi::FromInt(0)));
4337 __ push(r0);
4338 __ CallRuntime(Runtime::kNumberUnaryMinus, 1);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00004339 __ mov(r4, Operand(r0));
ager@chromium.org357bf652010-04-12 11:30:10 +00004340
4341 __ bind(&heapnumber_allocated);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00004342
4343 // Convert 32 random bits in r0 to 0.(32 random bits) in a double
4344 // by computing:
4345 // ( 1.(20 0s)(32 random bits) x 2^20 ) - (1.0 x 2^20)).
4346 if (CpuFeatures::IsSupported(VFP3)) {
4347 __ PrepareCallCFunction(0, r1);
4348 __ CallCFunction(ExternalReference::random_uint32_function(), 0);
4349
4350 CpuFeatures::Scope scope(VFP3);
4351 // 0x41300000 is the top half of 1.0 x 2^20 as a double.
4352 // Create this constant using mov/orr to avoid PC relative load.
4353 __ mov(r1, Operand(0x41000000));
4354 __ orr(r1, r1, Operand(0x300000));
4355 // Move 0x41300000xxxxxxxx (x = random bits) to VFP.
4356 __ vmov(d7, r0, r1);
4357 // Move 0x4130000000000000 to VFP.
4358 __ mov(r0, Operand(0));
4359 __ vmov(d8, r0, r1);
4360 // Subtract and store the result in the heap number.
4361 __ vsub(d7, d7, d8);
4362 __ sub(r0, r4, Operand(kHeapObjectTag));
4363 __ vstr(d7, r0, HeapNumber::kValueOffset);
4364 frame_->EmitPush(r4);
4365 } else {
4366 __ mov(r0, Operand(r4));
4367 __ PrepareCallCFunction(1, r1);
4368 __ CallCFunction(
4369 ExternalReference::fill_heap_number_with_random_function(), 1);
4370 frame_->EmitPush(r0);
4371 }
ager@chromium.orgeadaf222009-06-16 09:43:10 +00004372}
4373
4374
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00004375void CodeGenerator::GenerateStringAdd(ZoneList<Expression*>* args) {
4376 ASSERT_EQ(2, args->length());
4377
4378 Load(args->at(0));
4379 Load(args->at(1));
4380
ager@chromium.org5c838252010-02-19 08:53:10 +00004381 StringAddStub stub(NO_STRING_ADD_FLAGS);
4382 frame_->CallStub(&stub, 2);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00004383 frame_->EmitPush(r0);
4384}
4385
4386
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004387void CodeGenerator::GenerateSubString(ZoneList<Expression*>* args) {
4388 ASSERT_EQ(3, args->length());
4389
4390 Load(args->at(0));
4391 Load(args->at(1));
4392 Load(args->at(2));
4393
ager@chromium.org5c838252010-02-19 08:53:10 +00004394 SubStringStub stub;
4395 frame_->CallStub(&stub, 3);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004396 frame_->EmitPush(r0);
4397}
4398
4399
4400void CodeGenerator::GenerateStringCompare(ZoneList<Expression*>* args) {
4401 ASSERT_EQ(2, args->length());
4402
4403 Load(args->at(0));
4404 Load(args->at(1));
4405
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004406 StringCompareStub stub;
4407 frame_->CallStub(&stub, 2);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004408 frame_->EmitPush(r0);
4409}
4410
4411
4412void CodeGenerator::GenerateRegExpExec(ZoneList<Expression*>* args) {
4413 ASSERT_EQ(4, args->length());
4414
4415 Load(args->at(0));
4416 Load(args->at(1));
4417 Load(args->at(2));
4418 Load(args->at(3));
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00004419 RegExpExecStub stub;
4420 frame_->CallStub(&stub, 4);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004421 frame_->EmitPush(r0);
4422}
4423
4424
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00004425void CodeGenerator::GenerateRegExpConstructResult(ZoneList<Expression*>* args) {
4426 // No stub. This code only occurs a few times in regexp.js.
4427 const int kMaxInlineLength = 100;
4428 ASSERT_EQ(3, args->length());
4429 Load(args->at(0)); // Size of array, smi.
4430 Load(args->at(1)); // "index" property value.
4431 Load(args->at(2)); // "input" property value.
4432 {
4433 VirtualFrame::SpilledScope spilled_scope(frame_);
4434 Label slowcase;
4435 Label done;
4436 __ ldr(r1, MemOperand(sp, kPointerSize * 2));
4437 STATIC_ASSERT(kSmiTag == 0);
4438 STATIC_ASSERT(kSmiTagSize == 1);
4439 __ tst(r1, Operand(kSmiTagMask));
4440 __ b(ne, &slowcase);
4441 __ cmp(r1, Operand(Smi::FromInt(kMaxInlineLength)));
4442 __ b(hi, &slowcase);
4443 // Smi-tagging is equivalent to multiplying by 2.
4444 // Allocate RegExpResult followed by FixedArray with size in ebx.
4445 // JSArray: [Map][empty properties][Elements][Length-smi][index][input]
4446 // Elements: [Map][Length][..elements..]
4447 // Size of JSArray with two in-object properties and the header of a
4448 // FixedArray.
4449 int objects_size =
4450 (JSRegExpResult::kSize + FixedArray::kHeaderSize) / kPointerSize;
4451 __ mov(r5, Operand(r1, LSR, kSmiTagSize + kSmiShiftSize));
4452 __ add(r2, r5, Operand(objects_size));
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00004453 __ AllocateInNewSpace(
4454 r2, // In: Size, in words.
4455 r0, // Out: Start of allocation (tagged).
4456 r3, // Scratch register.
4457 r4, // Scratch register.
4458 &slowcase,
4459 static_cast<AllocationFlags>(TAG_OBJECT | SIZE_IN_WORDS));
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00004460 // r0: Start of allocated area, object-tagged.
4461 // r1: Number of elements in array, as smi.
4462 // r5: Number of elements, untagged.
4463
4464 // Set JSArray map to global.regexp_result_map().
4465 // Set empty properties FixedArray.
4466 // Set elements to point to FixedArray allocated right after the JSArray.
4467 // Interleave operations for better latency.
4468 __ ldr(r2, ContextOperand(cp, Context::GLOBAL_INDEX));
4469 __ add(r3, r0, Operand(JSRegExpResult::kSize));
4470 __ mov(r4, Operand(Factory::empty_fixed_array()));
4471 __ ldr(r2, FieldMemOperand(r2, GlobalObject::kGlobalContextOffset));
4472 __ str(r3, FieldMemOperand(r0, JSObject::kElementsOffset));
4473 __ ldr(r2, ContextOperand(r2, Context::REGEXP_RESULT_MAP_INDEX));
4474 __ str(r4, FieldMemOperand(r0, JSObject::kPropertiesOffset));
4475 __ str(r2, FieldMemOperand(r0, HeapObject::kMapOffset));
4476
4477 // Set input, index and length fields from arguments.
4478 __ ldm(ia_w, sp, static_cast<RegList>(r2.bit() | r4.bit()));
4479 __ str(r1, FieldMemOperand(r0, JSArray::kLengthOffset));
4480 __ add(sp, sp, Operand(kPointerSize));
4481 __ str(r4, FieldMemOperand(r0, JSRegExpResult::kIndexOffset));
4482 __ str(r2, FieldMemOperand(r0, JSRegExpResult::kInputOffset));
4483
4484 // Fill out the elements FixedArray.
4485 // r0: JSArray, tagged.
4486 // r3: FixedArray, tagged.
4487 // r5: Number of elements in array, untagged.
4488
4489 // Set map.
4490 __ mov(r2, Operand(Factory::fixed_array_map()));
4491 __ str(r2, FieldMemOperand(r3, HeapObject::kMapOffset));
4492 // Set FixedArray length.
4493 __ str(r5, FieldMemOperand(r3, FixedArray::kLengthOffset));
4494 // Fill contents of fixed-array with the-hole.
4495 __ mov(r2, Operand(Factory::the_hole_value()));
4496 __ add(r3, r3, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
4497 // Fill fixed array elements with hole.
4498 // r0: JSArray, tagged.
4499 // r2: the hole.
4500 // r3: Start of elements in FixedArray.
4501 // r5: Number of elements to fill.
4502 Label loop;
4503 __ tst(r5, Operand(r5));
4504 __ bind(&loop);
4505 __ b(le, &done); // Jump if r1 is negative or zero.
4506 __ sub(r5, r5, Operand(1), SetCC);
4507 __ str(r2, MemOperand(r3, r5, LSL, kPointerSizeLog2));
4508 __ jmp(&loop);
4509
4510 __ bind(&slowcase);
4511 __ CallRuntime(Runtime::kRegExpConstructResult, 3);
4512
4513 __ bind(&done);
4514 }
4515 frame_->Forget(3);
4516 frame_->EmitPush(r0);
4517}
4518
4519
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00004520class DeferredSearchCache: public DeferredCode {
4521 public:
4522 DeferredSearchCache(Register dst, Register cache, Register key)
4523 : dst_(dst), cache_(cache), key_(key) {
4524 set_comment("[ DeferredSearchCache");
4525 }
4526
4527 virtual void Generate();
4528
4529 private:
4530 Register dst_, cache_, key_;
4531};
4532
4533
4534void DeferredSearchCache::Generate() {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00004535 __ Push(cache_, key_);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00004536 __ CallRuntime(Runtime::kGetFromCache, 2);
4537 if (!dst_.is(r0)) {
4538 __ mov(dst_, r0);
4539 }
4540}
4541
4542
4543void CodeGenerator::GenerateGetFromCache(ZoneList<Expression*>* args) {
4544 ASSERT_EQ(2, args->length());
4545
4546 ASSERT_NE(NULL, args->at(0)->AsLiteral());
4547 int cache_id = Smi::cast(*(args->at(0)->AsLiteral()->handle()))->value();
4548
4549 Handle<FixedArray> jsfunction_result_caches(
4550 Top::global_context()->jsfunction_result_caches());
4551 if (jsfunction_result_caches->length() <= cache_id) {
4552 __ Abort("Attempt to use undefined cache.");
4553 __ LoadRoot(r0, Heap::kUndefinedValueRootIndex);
4554 frame_->EmitPush(r0);
4555 return;
4556 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00004557
4558 Load(args->at(1));
4559 frame_->EmitPop(r2);
4560
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00004561 __ ldr(r1, ContextOperand(cp, Context::GLOBAL_INDEX));
4562 __ ldr(r1, FieldMemOperand(r1, GlobalObject::kGlobalContextOffset));
4563 __ ldr(r1, ContextOperand(r1, Context::JSFUNCTION_RESULT_CACHES_INDEX));
4564 __ ldr(r1, FieldMemOperand(r1, FixedArray::OffsetOfElementAt(cache_id)));
4565
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00004566 DeferredSearchCache* deferred = new DeferredSearchCache(r0, r1, r2);
4567
4568 const int kFingerOffset =
4569 FixedArray::OffsetOfElementAt(JSFunctionResultCache::kFingerIndex);
4570 ASSERT(kSmiTag == 0 && kSmiTagSize == 1);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00004571 __ ldr(r0, FieldMemOperand(r1, kFingerOffset));
4572 // r0 now holds finger offset as a smi.
4573 __ add(r3, r1, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
4574 // r3 now points to the start of fixed array elements.
4575 __ ldr(r0, MemOperand(r3, r0, LSL, kPointerSizeLog2 - kSmiTagSize, PreIndex));
4576 // Note side effect of PreIndex: r3 now points to the key of the pair.
4577 __ cmp(r2, r0);
4578 deferred->Branch(ne);
4579
4580 __ ldr(r0, MemOperand(r3, kPointerSize));
4581
4582 deferred->BindExit();
4583 frame_->EmitPush(r0);
4584}
4585
4586
ager@chromium.org5c838252010-02-19 08:53:10 +00004587void CodeGenerator::GenerateNumberToString(ZoneList<Expression*>* args) {
4588 ASSERT_EQ(args->length(), 1);
4589
4590 // Load the argument on the stack and jump to the runtime.
4591 Load(args->at(0));
4592
fschneider@chromium.org086aac62010-03-17 13:18:24 +00004593 NumberToStringStub stub;
4594 frame_->CallStub(&stub, 1);
ager@chromium.org5c838252010-02-19 08:53:10 +00004595 frame_->EmitPush(r0);
4596}
4597
4598
ager@chromium.orgac091b72010-05-05 07:34:42 +00004599class DeferredSwapElements: public DeferredCode {
4600 public:
4601 DeferredSwapElements(Register object, Register index1, Register index2)
4602 : object_(object), index1_(index1), index2_(index2) {
4603 set_comment("[ DeferredSwapElements");
4604 }
4605
4606 virtual void Generate();
4607
4608 private:
4609 Register object_, index1_, index2_;
4610};
4611
4612
4613void DeferredSwapElements::Generate() {
4614 __ push(object_);
4615 __ push(index1_);
4616 __ push(index2_);
4617 __ CallRuntime(Runtime::kSwapElements, 3);
4618}
4619
4620
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00004621void CodeGenerator::GenerateSwapElements(ZoneList<Expression*>* args) {
4622 Comment cmnt(masm_, "[ GenerateSwapElements");
4623
4624 ASSERT_EQ(3, args->length());
4625
4626 Load(args->at(0));
4627 Load(args->at(1));
4628 Load(args->at(2));
4629
ager@chromium.orgac091b72010-05-05 07:34:42 +00004630 Register index2 = r2;
4631 Register index1 = r1;
4632 Register object = r0;
4633 Register tmp1 = r3;
4634 Register tmp2 = r4;
4635
4636 frame_->EmitPop(index2);
4637 frame_->EmitPop(index1);
4638 frame_->EmitPop(object);
4639
4640 DeferredSwapElements* deferred =
4641 new DeferredSwapElements(object, index1, index2);
4642
4643 // Fetch the map and check if array is in fast case.
4644 // Check that object doesn't require security checks and
4645 // has no indexed interceptor.
4646 __ CompareObjectType(object, tmp1, tmp2, FIRST_JS_OBJECT_TYPE);
4647 deferred->Branch(lt);
4648 __ ldrb(tmp2, FieldMemOperand(tmp1, Map::kBitFieldOffset));
4649 __ tst(tmp2, Operand(KeyedLoadIC::kSlowCaseBitFieldMask));
4650 deferred->Branch(nz);
4651
4652 // Check the object's elements are in fast case.
4653 __ ldr(tmp1, FieldMemOperand(object, JSObject::kElementsOffset));
4654 __ ldr(tmp2, FieldMemOperand(tmp1, HeapObject::kMapOffset));
4655 __ LoadRoot(ip, Heap::kFixedArrayMapRootIndex);
4656 __ cmp(tmp2, ip);
4657 deferred->Branch(ne);
4658
4659 // Smi-tagging is equivalent to multiplying by 2.
4660 STATIC_ASSERT(kSmiTag == 0);
4661 STATIC_ASSERT(kSmiTagSize == 1);
4662
4663 // Check that both indices are smis.
4664 __ mov(tmp2, index1);
4665 __ orr(tmp2, tmp2, index2);
4666 __ tst(tmp2, Operand(kSmiTagMask));
4667 deferred->Branch(nz);
4668
4669 // Bring the offsets into the fixed array in tmp1 into index1 and
4670 // index2.
4671 __ mov(tmp2, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
4672 __ add(index1, tmp2, Operand(index1, LSL, kPointerSizeLog2 - kSmiTagSize));
4673 __ add(index2, tmp2, Operand(index2, LSL, kPointerSizeLog2 - kSmiTagSize));
4674
4675 // Swap elements.
4676 Register tmp3 = object;
4677 object = no_reg;
4678 __ ldr(tmp3, MemOperand(tmp1, index1));
4679 __ ldr(tmp2, MemOperand(tmp1, index2));
4680 __ str(tmp3, MemOperand(tmp1, index2));
4681 __ str(tmp2, MemOperand(tmp1, index1));
4682
4683 Label done;
4684 __ InNewSpace(tmp1, tmp2, eq, &done);
4685 // Possible optimization: do a check that both values are Smis
4686 // (or them and test against Smi mask.)
4687
4688 __ mov(tmp2, tmp1);
4689 RecordWriteStub recordWrite1(tmp1, index1, tmp3);
4690 __ CallStub(&recordWrite1);
4691
4692 RecordWriteStub recordWrite2(tmp2, index2, tmp3);
4693 __ CallStub(&recordWrite2);
4694
4695 __ bind(&done);
4696
4697 deferred->BindExit();
4698 __ LoadRoot(tmp1, Heap::kUndefinedValueRootIndex);
4699 frame_->EmitPush(tmp1);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00004700}
4701
4702
ager@chromium.org357bf652010-04-12 11:30:10 +00004703void CodeGenerator::GenerateCallFunction(ZoneList<Expression*>* args) {
4704 Comment cmnt(masm_, "[ GenerateCallFunction");
4705
4706 ASSERT(args->length() >= 2);
4707
4708 int n_args = args->length() - 2; // for receiver and function.
4709 Load(args->at(0)); // receiver
4710 for (int i = 0; i < n_args; i++) {
4711 Load(args->at(i + 1));
4712 }
4713 Load(args->at(n_args + 1)); // function
4714 frame_->CallJSFunction(n_args);
4715 frame_->EmitPush(r0);
4716}
4717
4718
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004719void CodeGenerator::GenerateMathSin(ZoneList<Expression*>* args) {
4720 ASSERT_EQ(args->length(), 1);
4721 // Load the argument on the stack and jump to the runtime.
4722 Load(args->at(0));
4723 frame_->CallRuntime(Runtime::kMath_sin, 1);
4724 frame_->EmitPush(r0);
4725}
4726
4727
4728void CodeGenerator::GenerateMathCos(ZoneList<Expression*>* args) {
4729 ASSERT_EQ(args->length(), 1);
4730 // Load the argument on the stack and jump to the runtime.
4731 Load(args->at(0));
4732 frame_->CallRuntime(Runtime::kMath_cos, 1);
4733 frame_->EmitPush(r0);
4734}
4735
4736
ager@chromium.org7c537e22008-10-16 08:43:32 +00004737void CodeGenerator::GenerateObjectEquals(ZoneList<Expression*>* args) {
ager@chromium.org357bf652010-04-12 11:30:10 +00004738 VirtualFrame::SpilledScope spilled_scope(frame_);
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004739 ASSERT(args->length() == 2);
4740
4741 // Load the two objects into registers and perform the comparison.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004742 LoadAndSpill(args->at(0));
4743 LoadAndSpill(args->at(1));
4744 frame_->EmitPop(r0);
4745 frame_->EmitPop(r1);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00004746 __ cmp(r0, r1);
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004747 cc_reg_ = eq;
4748}
4749
4750
ager@chromium.org7c537e22008-10-16 08:43:32 +00004751void CodeGenerator::VisitCallRuntime(CallRuntime* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004752#ifdef DEBUG
4753 int original_height = frame_->height();
4754#endif
ager@chromium.org357bf652010-04-12 11:30:10 +00004755 VirtualFrame::SpilledScope spilled_scope(frame_);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004756 if (CheckForInlineRuntimeCall(node)) {
4757 ASSERT((has_cc() && frame_->height() == original_height) ||
4758 (!has_cc() && frame_->height() == original_height + 1));
4759 return;
4760 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004761
4762 ZoneList<Expression*>* args = node->arguments();
4763 Comment cmnt(masm_, "[ CallRuntime");
4764 Runtime::Function* function = node->function();
4765
ager@chromium.org41826e72009-03-30 13:30:57 +00004766 if (function == NULL) {
mads.s.ager31e71382008-08-13 09:32:07 +00004767 // Prepare stack for calling JS runtime function.
mads.s.ager31e71382008-08-13 09:32:07 +00004768 // Push the builtins object found in the current global object.
4769 __ ldr(r1, GlobalObject());
4770 __ ldr(r0, FieldMemOperand(r1, GlobalObject::kBuiltinsOffset));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004771 frame_->EmitPush(r0);
ager@chromium.org41826e72009-03-30 13:30:57 +00004772 }
mads.s.ager31e71382008-08-13 09:32:07 +00004773
ager@chromium.org41826e72009-03-30 13:30:57 +00004774 // Push the arguments ("left-to-right").
4775 int arg_count = args->length();
4776 for (int i = 0; i < arg_count; i++) {
4777 LoadAndSpill(args->at(i));
4778 }
mads.s.ager31e71382008-08-13 09:32:07 +00004779
ager@chromium.org41826e72009-03-30 13:30:57 +00004780 if (function == NULL) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004781 // Call the JS runtime function.
ager@chromium.org5c838252010-02-19 08:53:10 +00004782 __ mov(r2, Operand(node->name()));
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004783 InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP;
4784 Handle<Code> stub = ComputeCallInitialize(arg_count, in_loop);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004785 frame_->CallCodeObject(stub, RelocInfo::CODE_TARGET, arg_count + 1);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00004786 __ ldr(cp, frame_->Context());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004787 frame_->EmitPush(r0);
ager@chromium.org41826e72009-03-30 13:30:57 +00004788 } else {
4789 // Call the C runtime function.
4790 frame_->CallRuntime(function, arg_count);
4791 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004792 }
ager@chromium.orgac091b72010-05-05 07:34:42 +00004793 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004794}
4795
4796
ager@chromium.org7c537e22008-10-16 08:43:32 +00004797void CodeGenerator::VisitUnaryOperation(UnaryOperation* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004798#ifdef DEBUG
4799 int original_height = frame_->height();
4800#endif
ager@chromium.org357bf652010-04-12 11:30:10 +00004801 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004802 Comment cmnt(masm_, "[ UnaryOperation");
4803
4804 Token::Value op = node->op();
4805
4806 if (op == Token::NOT) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004807 LoadConditionAndSpill(node->expression(),
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004808 false_target(),
4809 true_target(),
4810 true);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00004811 // LoadCondition may (and usually does) leave a test and branch to
4812 // be emitted by the caller. In that case, negate the condition.
4813 if (has_cc()) cc_reg_ = NegateCondition(cc_reg_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004814
4815 } else if (op == Token::DELETE) {
4816 Property* property = node->expression()->AsProperty();
mads.s.ager31e71382008-08-13 09:32:07 +00004817 Variable* variable = node->expression()->AsVariableProxy()->AsVariable();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004818 if (property != NULL) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004819 LoadAndSpill(property->obj());
4820 LoadAndSpill(property->key());
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00004821 frame_->InvokeBuiltin(Builtins::DELETE, CALL_JS, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004822
mads.s.ager31e71382008-08-13 09:32:07 +00004823 } else if (variable != NULL) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004824 Slot* slot = variable->slot();
4825 if (variable->is_global()) {
4826 LoadGlobal();
mads.s.ager31e71382008-08-13 09:32:07 +00004827 __ mov(r0, Operand(variable->name()));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004828 frame_->EmitPush(r0);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00004829 frame_->InvokeBuiltin(Builtins::DELETE, CALL_JS, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004830
4831 } else if (slot != NULL && slot->type() == Slot::LOOKUP) {
4832 // lookup the context holding the named variable
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004833 frame_->EmitPush(cp);
mads.s.ager31e71382008-08-13 09:32:07 +00004834 __ mov(r0, Operand(variable->name()));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004835 frame_->EmitPush(r0);
4836 frame_->CallRuntime(Runtime::kLookupContext, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004837 // r0: context
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004838 frame_->EmitPush(r0);
mads.s.ager31e71382008-08-13 09:32:07 +00004839 __ mov(r0, Operand(variable->name()));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004840 frame_->EmitPush(r0);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00004841 frame_->InvokeBuiltin(Builtins::DELETE, CALL_JS, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004842
mads.s.ager31e71382008-08-13 09:32:07 +00004843 } else {
4844 // Default: Result of deleting non-global, not dynamically
4845 // introduced variables is false.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00004846 __ LoadRoot(r0, Heap::kFalseValueRootIndex);
mads.s.ager31e71382008-08-13 09:32:07 +00004847 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004848
4849 } else {
4850 // Default: Result of deleting expressions is true.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004851 LoadAndSpill(node->expression()); // may have side-effects
4852 frame_->Drop();
ager@chromium.orgab99eea2009-08-25 07:05:41 +00004853 __ LoadRoot(r0, Heap::kTrueValueRootIndex);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004854 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004855 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004856
4857 } else if (op == Token::TYPEOF) {
4858 // Special case for loading the typeof expression; see comment on
4859 // LoadTypeofExpression().
4860 LoadTypeofExpression(node->expression());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004861 frame_->CallRuntime(Runtime::kTypeof, 1);
4862 frame_->EmitPush(r0); // r0 has result
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004863
4864 } else {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004865 bool overwrite =
4866 (node->expression()->AsBinaryOperation() != NULL &&
4867 node->expression()->AsBinaryOperation()->ResultOverwriteAllowed());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004868 LoadAndSpill(node->expression());
4869 frame_->EmitPop(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004870 switch (op) {
4871 case Token::NOT:
4872 case Token::DELETE:
4873 case Token::TYPEOF:
4874 UNREACHABLE(); // handled above
4875 break;
4876
4877 case Token::SUB: {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004878 GenericUnaryOpStub stub(Token::SUB, overwrite);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004879 frame_->CallStub(&stub, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004880 break;
4881 }
4882
4883 case Token::BIT_NOT: {
4884 // smi check
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004885 JumpTarget smi_label;
4886 JumpTarget continue_label;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004887 __ tst(r0, Operand(kSmiTagMask));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004888 smi_label.Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004889
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004890 GenericUnaryOpStub stub(Token::BIT_NOT, overwrite);
4891 frame_->CallStub(&stub, 0);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004892 continue_label.Jump();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004893
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004894 smi_label.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004895 __ mvn(r0, Operand(r0));
4896 __ bic(r0, r0, Operand(kSmiTagMask)); // bit-clear inverted smi-tag
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004897 continue_label.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004898 break;
4899 }
4900
4901 case Token::VOID:
4902 // since the stack top is cached in r0, popping and then
4903 // pushing a value can be done by just writing to r0.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00004904 __ LoadRoot(r0, Heap::kUndefinedValueRootIndex);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004905 break;
4906
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004907 case Token::ADD: {
4908 // Smi check.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004909 JumpTarget continue_label;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004910 __ tst(r0, Operand(kSmiTagMask));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004911 continue_label.Branch(eq);
4912 frame_->EmitPush(r0);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00004913 frame_->InvokeBuiltin(Builtins::TO_NUMBER, CALL_JS, 1);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004914 continue_label.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004915 break;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004916 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004917 default:
4918 UNREACHABLE();
4919 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004920 frame_->EmitPush(r0); // r0 has result
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004921 }
kasperl@chromium.orge959c182009-07-27 08:59:04 +00004922 ASSERT(!has_valid_frame() ||
4923 (has_cc() && frame_->height() == original_height) ||
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004924 (!has_cc() && frame_->height() == original_height + 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004925}
4926
4927
ager@chromium.org7c537e22008-10-16 08:43:32 +00004928void CodeGenerator::VisitCountOperation(CountOperation* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004929#ifdef DEBUG
4930 int original_height = frame_->height();
4931#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004932 Comment cmnt(masm_, "[ CountOperation");
4933
4934 bool is_postfix = node->is_postfix();
4935 bool is_increment = node->op() == Token::INC;
4936
4937 Variable* var = node->expression()->AsVariableProxy()->AsVariable();
4938 bool is_const = (var != NULL && var->mode() == Variable::CONST);
4939
mads.s.ager31e71382008-08-13 09:32:07 +00004940 if (is_postfix) {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00004941 frame_->EmitPush(Operand(Smi::FromInt(0)));
mads.s.ager31e71382008-08-13 09:32:07 +00004942 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004943
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004944 // A constant reference is not saved to, so a constant reference is not a
4945 // compound assignment reference.
4946 { Reference target(this, node->expression(), !is_const);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004947 if (target.is_illegal()) {
4948 // Spoof the virtual frame to have the expected height (one higher
4949 // than on entry).
4950 if (!is_postfix) {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00004951 frame_->EmitPush(Operand(Smi::FromInt(0)));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004952 }
ager@chromium.orgac091b72010-05-05 07:34:42 +00004953 ASSERT_EQ(original_height + 1, frame_->height());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004954 return;
4955 }
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00004956 // This pushes 0, 1 or 2 words on the object to be used later when updating
4957 // the target. It also pushes the current value of the target.
ager@chromium.org357bf652010-04-12 11:30:10 +00004958 target.GetValue();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004959
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004960 JumpTarget slow;
4961 JumpTarget exit;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004962
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004963 // Check for smi operand.
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00004964 Register value = frame_->PopToRegister();
4965 __ tst(value, Operand(kSmiTagMask));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004966 slow.Branch(ne);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004967
4968 // Postfix: Store the old value as the result.
ager@chromium.org3bf7b912008-11-17 09:09:45 +00004969 if (is_postfix) {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00004970 frame_->SetElementAt(value, target.size());
ager@chromium.org3bf7b912008-11-17 09:09:45 +00004971 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004972
4973 // Perform optimistic increment/decrement.
4974 if (is_increment) {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00004975 __ add(value, value, Operand(Smi::FromInt(1)), SetCC);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004976 } else {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00004977 __ sub(value, value, Operand(Smi::FromInt(1)), SetCC);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004978 }
4979
4980 // If the increment/decrement didn't overflow, we're done.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004981 exit.Branch(vc);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004982
4983 // Revert optimistic increment/decrement.
4984 if (is_increment) {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00004985 __ sub(value, value, Operand(Smi::FromInt(1)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004986 } else {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00004987 __ add(value, value, Operand(Smi::FromInt(1)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004988 }
4989
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00004990 // Slow case: Convert to number. At this point the
4991 // value to be incremented is in the value register..
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004992 slow.Bind();
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00004993
4994 // Convert the operand to a number.
4995 frame_->EmitPush(value);
4996
kasperl@chromium.org8ccb0be2009-04-07 07:21:39 +00004997 {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00004998 VirtualFrame::SpilledScope spilled(frame_);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00004999 frame_->InvokeBuiltin(Builtins::TO_NUMBER, CALL_JS, 1);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005000
5001 if (is_postfix) {
5002 // Postfix: store to result (on the stack).
5003 __ str(r0, frame_->ElementAt(target.size()));
5004 }
5005
5006 // Compute the new value.
5007 frame_->EmitPush(r0);
5008 frame_->EmitPush(Operand(Smi::FromInt(1)));
5009 if (is_increment) {
5010 frame_->CallRuntime(Runtime::kNumberAdd, 2);
5011 } else {
5012 frame_->CallRuntime(Runtime::kNumberSub, 2);
5013 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005014 }
5015
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005016 __ Move(value, r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005017 // Store the new value in the target if not const.
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005018 // At this point the answer is in the value register.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005019 exit.Bind();
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005020 frame_->EmitPush(value);
5021 // Set the target with the result, leaving the result on
5022 // top of the stack. Removes the target from the stack if
5023 // it has a non-zero size.
ager@chromium.org7c537e22008-10-16 08:43:32 +00005024 if (!is_const) target.SetValue(NOT_CONST_INIT);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005025 }
5026
5027 // Postfix: Discard the new value and use the old.
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005028 if (is_postfix) frame_->Pop();
ager@chromium.orgac091b72010-05-05 07:34:42 +00005029 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005030}
5031
5032
ager@chromium.orgb26c50a2010-03-26 09:27:16 +00005033void CodeGenerator::GenerateLogicalBooleanOperation(BinaryOperation* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005034 // According to ECMA-262 section 11.11, page 58, the binary logical
5035 // operators must yield the result of one of the two expressions
5036 // before any ToBoolean() conversions. This means that the value
5037 // produced by a && or || operator is not necessarily a boolean.
5038
5039 // NOTE: If the left hand side produces a materialized value (not in
5040 // the CC register), we force the right hand side to do the
5041 // same. This is necessary because we may have to branch to the exit
5042 // after evaluating the left hand side (due to the shortcut
5043 // semantics), but the compiler must (statically) know if the result
5044 // of compiling the binary operation is materialized or not.
ager@chromium.orgb26c50a2010-03-26 09:27:16 +00005045 if (node->op() == Token::AND) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005046 JumpTarget is_true;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005047 LoadConditionAndSpill(node->left(),
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005048 &is_true,
5049 false_target(),
5050 false);
kasperl@chromium.orge959c182009-07-27 08:59:04 +00005051 if (has_valid_frame() && !has_cc()) {
5052 // The left-hand side result is on top of the virtual frame.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005053 JumpTarget pop_and_continue;
5054 JumpTarget exit;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005055
kasperl@chromium.orge959c182009-07-27 08:59:04 +00005056 __ ldr(r0, frame_->Top()); // Duplicate the stack top.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005057 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005058 // Avoid popping the result if it converts to 'false' using the
5059 // standard ToBoolean() conversion as described in ECMA-262,
5060 // section 9.2, page 30.
mads.s.ager31e71382008-08-13 09:32:07 +00005061 ToBoolean(&pop_and_continue, &exit);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005062 Branch(false, &exit);
5063
5064 // Pop the result of evaluating the first part.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005065 pop_and_continue.Bind();
5066 frame_->EmitPop(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005067
5068 // Evaluate right side expression.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005069 is_true.Bind();
5070 LoadAndSpill(node->right());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005071
5072 // Exit (always with a materialized value).
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005073 exit.Bind();
kasperl@chromium.orge959c182009-07-27 08:59:04 +00005074 } else if (has_cc() || is_true.is_linked()) {
5075 // The left-hand side is either (a) partially compiled to
5076 // control flow with a final branch left to emit or (b) fully
5077 // compiled to control flow and possibly true.
5078 if (has_cc()) {
5079 Branch(false, false_target());
5080 }
5081 is_true.Bind();
5082 LoadConditionAndSpill(node->right(),
kasperl@chromium.orge959c182009-07-27 08:59:04 +00005083 true_target(),
5084 false_target(),
5085 false);
5086 } else {
5087 // Nothing to do.
5088 ASSERT(!has_valid_frame() && !has_cc() && !is_true.is_linked());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005089 }
5090
ager@chromium.orgb26c50a2010-03-26 09:27:16 +00005091 } else {
5092 ASSERT(node->op() == Token::OR);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005093 JumpTarget is_false;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005094 LoadConditionAndSpill(node->left(),
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005095 true_target(),
5096 &is_false,
5097 false);
kasperl@chromium.orge959c182009-07-27 08:59:04 +00005098 if (has_valid_frame() && !has_cc()) {
5099 // The left-hand side result is on top of the virtual frame.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005100 JumpTarget pop_and_continue;
5101 JumpTarget exit;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005102
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005103 __ ldr(r0, frame_->Top());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005104 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005105 // Avoid popping the result if it converts to 'true' using the
5106 // standard ToBoolean() conversion as described in ECMA-262,
5107 // section 9.2, page 30.
mads.s.ager31e71382008-08-13 09:32:07 +00005108 ToBoolean(&exit, &pop_and_continue);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005109 Branch(true, &exit);
5110
5111 // Pop the result of evaluating the first part.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005112 pop_and_continue.Bind();
5113 frame_->EmitPop(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005114
5115 // Evaluate right side expression.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005116 is_false.Bind();
5117 LoadAndSpill(node->right());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005118
5119 // Exit (always with a materialized value).
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005120 exit.Bind();
kasperl@chromium.orge959c182009-07-27 08:59:04 +00005121 } else if (has_cc() || is_false.is_linked()) {
5122 // The left-hand side is either (a) partially compiled to
5123 // control flow with a final branch left to emit or (b) fully
5124 // compiled to control flow and possibly false.
5125 if (has_cc()) {
5126 Branch(true, true_target());
5127 }
5128 is_false.Bind();
5129 LoadConditionAndSpill(node->right(),
kasperl@chromium.orge959c182009-07-27 08:59:04 +00005130 true_target(),
5131 false_target(),
5132 false);
5133 } else {
5134 // Nothing to do.
5135 ASSERT(!has_valid_frame() && !has_cc() && !is_false.is_linked());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005136 }
ager@chromium.orgb26c50a2010-03-26 09:27:16 +00005137 }
5138}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005139
ager@chromium.orgb26c50a2010-03-26 09:27:16 +00005140
5141void CodeGenerator::VisitBinaryOperation(BinaryOperation* node) {
5142#ifdef DEBUG
5143 int original_height = frame_->height();
5144#endif
ager@chromium.orgb26c50a2010-03-26 09:27:16 +00005145 Comment cmnt(masm_, "[ BinaryOperation");
5146
5147 if (node->op() == Token::AND || node->op() == Token::OR) {
ager@chromium.org357bf652010-04-12 11:30:10 +00005148 VirtualFrame::SpilledScope spilled_scope(frame_);
ager@chromium.orgb26c50a2010-03-26 09:27:16 +00005149 GenerateLogicalBooleanOperation(node);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005150 } else {
5151 // Optimize for the case where (at least) one of the expressions
5152 // is a literal small integer.
5153 Literal* lliteral = node->left()->AsLiteral();
5154 Literal* rliteral = node->right()->AsLiteral();
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005155 // NOTE: The code below assumes that the slow cases (calls to runtime)
5156 // never return a constant/immutable object.
5157 bool overwrite_left =
5158 (node->left()->AsBinaryOperation() != NULL &&
5159 node->left()->AsBinaryOperation()->ResultOverwriteAllowed());
5160 bool overwrite_right =
5161 (node->right()->AsBinaryOperation() != NULL &&
5162 node->right()->AsBinaryOperation()->ResultOverwriteAllowed());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005163
5164 if (rliteral != NULL && rliteral->handle()->IsSmi()) {
ager@chromium.org357bf652010-04-12 11:30:10 +00005165 VirtualFrame::RegisterAllocationScope scope(this);
5166 Load(node->left());
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00005167 SmiOperation(node->op(),
5168 rliteral->handle(),
5169 false,
5170 overwrite_right ? OVERWRITE_RIGHT : NO_OVERWRITE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005171 } else if (lliteral != NULL && lliteral->handle()->IsSmi()) {
ager@chromium.org357bf652010-04-12 11:30:10 +00005172 VirtualFrame::RegisterAllocationScope scope(this);
5173 Load(node->right());
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00005174 SmiOperation(node->op(),
5175 lliteral->handle(),
5176 true,
5177 overwrite_left ? OVERWRITE_LEFT : NO_OVERWRITE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005178 } else {
ager@chromium.org357bf652010-04-12 11:30:10 +00005179 VirtualFrame::RegisterAllocationScope scope(this);
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005180 OverwriteMode overwrite_mode = NO_OVERWRITE;
5181 if (overwrite_left) {
5182 overwrite_mode = OVERWRITE_LEFT;
5183 } else if (overwrite_right) {
5184 overwrite_mode = OVERWRITE_RIGHT;
5185 }
ager@chromium.org357bf652010-04-12 11:30:10 +00005186 Load(node->left());
5187 Load(node->right());
5188 VirtualFrameBinaryOperation(node->op(), overwrite_mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005189 }
5190 }
kasperl@chromium.orge959c182009-07-27 08:59:04 +00005191 ASSERT(!has_valid_frame() ||
5192 (has_cc() && frame_->height() == original_height) ||
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005193 (!has_cc() && frame_->height() == original_height + 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005194}
5195
5196
ager@chromium.org7c537e22008-10-16 08:43:32 +00005197void CodeGenerator::VisitThisFunction(ThisFunction* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005198#ifdef DEBUG
5199 int original_height = frame_->height();
5200#endif
ager@chromium.org357bf652010-04-12 11:30:10 +00005201 VirtualFrame::SpilledScope spilled_scope(frame_);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005202 __ ldr(r0, frame_->Function());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005203 frame_->EmitPush(r0);
ager@chromium.orgac091b72010-05-05 07:34:42 +00005204 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005205}
5206
5207
ager@chromium.org7c537e22008-10-16 08:43:32 +00005208void CodeGenerator::VisitCompareOperation(CompareOperation* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005209#ifdef DEBUG
5210 int original_height = frame_->height();
5211#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005212 Comment cmnt(masm_, "[ CompareOperation");
5213
ager@chromium.org357bf652010-04-12 11:30:10 +00005214 VirtualFrame::RegisterAllocationScope nonspilled_scope(this);
5215
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005216 // Get the expressions from the node.
5217 Expression* left = node->left();
5218 Expression* right = node->right();
5219 Token::Value op = node->op();
5220
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005221 // To make null checks efficient, we check if either left or right is the
5222 // literal 'null'. If so, we optimize the code by inlining a null check
5223 // instead of calling the (very) general runtime routine for checking
5224 // equality.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005225 if (op == Token::EQ || op == Token::EQ_STRICT) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005226 bool left_is_null =
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005227 left->AsLiteral() != NULL && left->AsLiteral()->IsNull();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005228 bool right_is_null =
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005229 right->AsLiteral() != NULL && right->AsLiteral()->IsNull();
5230 // The 'null' value can only be equal to 'null' or 'undefined'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005231 if (left_is_null || right_is_null) {
ager@chromium.org357bf652010-04-12 11:30:10 +00005232 Load(left_is_null ? right : left);
5233 Register tos = frame_->PopToRegister();
5234 // JumpTargets can't cope with register allocation yet.
5235 frame_->SpillAll();
ager@chromium.orgab99eea2009-08-25 07:05:41 +00005236 __ LoadRoot(ip, Heap::kNullValueRootIndex);
ager@chromium.org357bf652010-04-12 11:30:10 +00005237 __ cmp(tos, ip);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005238
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005239 // The 'null' value is only equal to 'undefined' if using non-strict
5240 // comparisons.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005241 if (op != Token::EQ_STRICT) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005242 true_target()->Branch(eq);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005243
ager@chromium.orgab99eea2009-08-25 07:05:41 +00005244 __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
ager@chromium.org357bf652010-04-12 11:30:10 +00005245 __ cmp(tos, Operand(ip));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005246 true_target()->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005247
ager@chromium.org357bf652010-04-12 11:30:10 +00005248 __ tst(tos, Operand(kSmiTagMask));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005249 false_target()->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005250
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005251 // It can be an undetectable object.
ager@chromium.org357bf652010-04-12 11:30:10 +00005252 __ ldr(tos, FieldMemOperand(tos, HeapObject::kMapOffset));
5253 __ ldrb(tos, FieldMemOperand(tos, Map::kBitFieldOffset));
5254 __ and_(tos, tos, Operand(1 << Map::kIsUndetectable));
5255 __ cmp(tos, Operand(1 << Map::kIsUndetectable));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005256 }
5257
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005258 cc_reg_ = eq;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005259 ASSERT(has_cc() && frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005260 return;
5261 }
5262 }
5263
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005264 // To make typeof testing for natives implemented in JavaScript really
5265 // efficient, we generate special code for expressions of the form:
5266 // 'typeof <expression> == <string>'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005267 UnaryOperation* operation = left->AsUnaryOperation();
5268 if ((op == Token::EQ || op == Token::EQ_STRICT) &&
5269 (operation != NULL && operation->op() == Token::TYPEOF) &&
5270 (right->AsLiteral() != NULL &&
5271 right->AsLiteral()->handle()->IsString())) {
5272 Handle<String> check(String::cast(*right->AsLiteral()->handle()));
5273
ager@chromium.org357bf652010-04-12 11:30:10 +00005274 // Load the operand, move it to a register.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005275 LoadTypeofExpression(operation->expression());
ager@chromium.org357bf652010-04-12 11:30:10 +00005276 Register tos = frame_->PopToRegister();
5277
5278 // JumpTargets can't cope with register allocation yet.
5279 frame_->SpillAll();
5280
5281 Register scratch = VirtualFrame::scratch0();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005282
5283 if (check->Equals(Heap::number_symbol())) {
ager@chromium.org357bf652010-04-12 11:30:10 +00005284 __ tst(tos, Operand(kSmiTagMask));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005285 true_target()->Branch(eq);
ager@chromium.org357bf652010-04-12 11:30:10 +00005286 __ ldr(tos, FieldMemOperand(tos, HeapObject::kMapOffset));
ager@chromium.orgab99eea2009-08-25 07:05:41 +00005287 __ LoadRoot(ip, Heap::kHeapNumberMapRootIndex);
ager@chromium.org357bf652010-04-12 11:30:10 +00005288 __ cmp(tos, ip);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005289 cc_reg_ = eq;
5290
5291 } else if (check->Equals(Heap::string_symbol())) {
ager@chromium.org357bf652010-04-12 11:30:10 +00005292 __ tst(tos, Operand(kSmiTagMask));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005293 false_target()->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005294
ager@chromium.org357bf652010-04-12 11:30:10 +00005295 __ ldr(tos, FieldMemOperand(tos, HeapObject::kMapOffset));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005296
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005297 // It can be an undetectable string object.
ager@chromium.org357bf652010-04-12 11:30:10 +00005298 __ ldrb(scratch, FieldMemOperand(tos, Map::kBitFieldOffset));
5299 __ and_(scratch, scratch, Operand(1 << Map::kIsUndetectable));
5300 __ cmp(scratch, Operand(1 << Map::kIsUndetectable));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005301 false_target()->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005302
ager@chromium.org357bf652010-04-12 11:30:10 +00005303 __ ldrb(scratch, FieldMemOperand(tos, Map::kInstanceTypeOffset));
5304 __ cmp(scratch, Operand(FIRST_NONSTRING_TYPE));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005305 cc_reg_ = lt;
5306
5307 } else if (check->Equals(Heap::boolean_symbol())) {
ager@chromium.orgab99eea2009-08-25 07:05:41 +00005308 __ LoadRoot(ip, Heap::kTrueValueRootIndex);
ager@chromium.org357bf652010-04-12 11:30:10 +00005309 __ cmp(tos, ip);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005310 true_target()->Branch(eq);
ager@chromium.orgab99eea2009-08-25 07:05:41 +00005311 __ LoadRoot(ip, Heap::kFalseValueRootIndex);
ager@chromium.org357bf652010-04-12 11:30:10 +00005312 __ cmp(tos, ip);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005313 cc_reg_ = eq;
5314
5315 } else if (check->Equals(Heap::undefined_symbol())) {
ager@chromium.orgab99eea2009-08-25 07:05:41 +00005316 __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
ager@chromium.org357bf652010-04-12 11:30:10 +00005317 __ cmp(tos, ip);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005318 true_target()->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005319
ager@chromium.org357bf652010-04-12 11:30:10 +00005320 __ tst(tos, Operand(kSmiTagMask));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005321 false_target()->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005322
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005323 // It can be an undetectable object.
ager@chromium.org357bf652010-04-12 11:30:10 +00005324 __ ldr(tos, FieldMemOperand(tos, HeapObject::kMapOffset));
5325 __ ldrb(scratch, FieldMemOperand(tos, Map::kBitFieldOffset));
5326 __ and_(scratch, scratch, Operand(1 << Map::kIsUndetectable));
5327 __ cmp(scratch, Operand(1 << Map::kIsUndetectable));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005328
5329 cc_reg_ = eq;
5330
5331 } else if (check->Equals(Heap::function_symbol())) {
ager@chromium.org357bf652010-04-12 11:30:10 +00005332 __ tst(tos, Operand(kSmiTagMask));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005333 false_target()->Branch(eq);
ager@chromium.org357bf652010-04-12 11:30:10 +00005334 Register map_reg = scratch;
5335 __ CompareObjectType(tos, map_reg, tos, JS_FUNCTION_TYPE);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005336 true_target()->Branch(eq);
5337 // Regular expressions are callable so typeof == 'function'.
ager@chromium.org357bf652010-04-12 11:30:10 +00005338 __ CompareInstanceType(map_reg, tos, JS_REGEXP_TYPE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005339 cc_reg_ = eq;
5340
5341 } else if (check->Equals(Heap::object_symbol())) {
ager@chromium.org357bf652010-04-12 11:30:10 +00005342 __ tst(tos, Operand(kSmiTagMask));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005343 false_target()->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005344
ager@chromium.orgab99eea2009-08-25 07:05:41 +00005345 __ LoadRoot(ip, Heap::kNullValueRootIndex);
ager@chromium.org357bf652010-04-12 11:30:10 +00005346 __ cmp(tos, ip);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005347 true_target()->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005348
ager@chromium.org357bf652010-04-12 11:30:10 +00005349 Register map_reg = scratch;
5350 __ CompareObjectType(tos, map_reg, tos, JS_REGEXP_TYPE);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005351 false_target()->Branch(eq);
5352
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005353 // It can be an undetectable object.
ager@chromium.org357bf652010-04-12 11:30:10 +00005354 __ ldrb(tos, FieldMemOperand(map_reg, Map::kBitFieldOffset));
5355 __ and_(tos, tos, Operand(1 << Map::kIsUndetectable));
5356 __ cmp(tos, Operand(1 << Map::kIsUndetectable));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005357 false_target()->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005358
ager@chromium.org357bf652010-04-12 11:30:10 +00005359 __ ldrb(tos, FieldMemOperand(map_reg, Map::kInstanceTypeOffset));
5360 __ cmp(tos, Operand(FIRST_JS_OBJECT_TYPE));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005361 false_target()->Branch(lt);
ager@chromium.org357bf652010-04-12 11:30:10 +00005362 __ cmp(tos, Operand(LAST_JS_OBJECT_TYPE));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005363 cc_reg_ = le;
5364
5365 } else {
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005366 // Uncommon case: typeof testing against a string literal that is
5367 // never returned from the typeof operator.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005368 false_target()->Jump();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005369 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005370 ASSERT(!has_valid_frame() ||
5371 (has_cc() && frame_->height() == original_height));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005372 return;
5373 }
5374
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005375 switch (op) {
5376 case Token::EQ:
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005377 Comparison(eq, left, right, false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005378 break;
5379
5380 case Token::LT:
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005381 Comparison(lt, left, right);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005382 break;
5383
5384 case Token::GT:
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005385 Comparison(gt, left, right);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005386 break;
5387
5388 case Token::LTE:
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005389 Comparison(le, left, right);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005390 break;
5391
5392 case Token::GTE:
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005393 Comparison(ge, left, right);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005394 break;
5395
5396 case Token::EQ_STRICT:
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005397 Comparison(eq, left, right, true);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005398 break;
5399
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005400 case Token::IN: {
ager@chromium.org357bf652010-04-12 11:30:10 +00005401 VirtualFrame::SpilledScope scope(frame_);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005402 LoadAndSpill(left);
5403 LoadAndSpill(right);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00005404 frame_->InvokeBuiltin(Builtins::IN, CALL_JS, 2);
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00005405 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005406 break;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005407 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005408
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005409 case Token::INSTANCEOF: {
ager@chromium.org357bf652010-04-12 11:30:10 +00005410 VirtualFrame::SpilledScope scope(frame_);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005411 LoadAndSpill(left);
5412 LoadAndSpill(right);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00005413 InstanceofStub stub;
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00005414 frame_->CallStub(&stub, 2);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00005415 // At this point if instanceof succeeded then r0 == 0.
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00005416 __ tst(r0, Operand(r0));
ager@chromium.org7c537e22008-10-16 08:43:32 +00005417 cc_reg_ = eq;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005418 break;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005419 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005420
5421 default:
5422 UNREACHABLE();
5423 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005424 ASSERT((has_cc() && frame_->height() == original_height) ||
5425 (!has_cc() && frame_->height() == original_height + 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005426}
5427
5428
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005429class DeferredReferenceGetNamedValue: public DeferredCode {
5430 public:
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005431 explicit DeferredReferenceGetNamedValue(Register receiver,
5432 Handle<String> name)
5433 : receiver_(receiver), name_(name) {
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005434 set_comment("[ DeferredReferenceGetNamedValue");
5435 }
5436
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005437 virtual void Generate();
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005438
5439 private:
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005440 Register receiver_;
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005441 Handle<String> name_;
5442};
5443
5444
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005445void DeferredReferenceGetNamedValue::Generate() {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005446 ASSERT(receiver_.is(r0) || receiver_.is(r1));
5447
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005448 Register scratch1 = VirtualFrame::scratch0();
5449 Register scratch2 = VirtualFrame::scratch1();
5450 __ DecrementCounter(&Counters::named_load_inline, 1, scratch1, scratch2);
5451 __ IncrementCounter(&Counters::named_load_inline_miss, 1, scratch1, scratch2);
5452
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005453 // Ensure receiver in r0 and name in r2 to match load ic calling convention.
5454 __ Move(r0, receiver_);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005455 __ mov(r2, Operand(name_));
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005456
5457 // The rest of the instructions in the deferred code must be together.
5458 { Assembler::BlockConstPoolScope block_const_pool(masm_);
5459 Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize));
5460 __ Call(ic, RelocInfo::CODE_TARGET);
5461 // The call must be followed by a nop(1) instruction to indicate that the
5462 // in-object has been inlined.
5463 __ nop(PROPERTY_ACCESS_INLINED);
5464
5465 // Block the constant pool for one more instruction after leaving this
5466 // constant pool block scope to include the branch instruction ending the
5467 // deferred code.
5468 __ BlockConstPoolFor(1);
5469 }
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005470}
5471
5472
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005473class DeferredReferenceGetKeyedValue: public DeferredCode {
5474 public:
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005475 DeferredReferenceGetKeyedValue(Register key, Register receiver)
5476 : key_(key), receiver_(receiver) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005477 set_comment("[ DeferredReferenceGetKeyedValue");
5478 }
5479
5480 virtual void Generate();
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005481
5482 private:
5483 Register key_;
5484 Register receiver_;
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005485};
5486
5487
5488void DeferredReferenceGetKeyedValue::Generate() {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005489 ASSERT((key_.is(r0) && receiver_.is(r1)) ||
5490 (key_.is(r1) && receiver_.is(r0)));
5491
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005492 Register scratch1 = VirtualFrame::scratch0();
5493 Register scratch2 = VirtualFrame::scratch1();
5494 __ DecrementCounter(&Counters::keyed_load_inline, 1, scratch1, scratch2);
5495 __ IncrementCounter(&Counters::keyed_load_inline_miss, 1, scratch1, scratch2);
5496
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005497 // Ensure key in r0 and receiver in r1 to match keyed load ic calling
5498 // convention.
5499 if (key_.is(r1)) {
5500 __ Swap(r0, r1, ip);
5501 }
5502
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005503 // The rest of the instructions in the deferred code must be together.
5504 { Assembler::BlockConstPoolScope block_const_pool(masm_);
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00005505 // Call keyed load IC. It has the arguments key and receiver in r0 and r1.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005506 Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
5507 __ Call(ic, RelocInfo::CODE_TARGET);
5508 // The call must be followed by a nop instruction to indicate that the
5509 // keyed load has been inlined.
5510 __ nop(PROPERTY_ACCESS_INLINED);
5511
5512 // Block the constant pool for one more instruction after leaving this
5513 // constant pool block scope to include the branch instruction ending the
5514 // deferred code.
5515 __ BlockConstPoolFor(1);
5516 }
5517}
5518
5519
5520class DeferredReferenceSetKeyedValue: public DeferredCode {
5521 public:
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005522 DeferredReferenceSetKeyedValue(Register value,
5523 Register key,
5524 Register receiver)
5525 : value_(value), key_(key), receiver_(receiver) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005526 set_comment("[ DeferredReferenceSetKeyedValue");
5527 }
5528
5529 virtual void Generate();
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005530
5531 private:
5532 Register value_;
5533 Register key_;
5534 Register receiver_;
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005535};
5536
5537
5538void DeferredReferenceSetKeyedValue::Generate() {
5539 Register scratch1 = VirtualFrame::scratch0();
5540 Register scratch2 = VirtualFrame::scratch1();
5541 __ DecrementCounter(&Counters::keyed_store_inline, 1, scratch1, scratch2);
5542 __ IncrementCounter(
5543 &Counters::keyed_store_inline_miss, 1, scratch1, scratch2);
5544
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005545 // Ensure value in r0, key in r1 and receiver in r2 to match keyed store ic
5546 // calling convention.
5547 if (value_.is(r1)) {
5548 __ Swap(r0, r1, ip);
5549 }
5550 ASSERT(receiver_.is(r2));
5551
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005552 // The rest of the instructions in the deferred code must be together.
5553 { Assembler::BlockConstPoolScope block_const_pool(masm_);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005554 // Call keyed store IC. It has the arguments value, key and receiver in r0,
5555 // r1 and r2.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005556 Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize));
5557 __ Call(ic, RelocInfo::CODE_TARGET);
5558 // The call must be followed by a nop instruction to indicate that the
5559 // keyed store has been inlined.
5560 __ nop(PROPERTY_ACCESS_INLINED);
5561
5562 // Block the constant pool for one more instruction after leaving this
5563 // constant pool block scope to include the branch instruction ending the
5564 // deferred code.
5565 __ BlockConstPoolFor(1);
5566 }
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005567}
5568
5569
5570void CodeGenerator::EmitNamedLoad(Handle<String> name, bool is_contextual) {
5571 if (is_contextual || scope()->is_global_scope() || loop_nesting() == 0) {
5572 Comment cmnt(masm(), "[ Load from named Property");
5573 // Setup the name register and call load IC.
ager@chromium.orgac091b72010-05-05 07:34:42 +00005574 frame_->CallLoadIC(name,
5575 is_contextual
5576 ? RelocInfo::CODE_TARGET_CONTEXT
5577 : RelocInfo::CODE_TARGET);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005578 } else {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005579 // Inline the in-object property case.
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005580 Comment cmnt(masm(), "[ Inlined named property load");
5581
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005582 // Counter will be decremented in the deferred code. Placed here to avoid
5583 // having it in the instruction stream below where patching will occur.
5584 __ IncrementCounter(&Counters::named_load_inline, 1,
5585 frame_->scratch0(), frame_->scratch1());
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005586
5587 // The following instructions are the inlined load of an in-object property.
5588 // Parts of this code is patched, so the exact instructions generated needs
5589 // to be fixed. Therefore the instruction pool is blocked when generating
5590 // this code
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005591
5592 // Load the receiver from the stack.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005593 Register receiver = frame_->PopToRegister();
5594 VirtualFrame::SpilledScope spilled(frame_);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005595
5596 DeferredReferenceGetNamedValue* deferred =
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005597 new DeferredReferenceGetNamedValue(receiver, name);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005598
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005599#ifdef DEBUG
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005600 int kInlinedNamedLoadInstructions = 7;
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005601 Label check_inlined_codesize;
5602 masm_->bind(&check_inlined_codesize);
5603#endif
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005604
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005605 { Assembler::BlockConstPoolScope block_const_pool(masm_);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005606 // Check that the receiver is a heap object.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005607 __ tst(receiver, Operand(kSmiTagMask));
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005608 deferred->Branch(eq);
5609
5610 // Check the map. The null map used below is patched by the inline cache
5611 // code.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005612 __ ldr(r2, FieldMemOperand(receiver, HeapObject::kMapOffset));
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005613 __ mov(r3, Operand(Factory::null_value()));
5614 __ cmp(r2, r3);
5615 deferred->Branch(ne);
5616
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005617 // Initially use an invalid index. The index will be patched by the
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005618 // inline cache code.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005619 __ ldr(r0, MemOperand(receiver, 0));
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005620
5621 // Make sure that the expected number of instructions are generated.
5622 ASSERT_EQ(kInlinedNamedLoadInstructions,
5623 masm_->InstructionsGeneratedSince(&check_inlined_codesize));
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005624 }
5625
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005626 deferred->BindExit();
5627 }
5628}
5629
5630
ager@chromium.orgac091b72010-05-05 07:34:42 +00005631void CodeGenerator::EmitNamedStore(Handle<String> name, bool is_contextual) {
5632#ifdef DEBUG
5633 int expected_height = frame_->height() - (is_contextual ? 1 : 2);
5634#endif
5635 frame_->CallStoreIC(name, is_contextual);
5636
5637 ASSERT_EQ(expected_height, frame_->height());
5638}
5639
5640
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005641void CodeGenerator::EmitKeyedLoad() {
5642 if (loop_nesting() == 0) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005643 Comment cmnt(masm_, "[ Load from keyed property");
5644 frame_->CallKeyedLoadIC();
5645 } else {
5646 // Inline the keyed load.
5647 Comment cmnt(masm_, "[ Inlined load from keyed property");
5648
5649 // Counter will be decremented in the deferred code. Placed here to avoid
5650 // having it in the instruction stream below where patching will occur.
5651 __ IncrementCounter(&Counters::keyed_load_inline, 1,
5652 frame_->scratch0(), frame_->scratch1());
5653
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005654 // Load the key and receiver from the stack.
5655 Register key = frame_->PopToRegister();
5656 Register receiver = frame_->PopToRegister(key);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005657 VirtualFrame::SpilledScope spilled(frame_);
5658
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005659 // The deferred code expects key and receiver in registers.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005660 DeferredReferenceGetKeyedValue* deferred =
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005661 new DeferredReferenceGetKeyedValue(key, receiver);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005662
5663 // Check that the receiver is a heap object.
5664 __ tst(receiver, Operand(kSmiTagMask));
5665 deferred->Branch(eq);
5666
5667 // The following instructions are the part of the inlined load keyed
5668 // property code which can be patched. Therefore the exact number of
5669 // instructions generated need to be fixed, so the constant pool is blocked
5670 // while generating this code.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005671 { Assembler::BlockConstPoolScope block_const_pool(masm_);
5672 Register scratch1 = VirtualFrame::scratch0();
5673 Register scratch2 = VirtualFrame::scratch1();
5674 // Check the map. The null map used below is patched by the inline cache
5675 // code.
5676 __ ldr(scratch1, FieldMemOperand(receiver, HeapObject::kMapOffset));
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005677#ifdef DEBUG
5678 Label check_inlined_codesize;
5679 masm_->bind(&check_inlined_codesize);
5680#endif
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005681 __ mov(scratch2, Operand(Factory::null_value()));
5682 __ cmp(scratch1, scratch2);
5683 deferred->Branch(ne);
5684
5685 // Check that the key is a smi.
5686 __ tst(key, Operand(kSmiTagMask));
5687 deferred->Branch(ne);
5688
5689 // Get the elements array from the receiver and check that it
5690 // is not a dictionary.
5691 __ ldr(scratch1, FieldMemOperand(receiver, JSObject::kElementsOffset));
5692 __ ldr(scratch2, FieldMemOperand(scratch1, JSObject::kMapOffset));
5693 __ LoadRoot(ip, Heap::kFixedArrayMapRootIndex);
5694 __ cmp(scratch2, ip);
5695 deferred->Branch(ne);
5696
5697 // Check that key is within bounds. Use unsigned comparison to handle
5698 // negative keys.
5699 __ ldr(scratch2, FieldMemOperand(scratch1, FixedArray::kLengthOffset));
5700 __ cmp(scratch2, Operand(key, ASR, kSmiTagSize));
5701 deferred->Branch(ls); // Unsigned less equal.
5702
5703 // Load and check that the result is not the hole (key is a smi).
5704 __ LoadRoot(scratch2, Heap::kTheHoleValueRootIndex);
5705 __ add(scratch1,
5706 scratch1,
5707 Operand(FixedArray::kHeaderSize - kHeapObjectTag));
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005708 __ ldr(scratch1,
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005709 MemOperand(scratch1, key, LSL,
5710 kPointerSizeLog2 - (kSmiTagSize + kSmiShiftSize)));
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005711 __ cmp(scratch1, scratch2);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005712 deferred->Branch(eq);
5713
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005714 __ mov(r0, scratch1);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005715 // Make sure that the expected number of instructions are generated.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005716 ASSERT_EQ(kInlinedKeyedLoadInstructionsAfterPatch,
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005717 masm_->InstructionsGeneratedSince(&check_inlined_codesize));
5718 }
5719
5720 deferred->BindExit();
5721 }
5722}
5723
5724
5725void CodeGenerator::EmitKeyedStore(StaticType* key_type) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005726 // Generate inlined version of the keyed store if the code is in a loop
5727 // and the key is likely to be a smi.
5728 if (loop_nesting() > 0 && key_type->IsLikelySmi()) {
5729 // Inline the keyed store.
5730 Comment cmnt(masm_, "[ Inlined store to keyed property");
5731
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005732 Register scratch1 = VirtualFrame::scratch0();
5733 Register scratch2 = VirtualFrame::scratch1();
5734 Register scratch3 = r3;
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005735
5736 // Counter will be decremented in the deferred code. Placed here to avoid
5737 // having it in the instruction stream below where patching will occur.
5738 __ IncrementCounter(&Counters::keyed_store_inline, 1,
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005739 scratch1, scratch2);
5740
5741 // Load the value, key and receiver from the stack.
5742 Register value = frame_->PopToRegister();
5743 Register key = frame_->PopToRegister(value);
5744 Register receiver = r2;
5745 frame_->EmitPop(receiver);
5746 VirtualFrame::SpilledScope spilled(frame_);
5747
5748 // The deferred code expects value, key and receiver in registers.
5749 DeferredReferenceSetKeyedValue* deferred =
5750 new DeferredReferenceSetKeyedValue(value, key, receiver);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005751
5752 // Check that the value is a smi. As this inlined code does not set the
5753 // write barrier it is only possible to store smi values.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005754 __ tst(value, Operand(kSmiTagMask));
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005755 deferred->Branch(ne);
5756
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005757 // Check that the key is a smi.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005758 __ tst(key, Operand(kSmiTagMask));
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005759 deferred->Branch(ne);
5760
5761 // Check that the receiver is a heap object.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005762 __ tst(receiver, Operand(kSmiTagMask));
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005763 deferred->Branch(eq);
5764
5765 // Check that the receiver is a JSArray.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005766 __ CompareObjectType(receiver, scratch1, scratch1, JS_ARRAY_TYPE);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005767 deferred->Branch(ne);
5768
5769 // Check that the key is within bounds. Both the key and the length of
5770 // the JSArray are smis. Use unsigned comparison to handle negative keys.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005771 __ ldr(scratch1, FieldMemOperand(receiver, JSArray::kLengthOffset));
5772 __ cmp(scratch1, key);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005773 deferred->Branch(ls); // Unsigned less equal.
5774
5775 // The following instructions are the part of the inlined store keyed
5776 // property code which can be patched. Therefore the exact number of
5777 // instructions generated need to be fixed, so the constant pool is blocked
5778 // while generating this code.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005779 { Assembler::BlockConstPoolScope block_const_pool(masm_);
5780 // Get the elements array from the receiver and check that it
5781 // is not a dictionary.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005782 __ ldr(scratch1, FieldMemOperand(receiver, JSObject::kElementsOffset));
5783 __ ldr(scratch2, FieldMemOperand(scratch1, JSObject::kMapOffset));
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005784 // Read the fixed array map from the constant pool (not from the root
5785 // array) so that the value can be patched. When debugging, we patch this
5786 // comparison to always fail so that we will hit the IC call in the
5787 // deferred code which will allow the debugger to break for fast case
5788 // stores.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005789#ifdef DEBUG
5790 Label check_inlined_codesize;
5791 masm_->bind(&check_inlined_codesize);
5792#endif
5793 __ mov(scratch3, Operand(Factory::fixed_array_map()));
5794 __ cmp(scratch2, scratch3);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005795 deferred->Branch(ne);
5796
5797 // Store the value.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005798 __ add(scratch1, scratch1,
5799 Operand(FixedArray::kHeaderSize - kHeapObjectTag));
5800 __ str(value,
5801 MemOperand(scratch1, key, LSL,
5802 kPointerSizeLog2 - (kSmiTagSize + kSmiShiftSize)));
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005803
5804 // Make sure that the expected number of instructions are generated.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005805 ASSERT_EQ(kInlinedKeyedStoreInstructionsAfterPatch,
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005806 masm_->InstructionsGeneratedSince(&check_inlined_codesize));
5807 }
5808
5809 deferred->BindExit();
5810 } else {
5811 frame()->CallKeyedStoreIC();
5812 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005813}
5814
5815
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005816#ifdef DEBUG
5817bool CodeGenerator::HasValidEntryRegisters() { return true; }
5818#endif
5819
5820
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005821#undef __
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005822#define __ ACCESS_MASM(masm)
5823
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005824
ager@chromium.org7c537e22008-10-16 08:43:32 +00005825Handle<String> Reference::GetName() {
5826 ASSERT(type_ == NAMED);
5827 Property* property = expression_->AsProperty();
5828 if (property == NULL) {
5829 // Global variable reference treated as a named property reference.
5830 VariableProxy* proxy = expression_->AsVariableProxy();
5831 ASSERT(proxy->AsVariable() != NULL);
5832 ASSERT(proxy->AsVariable()->is_global());
5833 return proxy->name();
5834 } else {
5835 Literal* raw_name = property->key()->AsLiteral();
5836 ASSERT(raw_name != NULL);
5837 return Handle<String>(String::cast(*raw_name->handle()));
5838 }
5839}
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00005840
ager@chromium.org7c537e22008-10-16 08:43:32 +00005841
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005842void Reference::GetValue() {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005843 ASSERT(cgen_->HasValidEntryRegisters());
ager@chromium.org7c537e22008-10-16 08:43:32 +00005844 ASSERT(!is_illegal());
5845 ASSERT(!cgen_->has_cc());
5846 MacroAssembler* masm = cgen_->masm();
5847 Property* property = expression_->AsProperty();
5848 if (property != NULL) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00005849 cgen_->CodeForSourcePosition(property->position());
ager@chromium.org7c537e22008-10-16 08:43:32 +00005850 }
5851
5852 switch (type_) {
5853 case SLOT: {
5854 Comment cmnt(masm, "[ Load from Slot");
5855 Slot* slot = expression_->AsVariableProxy()->AsVariable()->slot();
5856 ASSERT(slot != NULL);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005857 cgen_->LoadFromSlotCheckForArguments(slot, NOT_INSIDE_TYPEOF);
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00005858 if (!persist_after_get_) {
5859 cgen_->UnloadReference(this);
5860 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00005861 break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00005862 }
5863
ager@chromium.org7c537e22008-10-16 08:43:32 +00005864 case NAMED: {
ager@chromium.org7c537e22008-10-16 08:43:32 +00005865 Variable* var = expression_->AsVariableProxy()->AsVariable();
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005866 bool is_global = var != NULL;
5867 ASSERT(!is_global || var->is_global());
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005868 if (persist_after_get_) {
5869 cgen_->frame()->Dup();
5870 }
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005871 cgen_->EmitNamedLoad(GetName(), is_global);
5872 cgen_->frame()->EmitPush(r0);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005873 if (!persist_after_get_) set_unloaded();
ager@chromium.org7c537e22008-10-16 08:43:32 +00005874 break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00005875 }
5876
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005877 case KEYED: {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005878 ASSERT(property != NULL);
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00005879 if (persist_after_get_) {
5880 cgen_->frame()->Dup2();
5881 }
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005882 cgen_->EmitKeyedLoad();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005883 cgen_->frame()->EmitPush(r0);
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00005884 if (!persist_after_get_) set_unloaded();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005885 break;
5886 }
5887
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00005888 default:
5889 UNREACHABLE();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00005890 }
5891}
5892
5893
ager@chromium.org7c537e22008-10-16 08:43:32 +00005894void Reference::SetValue(InitState init_state) {
5895 ASSERT(!is_illegal());
5896 ASSERT(!cgen_->has_cc());
5897 MacroAssembler* masm = cgen_->masm();
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005898 VirtualFrame* frame = cgen_->frame();
ager@chromium.org7c537e22008-10-16 08:43:32 +00005899 Property* property = expression_->AsProperty();
5900 if (property != NULL) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00005901 cgen_->CodeForSourcePosition(property->position());
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00005902 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00005903
ager@chromium.org7c537e22008-10-16 08:43:32 +00005904 switch (type_) {
5905 case SLOT: {
5906 Comment cmnt(masm, "[ Store to Slot");
5907 Slot* slot = expression_->AsVariableProxy()->AsVariable()->slot();
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005908 cgen_->StoreToSlot(slot, init_state);
ager@chromium.orgac091b72010-05-05 07:34:42 +00005909 set_unloaded();
ager@chromium.org7c537e22008-10-16 08:43:32 +00005910 break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00005911 }
5912
ager@chromium.org7c537e22008-10-16 08:43:32 +00005913 case NAMED: {
5914 Comment cmnt(masm, "[ Store to named Property");
ager@chromium.orgac091b72010-05-05 07:34:42 +00005915 cgen_->EmitNamedStore(GetName(), false);
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00005916 frame->EmitPush(r0);
ager@chromium.org5c838252010-02-19 08:53:10 +00005917 set_unloaded();
ager@chromium.org7c537e22008-10-16 08:43:32 +00005918 break;
5919 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00005920
ager@chromium.org7c537e22008-10-16 08:43:32 +00005921 case KEYED: {
5922 Comment cmnt(masm, "[ Store to keyed Property");
5923 Property* property = expression_->AsProperty();
5924 ASSERT(property != NULL);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00005925 cgen_->CodeForSourcePosition(property->position());
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005926 cgen_->EmitKeyedStore(property->key()->type());
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00005927 frame->EmitPush(r0);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005928 set_unloaded();
ager@chromium.org7c537e22008-10-16 08:43:32 +00005929 break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00005930 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00005931
5932 default:
5933 UNREACHABLE();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00005934 }
5935}
5936
5937
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005938void FastNewClosureStub::Generate(MacroAssembler* masm) {
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00005939 // Create a new closure from the given function info in new
5940 // space. Set the context to the current context in cp.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005941 Label gc;
5942
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00005943 // Pop the function info from the stack.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005944 __ pop(r3);
5945
5946 // Attempt to allocate new JSFunction in new space.
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00005947 __ AllocateInNewSpace(JSFunction::kSize,
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005948 r0,
5949 r1,
5950 r2,
5951 &gc,
5952 TAG_OBJECT);
5953
5954 // Compute the function map in the current global context and set that
5955 // as the map of the allocated object.
5956 __ ldr(r2, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_INDEX)));
5957 __ ldr(r2, FieldMemOperand(r2, GlobalObject::kGlobalContextOffset));
5958 __ ldr(r2, MemOperand(r2, Context::SlotOffset(Context::FUNCTION_MAP_INDEX)));
5959 __ str(r2, FieldMemOperand(r0, HeapObject::kMapOffset));
5960
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00005961 // Initialize the rest of the function. We don't have to update the
5962 // write barrier because the allocated object is in new space.
5963 __ LoadRoot(r1, Heap::kEmptyFixedArrayRootIndex);
5964 __ LoadRoot(r2, Heap::kTheHoleValueRootIndex);
5965 __ str(r1, FieldMemOperand(r0, JSObject::kPropertiesOffset));
5966 __ str(r1, FieldMemOperand(r0, JSObject::kElementsOffset));
5967 __ str(r2, FieldMemOperand(r0, JSFunction::kPrototypeOrInitialMapOffset));
5968 __ str(r3, FieldMemOperand(r0, JSFunction::kSharedFunctionInfoOffset));
5969 __ str(cp, FieldMemOperand(r0, JSFunction::kContextOffset));
5970 __ str(r1, FieldMemOperand(r0, JSFunction::kLiteralsOffset));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005971
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00005972 // Return result. The argument function info has been popped already.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005973 __ Ret();
5974
5975 // Create a new closure through the slower runtime call.
5976 __ bind(&gc);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005977 __ Push(cp, r3);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005978 __ TailCallRuntime(Runtime::kNewClosure, 2, 1);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005979}
5980
5981
5982void FastNewContextStub::Generate(MacroAssembler* masm) {
5983 // Try to allocate the context in new space.
5984 Label gc;
5985 int length = slots_ + Context::MIN_CONTEXT_SLOTS;
5986
5987 // Attempt to allocate the context in new space.
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00005988 __ AllocateInNewSpace(FixedArray::SizeFor(length),
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005989 r0,
5990 r1,
5991 r2,
5992 &gc,
5993 TAG_OBJECT);
5994
5995 // Load the function from the stack.
ager@chromium.org5c838252010-02-19 08:53:10 +00005996 __ ldr(r3, MemOperand(sp, 0));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005997
5998 // Setup the object header.
5999 __ LoadRoot(r2, Heap::kContextMapRootIndex);
6000 __ str(r2, FieldMemOperand(r0, HeapObject::kMapOffset));
6001 __ mov(r2, Operand(length));
6002 __ str(r2, FieldMemOperand(r0, Array::kLengthOffset));
6003
6004 // Setup the fixed slots.
6005 __ mov(r1, Operand(Smi::FromInt(0)));
6006 __ str(r3, MemOperand(r0, Context::SlotOffset(Context::CLOSURE_INDEX)));
6007 __ str(r0, MemOperand(r0, Context::SlotOffset(Context::FCONTEXT_INDEX)));
6008 __ str(r1, MemOperand(r0, Context::SlotOffset(Context::PREVIOUS_INDEX)));
6009 __ str(r1, MemOperand(r0, Context::SlotOffset(Context::EXTENSION_INDEX)));
6010
6011 // Copy the global object from the surrounding context.
6012 __ ldr(r1, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_INDEX)));
6013 __ str(r1, MemOperand(r0, Context::SlotOffset(Context::GLOBAL_INDEX)));
6014
6015 // Initialize the rest of the slots to undefined.
6016 __ LoadRoot(r1, Heap::kUndefinedValueRootIndex);
6017 for (int i = Context::MIN_CONTEXT_SLOTS; i < length; i++) {
6018 __ str(r1, MemOperand(r0, Context::SlotOffset(i)));
6019 }
6020
6021 // Remove the on-stack argument and return.
6022 __ mov(cp, r0);
6023 __ pop();
6024 __ Ret();
6025
6026 // Need to collect. Call into runtime system.
6027 __ bind(&gc);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006028 __ TailCallRuntime(Runtime::kNewContext, 1, 1);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006029}
6030
6031
ager@chromium.org5c838252010-02-19 08:53:10 +00006032void FastCloneShallowArrayStub::Generate(MacroAssembler* masm) {
6033 // Stack layout on entry:
6034 //
6035 // [sp]: constant elements.
6036 // [sp + kPointerSize]: literal index.
6037 // [sp + (2 * kPointerSize)]: literals array.
6038
6039 // All sizes here are multiples of kPointerSize.
6040 int elements_size = (length_ > 0) ? FixedArray::SizeFor(length_) : 0;
6041 int size = JSArray::kSize + elements_size;
6042
6043 // Load boilerplate object into r3 and check if we need to create a
6044 // boilerplate.
6045 Label slow_case;
6046 __ ldr(r3, MemOperand(sp, 2 * kPointerSize));
6047 __ ldr(r0, MemOperand(sp, 1 * kPointerSize));
6048 __ add(r3, r3, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
6049 __ ldr(r3, MemOperand(r3, r0, LSL, kPointerSizeLog2 - kSmiTagSize));
6050 __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
6051 __ cmp(r3, ip);
6052 __ b(eq, &slow_case);
6053
6054 // Allocate both the JS array and the elements array in one big
6055 // allocation. This avoids multiple limit checks.
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00006056 __ AllocateInNewSpace(size,
ager@chromium.org5c838252010-02-19 08:53:10 +00006057 r0,
6058 r1,
6059 r2,
6060 &slow_case,
6061 TAG_OBJECT);
6062
6063 // Copy the JS array part.
6064 for (int i = 0; i < JSArray::kSize; i += kPointerSize) {
6065 if ((i != JSArray::kElementsOffset) || (length_ == 0)) {
6066 __ ldr(r1, FieldMemOperand(r3, i));
6067 __ str(r1, FieldMemOperand(r0, i));
6068 }
6069 }
6070
6071 if (length_ > 0) {
6072 // Get hold of the elements array of the boilerplate and setup the
6073 // elements pointer in the resulting object.
6074 __ ldr(r3, FieldMemOperand(r3, JSArray::kElementsOffset));
6075 __ add(r2, r0, Operand(JSArray::kSize));
6076 __ str(r2, FieldMemOperand(r0, JSArray::kElementsOffset));
6077
6078 // Copy the elements array.
6079 for (int i = 0; i < elements_size; i += kPointerSize) {
6080 __ ldr(r1, FieldMemOperand(r3, i));
6081 __ str(r1, FieldMemOperand(r2, i));
6082 }
6083 }
6084
6085 // Return and remove the on-stack parameters.
6086 __ add(sp, sp, Operand(3 * kPointerSize));
6087 __ Ret();
6088
6089 __ bind(&slow_case);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006090 __ TailCallRuntime(Runtime::kCreateArrayLiteralShallow, 3, 1);
ager@chromium.org5c838252010-02-19 08:53:10 +00006091}
6092
6093
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006094// Takes a Smi and converts to an IEEE 64 bit floating point value in two
6095// registers. The format is 1 sign bit, 11 exponent bits (biased 1023) and
6096// 52 fraction bits (20 in the first word, 32 in the second). Zeros is a
6097// scratch register. Destroys the source register. No GC occurs during this
6098// stub so you don't have to set up the frame.
6099class ConvertToDoubleStub : public CodeStub {
6100 public:
6101 ConvertToDoubleStub(Register result_reg_1,
6102 Register result_reg_2,
6103 Register source_reg,
6104 Register scratch_reg)
6105 : result1_(result_reg_1),
6106 result2_(result_reg_2),
6107 source_(source_reg),
6108 zeros_(scratch_reg) { }
6109
6110 private:
6111 Register result1_;
6112 Register result2_;
6113 Register source_;
6114 Register zeros_;
6115
6116 // Minor key encoding in 16 bits.
6117 class ModeBits: public BitField<OverwriteMode, 0, 2> {};
6118 class OpBits: public BitField<Token::Value, 2, 14> {};
6119
6120 Major MajorKey() { return ConvertToDouble; }
6121 int MinorKey() {
6122 // Encode the parameters in a unique 16 bit value.
6123 return result1_.code() +
6124 (result2_.code() << 4) +
6125 (source_.code() << 8) +
6126 (zeros_.code() << 12);
6127 }
6128
6129 void Generate(MacroAssembler* masm);
6130
6131 const char* GetName() { return "ConvertToDoubleStub"; }
6132
6133#ifdef DEBUG
6134 void Print() { PrintF("ConvertToDoubleStub\n"); }
6135#endif
6136};
6137
6138
6139void ConvertToDoubleStub::Generate(MacroAssembler* masm) {
6140#ifndef BIG_ENDIAN_FLOATING_POINT
6141 Register exponent = result1_;
6142 Register mantissa = result2_;
6143#else
6144 Register exponent = result2_;
6145 Register mantissa = result1_;
6146#endif
6147 Label not_special;
6148 // Convert from Smi to integer.
6149 __ mov(source_, Operand(source_, ASR, kSmiTagSize));
6150 // Move sign bit from source to destination. This works because the sign bit
6151 // in the exponent word of the double has the same position and polarity as
6152 // the 2's complement sign bit in a Smi.
6153 ASSERT(HeapNumber::kSignMask == 0x80000000u);
6154 __ and_(exponent, source_, Operand(HeapNumber::kSignMask), SetCC);
6155 // Subtract from 0 if source was negative.
6156 __ rsb(source_, source_, Operand(0), LeaveCC, ne);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006157
6158 // We have -1, 0 or 1, which we treat specially. Register source_ contains
6159 // absolute value: it is either equal to 1 (special case of -1 and 1),
6160 // greater than 1 (not a special case) or less than 1 (special case of 0).
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006161 __ cmp(source_, Operand(1));
6162 __ b(gt, &not_special);
6163
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006164 // For 1 or -1 we need to or in the 0 exponent (biased to 1023).
6165 static const uint32_t exponent_word_for_1 =
6166 HeapNumber::kExponentBias << HeapNumber::kExponentShift;
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006167 __ orr(exponent, exponent, Operand(exponent_word_for_1), LeaveCC, eq);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006168 // 1, 0 and -1 all have 0 for the second word.
6169 __ mov(mantissa, Operand(0));
6170 __ Ret();
6171
6172 __ bind(&not_special);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006173 // Count leading zeros. Uses mantissa for a scratch register on pre-ARM5.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006174 // Gets the wrong answer for 0, but we already checked for that case above.
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006175 __ CountLeadingZeros(source_, mantissa, zeros_);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006176 // Compute exponent and or it into the exponent register.
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006177 // We use mantissa as a scratch register here.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006178 __ rsb(mantissa, zeros_, Operand(31 + HeapNumber::kExponentBias));
6179 __ orr(exponent,
6180 exponent,
6181 Operand(mantissa, LSL, HeapNumber::kExponentShift));
6182 // Shift up the source chopping the top bit off.
6183 __ add(zeros_, zeros_, Operand(1));
6184 // This wouldn't work for 1.0 or -1.0 as the shift would be 32 which means 0.
6185 __ mov(source_, Operand(source_, LSL, zeros_));
6186 // Compute lower part of fraction (last 12 bits).
6187 __ mov(mantissa, Operand(source_, LSL, HeapNumber::kMantissaBitsInTopWord));
6188 // And the top (top 20 bits).
6189 __ orr(exponent,
6190 exponent,
6191 Operand(source_, LSR, 32 - HeapNumber::kMantissaBitsInTopWord));
6192 __ Ret();
6193}
6194
6195
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006196// See comment for class.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006197void WriteInt32ToHeapNumberStub::Generate(MacroAssembler* masm) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006198 Label max_negative_int;
6199 // the_int_ has the answer which is a signed int32 but not a Smi.
6200 // We test for the special value that has a different exponent. This test
6201 // has the neat side effect of setting the flags according to the sign.
6202 ASSERT(HeapNumber::kSignMask == 0x80000000u);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00006203 __ cmp(the_int_, Operand(0x80000000u));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006204 __ b(eq, &max_negative_int);
6205 // Set up the correct exponent in scratch_. All non-Smi int32s have the same.
6206 // A non-Smi integer is 1.xxx * 2^30 so the exponent is 30 (biased).
6207 uint32_t non_smi_exponent =
6208 (HeapNumber::kExponentBias + 30) << HeapNumber::kExponentShift;
6209 __ mov(scratch_, Operand(non_smi_exponent));
6210 // Set the sign bit in scratch_ if the value was negative.
6211 __ orr(scratch_, scratch_, Operand(HeapNumber::kSignMask), LeaveCC, cs);
6212 // Subtract from 0 if the value was negative.
6213 __ rsb(the_int_, the_int_, Operand(0), LeaveCC, cs);
6214 // We should be masking the implict first digit of the mantissa away here,
6215 // but it just ends up combining harmlessly with the last digit of the
6216 // exponent that happens to be 1. The sign bit is 0 so we shift 10 to get
6217 // the most significant 1 to hit the last bit of the 12 bit sign and exponent.
6218 ASSERT(((1 << HeapNumber::kExponentShift) & non_smi_exponent) != 0);
6219 const int shift_distance = HeapNumber::kNonMantissaBitsInTopWord - 2;
6220 __ orr(scratch_, scratch_, Operand(the_int_, LSR, shift_distance));
6221 __ str(scratch_, FieldMemOperand(the_heap_number_,
6222 HeapNumber::kExponentOffset));
6223 __ mov(scratch_, Operand(the_int_, LSL, 32 - shift_distance));
6224 __ str(scratch_, FieldMemOperand(the_heap_number_,
6225 HeapNumber::kMantissaOffset));
6226 __ Ret();
6227
6228 __ bind(&max_negative_int);
6229 // The max negative int32 is stored as a positive number in the mantissa of
6230 // a double because it uses a sign bit instead of using two's complement.
6231 // The actual mantissa bits stored are all 0 because the implicit most
6232 // significant 1 bit is not stored.
6233 non_smi_exponent += 1 << HeapNumber::kExponentShift;
6234 __ mov(ip, Operand(HeapNumber::kSignMask | non_smi_exponent));
6235 __ str(ip, FieldMemOperand(the_heap_number_, HeapNumber::kExponentOffset));
6236 __ mov(ip, Operand(0));
6237 __ str(ip, FieldMemOperand(the_heap_number_, HeapNumber::kMantissaOffset));
6238 __ Ret();
6239}
6240
6241
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006242// Handle the case where the lhs and rhs are the same object.
6243// Equality is almost reflexive (everything but NaN), so this is a test
6244// for "identity and not NaN".
6245static void EmitIdenticalObjectComparison(MacroAssembler* masm,
6246 Label* slow,
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006247 Condition cc,
6248 bool never_nan_nan) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006249 Label not_identical;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006250 Label heap_number, return_equal;
6251 Register exp_mask_reg = r5;
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006252 __ cmp(r0, r1);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006253 __ b(ne, &not_identical);
6254
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006255 // The two objects are identical. If we know that one of them isn't NaN then
6256 // we now know they test equal.
6257 if (cc != eq || !never_nan_nan) {
6258 __ mov(exp_mask_reg, Operand(HeapNumber::kExponentMask));
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006259
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006260 // Test for NaN. Sadly, we can't just compare to Factory::nan_value(),
6261 // so we do the second best thing - test it ourselves.
6262 // They are both equal and they are not both Smis so both of them are not
6263 // Smis. If it's not a heap number, then return equal.
6264 if (cc == lt || cc == gt) {
6265 __ CompareObjectType(r0, r4, r4, FIRST_JS_OBJECT_TYPE);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006266 __ b(ge, slow);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006267 } else {
6268 __ CompareObjectType(r0, r4, r4, HEAP_NUMBER_TYPE);
6269 __ b(eq, &heap_number);
6270 // Comparing JS objects with <=, >= is complicated.
6271 if (cc != eq) {
6272 __ cmp(r4, Operand(FIRST_JS_OBJECT_TYPE));
6273 __ b(ge, slow);
6274 // Normally here we fall through to return_equal, but undefined is
6275 // special: (undefined == undefined) == true, but
6276 // (undefined <= undefined) == false! See ECMAScript 11.8.5.
6277 if (cc == le || cc == ge) {
6278 __ cmp(r4, Operand(ODDBALL_TYPE));
6279 __ b(ne, &return_equal);
6280 __ LoadRoot(r2, Heap::kUndefinedValueRootIndex);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006281 __ cmp(r0, r2);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006282 __ b(ne, &return_equal);
6283 if (cc == le) {
6284 // undefined <= undefined should fail.
6285 __ mov(r0, Operand(GREATER));
6286 } else {
6287 // undefined >= undefined should fail.
6288 __ mov(r0, Operand(LESS));
6289 }
6290 __ mov(pc, Operand(lr)); // Return.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006291 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006292 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006293 }
6294 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006295
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006296 __ bind(&return_equal);
6297 if (cc == lt) {
6298 __ mov(r0, Operand(GREATER)); // Things aren't less than themselves.
6299 } else if (cc == gt) {
6300 __ mov(r0, Operand(LESS)); // Things aren't greater than themselves.
6301 } else {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006302 __ mov(r0, Operand(EQUAL)); // Things are <=, >=, ==, === themselves.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006303 }
6304 __ mov(pc, Operand(lr)); // Return.
6305
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006306 if (cc != eq || !never_nan_nan) {
6307 // For less and greater we don't have to check for NaN since the result of
6308 // x < x is false regardless. For the others here is some code to check
6309 // for NaN.
6310 if (cc != lt && cc != gt) {
6311 __ bind(&heap_number);
6312 // It is a heap number, so return non-equal if it's NaN and equal if it's
6313 // not NaN.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006314
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006315 // The representation of NaN values has all exponent bits (52..62) set,
6316 // and not all mantissa bits (0..51) clear.
6317 // Read top bits of double representation (second word of value).
6318 __ ldr(r2, FieldMemOperand(r0, HeapNumber::kExponentOffset));
6319 // Test that exponent bits are all set.
6320 __ and_(r3, r2, Operand(exp_mask_reg));
6321 __ cmp(r3, Operand(exp_mask_reg));
6322 __ b(ne, &return_equal);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006323
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006324 // Shift out flag and all exponent bits, retaining only mantissa.
6325 __ mov(r2, Operand(r2, LSL, HeapNumber::kNonMantissaBitsInTopWord));
6326 // Or with all low-bits of mantissa.
6327 __ ldr(r3, FieldMemOperand(r0, HeapNumber::kMantissaOffset));
6328 __ orr(r0, r3, Operand(r2), SetCC);
6329 // For equal we already have the right value in r0: Return zero (equal)
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006330 // if all bits in mantissa are zero (it's an Infinity) and non-zero if
6331 // not (it's a NaN). For <= and >= we need to load r0 with the failing
6332 // value if it's a NaN.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006333 if (cc != eq) {
6334 // All-zero means Infinity means equal.
6335 __ mov(pc, Operand(lr), LeaveCC, eq); // Return equal
6336 if (cc == le) {
6337 __ mov(r0, Operand(GREATER)); // NaN <= NaN should fail.
6338 } else {
6339 __ mov(r0, Operand(LESS)); // NaN >= NaN should fail.
6340 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006341 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006342 __ mov(pc, Operand(lr)); // Return.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006343 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006344 // No fall through here.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006345 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006346
6347 __ bind(&not_identical);
6348}
6349
6350
6351// See comment at call site.
6352static void EmitSmiNonsmiComparison(MacroAssembler* masm,
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006353 Label* lhs_not_nan,
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006354 Label* slow,
6355 bool strict) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006356 Label rhs_is_smi;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006357 __ tst(r0, Operand(kSmiTagMask));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006358 __ b(eq, &rhs_is_smi);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006359
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006360 // Lhs is a Smi. Check whether the rhs is a heap number.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006361 __ CompareObjectType(r0, r4, r4, HEAP_NUMBER_TYPE);
6362 if (strict) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006363 // If rhs is not a number and lhs is a Smi then strict equality cannot
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006364 // succeed. Return non-equal (r0 is already not zero)
6365 __ mov(pc, Operand(lr), LeaveCC, ne); // Return.
6366 } else {
6367 // Smi compared non-strictly with a non-Smi non-heap-number. Call
6368 // the runtime.
6369 __ b(ne, slow);
6370 }
6371
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006372 // Lhs (r1) is a smi, rhs (r0) is a number.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006373 if (CpuFeatures::IsSupported(VFP3)) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006374 // Convert lhs to a double in d7 .
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006375 CpuFeatures::Scope scope(VFP3);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006376 __ mov(r7, Operand(r1, ASR, kSmiTagSize));
6377 __ vmov(s15, r7);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006378 __ vcvt_f64_s32(d7, s15);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006379 // Load the double from rhs, tagged HeapNumber r0, to d6.
6380 __ sub(r7, r0, Operand(kHeapObjectTag));
6381 __ vldr(d6, r7, HeapNumber::kValueOffset);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006382 } else {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006383 __ push(lr);
6384 // Convert lhs to a double in r2, r3.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006385 __ mov(r7, Operand(r1));
6386 ConvertToDoubleStub stub1(r3, r2, r7, r6);
6387 __ Call(stub1.GetCode(), RelocInfo::CODE_TARGET);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006388 // Load rhs to a double in r0, r1.
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00006389 __ ldrd(r0, FieldMemOperand(r0, HeapNumber::kValueOffset));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006390 __ pop(lr);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006391 }
6392
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006393 // We now have both loaded as doubles but we can skip the lhs nan check
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006394 // since it's a smi.
6395 __ jmp(lhs_not_nan);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006396
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006397 __ bind(&rhs_is_smi);
6398 // Rhs is a smi. Check whether the non-smi lhs is a heap number.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006399 __ CompareObjectType(r1, r4, r4, HEAP_NUMBER_TYPE);
6400 if (strict) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006401 // If lhs is not a number and rhs is a smi then strict equality cannot
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006402 // succeed. Return non-equal.
6403 __ mov(r0, Operand(1), LeaveCC, ne); // Non-zero indicates not equal.
6404 __ mov(pc, Operand(lr), LeaveCC, ne); // Return.
6405 } else {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006406 // Smi compared non-strictly with a non-smi non-heap-number. Call
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006407 // the runtime.
6408 __ b(ne, slow);
6409 }
6410
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006411 // Rhs (r0) is a smi, lhs (r1) is a heap number.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006412 if (CpuFeatures::IsSupported(VFP3)) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006413 // Convert rhs to a double in d6 .
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006414 CpuFeatures::Scope scope(VFP3);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006415 // Load the double from lhs, tagged HeapNumber r1, to d7.
6416 __ sub(r7, r1, Operand(kHeapObjectTag));
6417 __ vldr(d7, r7, HeapNumber::kValueOffset);
6418 __ mov(r7, Operand(r0, ASR, kSmiTagSize));
6419 __ vmov(s13, r7);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006420 __ vcvt_f64_s32(d6, s13);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006421 } else {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006422 __ push(lr);
6423 // Load lhs to a double in r2, r3.
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00006424 __ ldrd(r2, FieldMemOperand(r1, HeapNumber::kValueOffset));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006425 // Convert rhs to a double in r0, r1.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006426 __ mov(r7, Operand(r0));
6427 ConvertToDoubleStub stub2(r1, r0, r7, r6);
6428 __ Call(stub2.GetCode(), RelocInfo::CODE_TARGET);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006429 __ pop(lr);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006430 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006431 // Fall through to both_loaded_as_doubles.
6432}
6433
6434
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006435void EmitNanCheck(MacroAssembler* masm, Label* lhs_not_nan, Condition cc) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006436 bool exp_first = (HeapNumber::kExponentOffset == HeapNumber::kValueOffset);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006437 Register rhs_exponent = exp_first ? r0 : r1;
6438 Register lhs_exponent = exp_first ? r2 : r3;
6439 Register rhs_mantissa = exp_first ? r1 : r0;
6440 Register lhs_mantissa = exp_first ? r3 : r2;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006441 Label one_is_nan, neither_is_nan;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006442 Label lhs_not_nan_exp_mask_is_loaded;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006443
6444 Register exp_mask_reg = r5;
6445
6446 __ mov(exp_mask_reg, Operand(HeapNumber::kExponentMask));
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006447 __ and_(r4, lhs_exponent, Operand(exp_mask_reg));
6448 __ cmp(r4, Operand(exp_mask_reg));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006449 __ b(ne, &lhs_not_nan_exp_mask_is_loaded);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006450 __ mov(r4,
6451 Operand(lhs_exponent, LSL, HeapNumber::kNonMantissaBitsInTopWord),
6452 SetCC);
6453 __ b(ne, &one_is_nan);
6454 __ cmp(lhs_mantissa, Operand(0));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006455 __ b(ne, &one_is_nan);
6456
6457 __ bind(lhs_not_nan);
6458 __ mov(exp_mask_reg, Operand(HeapNumber::kExponentMask));
6459 __ bind(&lhs_not_nan_exp_mask_is_loaded);
6460 __ and_(r4, rhs_exponent, Operand(exp_mask_reg));
6461 __ cmp(r4, Operand(exp_mask_reg));
6462 __ b(ne, &neither_is_nan);
6463 __ mov(r4,
6464 Operand(rhs_exponent, LSL, HeapNumber::kNonMantissaBitsInTopWord),
6465 SetCC);
6466 __ b(ne, &one_is_nan);
6467 __ cmp(rhs_mantissa, Operand(0));
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006468 __ b(eq, &neither_is_nan);
6469
6470 __ bind(&one_is_nan);
6471 // NaN comparisons always fail.
6472 // Load whatever we need in r0 to make the comparison fail.
6473 if (cc == lt || cc == le) {
6474 __ mov(r0, Operand(GREATER));
6475 } else {
6476 __ mov(r0, Operand(LESS));
6477 }
6478 __ mov(pc, Operand(lr)); // Return.
6479
6480 __ bind(&neither_is_nan);
6481}
6482
6483
6484// See comment at call site.
6485static void EmitTwoNonNanDoubleComparison(MacroAssembler* masm, Condition cc) {
6486 bool exp_first = (HeapNumber::kExponentOffset == HeapNumber::kValueOffset);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006487 Register rhs_exponent = exp_first ? r0 : r1;
6488 Register lhs_exponent = exp_first ? r2 : r3;
6489 Register rhs_mantissa = exp_first ? r1 : r0;
6490 Register lhs_mantissa = exp_first ? r3 : r2;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006491
6492 // r0, r1, r2, r3 have the two doubles. Neither is a NaN.
6493 if (cc == eq) {
6494 // Doubles are not equal unless they have the same bit pattern.
6495 // Exception: 0 and -0.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006496 __ cmp(rhs_mantissa, Operand(lhs_mantissa));
6497 __ orr(r0, rhs_mantissa, Operand(lhs_mantissa), LeaveCC, ne);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006498 // Return non-zero if the numbers are unequal.
6499 __ mov(pc, Operand(lr), LeaveCC, ne);
6500
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006501 __ sub(r0, rhs_exponent, Operand(lhs_exponent), SetCC);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006502 // If exponents are equal then return 0.
6503 __ mov(pc, Operand(lr), LeaveCC, eq);
6504
6505 // Exponents are unequal. The only way we can return that the numbers
6506 // are equal is if one is -0 and the other is 0. We already dealt
6507 // with the case where both are -0 or both are 0.
6508 // We start by seeing if the mantissas (that are equal) or the bottom
6509 // 31 bits of the rhs exponent are non-zero. If so we return not
6510 // equal.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006511 __ orr(r4, lhs_mantissa, Operand(lhs_exponent, LSL, kSmiTagSize), SetCC);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006512 __ mov(r0, Operand(r4), LeaveCC, ne);
6513 __ mov(pc, Operand(lr), LeaveCC, ne); // Return conditionally.
6514 // Now they are equal if and only if the lhs exponent is zero in its
6515 // low 31 bits.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006516 __ mov(r0, Operand(rhs_exponent, LSL, kSmiTagSize));
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006517 __ mov(pc, Operand(lr));
6518 } else {
6519 // Call a native function to do a comparison between two non-NaNs.
6520 // Call C routine that may not cause GC or other trouble.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00006521 __ push(lr);
6522 __ PrepareCallCFunction(4, r5); // Two doubles count as 4 arguments.
6523 __ CallCFunction(ExternalReference::compare_doubles(), 4);
6524 __ pop(pc); // Return.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006525 }
6526}
6527
6528
6529// See comment at call site.
6530static void EmitStrictTwoHeapObjectCompare(MacroAssembler* masm) {
6531 // If either operand is a JSObject or an oddball value, then they are
6532 // not equal since their pointers are different.
6533 // There is no test for undetectability in strict equality.
6534 ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
6535 Label first_non_object;
6536 // Get the type of the first operand into r2 and compare it with
6537 // FIRST_JS_OBJECT_TYPE.
6538 __ CompareObjectType(r0, r2, r2, FIRST_JS_OBJECT_TYPE);
6539 __ b(lt, &first_non_object);
6540
6541 // Return non-zero (r0 is not zero)
6542 Label return_not_equal;
6543 __ bind(&return_not_equal);
6544 __ mov(pc, Operand(lr)); // Return.
6545
6546 __ bind(&first_non_object);
6547 // Check for oddballs: true, false, null, undefined.
6548 __ cmp(r2, Operand(ODDBALL_TYPE));
6549 __ b(eq, &return_not_equal);
6550
6551 __ CompareObjectType(r1, r3, r3, FIRST_JS_OBJECT_TYPE);
6552 __ b(ge, &return_not_equal);
6553
6554 // Check for oddballs: true, false, null, undefined.
6555 __ cmp(r3, Operand(ODDBALL_TYPE));
6556 __ b(eq, &return_not_equal);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006557
6558 // Now that we have the types we might as well check for symbol-symbol.
6559 // Ensure that no non-strings have the symbol bit set.
6560 ASSERT(kNotStringTag + kIsSymbolMask > LAST_TYPE);
6561 ASSERT(kSymbolTag != 0);
6562 __ and_(r2, r2, Operand(r3));
6563 __ tst(r2, Operand(kIsSymbolMask));
6564 __ b(ne, &return_not_equal);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006565}
6566
6567
6568// See comment at call site.
6569static void EmitCheckForTwoHeapNumbers(MacroAssembler* masm,
6570 Label* both_loaded_as_doubles,
6571 Label* not_heap_numbers,
6572 Label* slow) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006573 __ CompareObjectType(r0, r3, r2, HEAP_NUMBER_TYPE);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006574 __ b(ne, not_heap_numbers);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006575 __ ldr(r2, FieldMemOperand(r1, HeapObject::kMapOffset));
6576 __ cmp(r2, r3);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006577 __ b(ne, slow); // First was a heap number, second wasn't. Go slow case.
6578
6579 // Both are heap numbers. Load them up then jump to the code we have
6580 // for that.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006581 if (CpuFeatures::IsSupported(VFP3)) {
6582 CpuFeatures::Scope scope(VFP3);
6583 __ sub(r7, r0, Operand(kHeapObjectTag));
6584 __ vldr(d6, r7, HeapNumber::kValueOffset);
6585 __ sub(r7, r1, Operand(kHeapObjectTag));
6586 __ vldr(d7, r7, HeapNumber::kValueOffset);
6587 } else {
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00006588 __ ldrd(r2, FieldMemOperand(r1, HeapNumber::kValueOffset));
6589 __ ldrd(r0, FieldMemOperand(r0, HeapNumber::kValueOffset));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006590 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006591 __ jmp(both_loaded_as_doubles);
6592}
6593
6594
6595// Fast negative check for symbol-to-symbol equality.
6596static void EmitCheckForSymbols(MacroAssembler* masm, Label* slow) {
6597 // r2 is object type of r0.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006598 // Ensure that no non-strings have the symbol bit set.
6599 ASSERT(kNotStringTag + kIsSymbolMask > LAST_TYPE);
6600 ASSERT(kSymbolTag != 0);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006601 __ tst(r2, Operand(kIsSymbolMask));
6602 __ b(eq, slow);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006603 __ ldr(r3, FieldMemOperand(r1, HeapObject::kMapOffset));
6604 __ ldrb(r3, FieldMemOperand(r3, Map::kInstanceTypeOffset));
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006605 __ tst(r3, Operand(kIsSymbolMask));
6606 __ b(eq, slow);
6607
6608 // Both are symbols. We already checked they weren't the same pointer
6609 // so they are not equal.
6610 __ mov(r0, Operand(1)); // Non-zero indicates not equal.
6611 __ mov(pc, Operand(lr)); // Return.
6612}
6613
6614
fschneider@chromium.org086aac62010-03-17 13:18:24 +00006615void NumberToStringStub::GenerateLookupNumberStringCache(MacroAssembler* masm,
6616 Register object,
6617 Register result,
6618 Register scratch1,
6619 Register scratch2,
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006620 Register scratch3,
fschneider@chromium.org086aac62010-03-17 13:18:24 +00006621 bool object_is_smi,
6622 Label* not_found) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00006623 // Use of registers. Register result is used as a temporary.
6624 Register number_string_cache = result;
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006625 Register mask = scratch3;
fschneider@chromium.org086aac62010-03-17 13:18:24 +00006626
6627 // Load the number string cache.
6628 __ LoadRoot(number_string_cache, Heap::kNumberStringCacheRootIndex);
6629
6630 // Make the hash mask from the length of the number string cache. It
6631 // contains two elements (number and string) for each cache entry.
6632 __ ldr(mask, FieldMemOperand(number_string_cache, FixedArray::kLengthOffset));
6633 // Divide length by two (length is not a smi).
6634 __ mov(mask, Operand(mask, ASR, 1));
6635 __ sub(mask, mask, Operand(1)); // Make mask.
6636
6637 // Calculate the entry in the number string cache. The hash value in the
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006638 // number string cache for smis is just the smi value, and the hash for
6639 // doubles is the xor of the upper and lower words. See
6640 // Heap::GetNumberStringCache.
6641 Label is_smi;
6642 Label load_result_from_cache;
6643 if (!object_is_smi) {
6644 __ BranchOnSmi(object, &is_smi);
6645 if (CpuFeatures::IsSupported(VFP3)) {
6646 CpuFeatures::Scope scope(VFP3);
6647 __ CheckMap(object,
6648 scratch1,
6649 Factory::heap_number_map(),
6650 not_found,
6651 true);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00006652
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006653 ASSERT_EQ(8, kDoubleSize);
6654 __ add(scratch1,
6655 object,
6656 Operand(HeapNumber::kValueOffset - kHeapObjectTag));
6657 __ ldm(ia, scratch1, scratch1.bit() | scratch2.bit());
6658 __ eor(scratch1, scratch1, Operand(scratch2));
6659 __ and_(scratch1, scratch1, Operand(mask));
6660
6661 // Calculate address of entry in string cache: each entry consists
6662 // of two pointer sized fields.
6663 __ add(scratch1,
6664 number_string_cache,
6665 Operand(scratch1, LSL, kPointerSizeLog2 + 1));
6666
6667 Register probe = mask;
6668 __ ldr(probe,
6669 FieldMemOperand(scratch1, FixedArray::kHeaderSize));
6670 __ BranchOnSmi(probe, not_found);
6671 __ sub(scratch2, object, Operand(kHeapObjectTag));
6672 __ vldr(d0, scratch2, HeapNumber::kValueOffset);
6673 __ sub(probe, probe, Operand(kHeapObjectTag));
6674 __ vldr(d1, probe, HeapNumber::kValueOffset);
6675 __ vcmp(d0, d1);
6676 __ vmrs(pc);
6677 __ b(ne, not_found); // The cache did not contain this value.
6678 __ b(&load_result_from_cache);
6679 } else {
6680 __ b(not_found);
6681 }
6682 }
6683
6684 __ bind(&is_smi);
6685 Register scratch = scratch1;
6686 __ and_(scratch, mask, Operand(object, ASR, 1));
fschneider@chromium.org086aac62010-03-17 13:18:24 +00006687 // Calculate address of entry in string cache: each entry consists
6688 // of two pointer sized fields.
6689 __ add(scratch,
6690 number_string_cache,
6691 Operand(scratch, LSL, kPointerSizeLog2 + 1));
6692
6693 // Check if the entry is the smi we are looking for.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006694 Register probe = mask;
6695 __ ldr(probe, FieldMemOperand(scratch, FixedArray::kHeaderSize));
6696 __ cmp(object, probe);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00006697 __ b(ne, not_found);
6698
6699 // Get the result from the cache.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006700 __ bind(&load_result_from_cache);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00006701 __ ldr(result,
6702 FieldMemOperand(scratch, FixedArray::kHeaderSize + kPointerSize));
fschneider@chromium.org086aac62010-03-17 13:18:24 +00006703 __ IncrementCounter(&Counters::number_to_string_native,
6704 1,
6705 scratch1,
6706 scratch2);
6707}
6708
6709
6710void NumberToStringStub::Generate(MacroAssembler* masm) {
6711 Label runtime;
6712
6713 __ ldr(r1, MemOperand(sp, 0));
6714
6715 // Generate code to lookup number in the number string cache.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006716 GenerateLookupNumberStringCache(masm, r1, r0, r2, r3, r4, false, &runtime);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00006717 __ add(sp, sp, Operand(1 * kPointerSize));
6718 __ Ret();
6719
6720 __ bind(&runtime);
6721 // Handle number to string in the runtime system if not found in the cache.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006722 __ TailCallRuntime(Runtime::kNumberToStringSkipCache, 1, 1);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00006723}
6724
6725
ager@chromium.orgac091b72010-05-05 07:34:42 +00006726void RecordWriteStub::Generate(MacroAssembler* masm) {
6727 __ RecordWriteHelper(object_, offset_, scratch_);
6728 __ Ret();
6729}
6730
6731
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006732// On entry r0 (rhs) and r1 (lhs) are the values to be compared.
6733// On exit r0 is 0, positive or negative to indicate the result of
6734// the comparison.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006735void CompareStub::Generate(MacroAssembler* masm) {
6736 Label slow; // Call builtin.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006737 Label not_smis, both_loaded_as_doubles, lhs_not_nan;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006738
6739 // NOTICE! This code is only reached after a smi-fast-case check, so
6740 // it is certain that at least one operand isn't a smi.
6741
6742 // Handle the case where the objects are identical. Either returns the answer
6743 // or goes to slow. Only falls through if the objects were not identical.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006744 EmitIdenticalObjectComparison(masm, &slow, cc_, never_nan_nan_);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006745
6746 // If either is a Smi (we know that not both are), then they can only
6747 // be strictly equal if the other is a HeapNumber.
6748 ASSERT_EQ(0, kSmiTag);
6749 ASSERT_EQ(0, Smi::FromInt(0));
6750 __ and_(r2, r0, Operand(r1));
6751 __ tst(r2, Operand(kSmiTagMask));
6752 __ b(ne, &not_smis);
6753 // One operand is a smi. EmitSmiNonsmiComparison generates code that can:
6754 // 1) Return the answer.
6755 // 2) Go to slow.
6756 // 3) Fall through to both_loaded_as_doubles.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006757 // 4) Jump to lhs_not_nan.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006758 // In cases 3 and 4 we have found out we were dealing with a number-number
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006759 // comparison. If VFP3 is supported the double values of the numbers have
6760 // been loaded into d7 and d6. Otherwise, the double values have been loaded
6761 // into r0, r1, r2, and r3.
6762 EmitSmiNonsmiComparison(masm, &lhs_not_nan, &slow, strict_);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006763
6764 __ bind(&both_loaded_as_doubles);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006765 // The arguments have been converted to doubles and stored in d6 and d7, if
6766 // VFP3 is supported, or in r0, r1, r2, and r3.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006767 if (CpuFeatures::IsSupported(VFP3)) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006768 __ bind(&lhs_not_nan);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006769 CpuFeatures::Scope scope(VFP3);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006770 Label no_nan;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006771 // ARMv7 VFP3 instructions to implement double precision comparison.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006772 __ vcmp(d7, d6);
6773 __ vmrs(pc); // Move vector status bits to normal status bits.
6774 Label nan;
6775 __ b(vs, &nan);
6776 __ mov(r0, Operand(EQUAL), LeaveCC, eq);
6777 __ mov(r0, Operand(LESS), LeaveCC, lt);
6778 __ mov(r0, Operand(GREATER), LeaveCC, gt);
6779 __ mov(pc, Operand(lr));
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006780
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006781 __ bind(&nan);
6782 // If one of the sides was a NaN then the v flag is set. Load r0 with
6783 // whatever it takes to make the comparison fail, since comparisons with NaN
6784 // always fail.
6785 if (cc_ == lt || cc_ == le) {
6786 __ mov(r0, Operand(GREATER));
6787 } else {
6788 __ mov(r0, Operand(LESS));
6789 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006790 __ mov(pc, Operand(lr));
6791 } else {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006792 // Checks for NaN in the doubles we have loaded. Can return the answer or
6793 // fall through if neither is a NaN. Also binds lhs_not_nan.
6794 EmitNanCheck(masm, &lhs_not_nan, cc_);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006795 // Compares two doubles in r0, r1, r2, r3 that are not NaNs. Returns the
6796 // answer. Never falls through.
6797 EmitTwoNonNanDoubleComparison(masm, cc_);
6798 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006799
6800 __ bind(&not_smis);
6801 // At this point we know we are dealing with two different objects,
6802 // and neither of them is a Smi. The objects are in r0 and r1.
6803 if (strict_) {
6804 // This returns non-equal for some object types, or falls through if it
6805 // was not lucky.
6806 EmitStrictTwoHeapObjectCompare(masm);
6807 }
6808
6809 Label check_for_symbols;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006810 Label flat_string_check;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006811 // Check for heap-number-heap-number comparison. Can jump to slow case,
6812 // or load both doubles into r0, r1, r2, r3 and jump to the code that handles
6813 // that case. If the inputs are not doubles then jumps to check_for_symbols.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006814 // In this case r2 will contain the type of r0. Never falls through.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006815 EmitCheckForTwoHeapNumbers(masm,
6816 &both_loaded_as_doubles,
6817 &check_for_symbols,
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006818 &flat_string_check);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006819
6820 __ bind(&check_for_symbols);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006821 // In the strict case the EmitStrictTwoHeapObjectCompare already took care of
6822 // symbols.
6823 if (cc_ == eq && !strict_) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006824 // Either jumps to slow or returns the answer. Assumes that r2 is the type
6825 // of r0 on entry.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006826 EmitCheckForSymbols(masm, &flat_string_check);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006827 }
6828
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006829 // Check for both being sequential ASCII strings, and inline if that is the
6830 // case.
6831 __ bind(&flat_string_check);
6832
6833 __ JumpIfNonSmisNotBothSequentialAsciiStrings(r0, r1, r2, r3, &slow);
6834
6835 __ IncrementCounter(&Counters::string_compare_native, 1, r2, r3);
6836 StringCompareStub::GenerateCompareFlatAsciiStrings(masm,
6837 r1,
6838 r0,
6839 r2,
6840 r3,
6841 r4,
6842 r5);
6843 // Never falls through to here.
6844
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006845 __ bind(&slow);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006846
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006847 __ Push(r1, r0);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006848 // Figure out which native to call and setup the arguments.
6849 Builtins::JavaScript native;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006850 if (cc_ == eq) {
6851 native = strict_ ? Builtins::STRICT_EQUALS : Builtins::EQUALS;
6852 } else {
6853 native = Builtins::COMPARE;
6854 int ncr; // NaN compare result
6855 if (cc_ == lt || cc_ == le) {
6856 ncr = GREATER;
6857 } else {
6858 ASSERT(cc_ == gt || cc_ == ge); // remaining cases
6859 ncr = LESS;
6860 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006861 __ mov(r0, Operand(Smi::FromInt(ncr)));
6862 __ push(r0);
6863 }
6864
6865 // Call the native; it returns -1 (less), 0 (equal), or 1 (greater)
6866 // tagged as a small integer.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006867 __ InvokeBuiltin(native, JUMP_JS);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006868}
6869
6870
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00006871// We fall into this code if the operands were Smis, but the result was
6872// not (eg. overflow). We branch into this code (to the not_smi label) if
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006873// the operands were not both Smi. The operands are in r0 and r1. In order
6874// to call the C-implemented binary fp operation routines we need to end up
6875// with the double precision floating point operands in r0 and r1 (for the
6876// value in r1) and r2 and r3 (for the value in r0).
ager@chromium.org357bf652010-04-12 11:30:10 +00006877void GenericBinaryOpStub::HandleBinaryOpSlowCases(
6878 MacroAssembler* masm,
6879 Label* not_smi,
6880 Register lhs,
6881 Register rhs,
6882 const Builtins::JavaScript& builtin) {
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006883 Label slow, slow_reverse, do_the_call;
ager@chromium.org357bf652010-04-12 11:30:10 +00006884 bool use_fp_registers = CpuFeatures::IsSupported(VFP3) && Token::MOD != op_;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006885
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00006886 ASSERT((lhs.is(r0) && rhs.is(r1)) || (lhs.is(r1) && rhs.is(r0)));
ager@chromium.org357bf652010-04-12 11:30:10 +00006887
6888 if (ShouldGenerateSmiCode()) {
6889 // Smi-smi case (overflow).
6890 // Since both are Smis there is no heap number to overwrite, so allocate.
6891 // The new heap number is in r5. r6 and r7 are scratch.
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006892 __ AllocateHeapNumber(r5, r6, r7, lhs.is(r0) ? &slow_reverse : &slow);
ager@chromium.org357bf652010-04-12 11:30:10 +00006893
6894 // If we have floating point hardware, inline ADD, SUB, MUL, and DIV,
6895 // using registers d7 and d6 for the double values.
6896 if (use_fp_registers) {
6897 CpuFeatures::Scope scope(VFP3);
6898 __ mov(r7, Operand(rhs, ASR, kSmiTagSize));
6899 __ vmov(s15, r7);
6900 __ vcvt_f64_s32(d7, s15);
6901 __ mov(r7, Operand(lhs, ASR, kSmiTagSize));
6902 __ vmov(s13, r7);
6903 __ vcvt_f64_s32(d6, s13);
6904 } else {
6905 // Write Smi from rhs to r3 and r2 in double format. r6 is scratch.
6906 __ mov(r7, Operand(rhs));
6907 ConvertToDoubleStub stub1(r3, r2, r7, r6);
6908 __ push(lr);
6909 __ Call(stub1.GetCode(), RelocInfo::CODE_TARGET);
6910 // Write Smi from lhs to r1 and r0 in double format. r6 is scratch.
6911 __ mov(r7, Operand(lhs));
6912 ConvertToDoubleStub stub2(r1, r0, r7, r6);
6913 __ Call(stub2.GetCode(), RelocInfo::CODE_TARGET);
6914 __ pop(lr);
6915 }
6916 __ jmp(&do_the_call); // Tail call. No return.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006917 }
6918
ager@chromium.org357bf652010-04-12 11:30:10 +00006919 // We branch here if at least one of r0 and r1 is not a Smi.
6920 __ bind(not_smi);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006921
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006922 // After this point we have the left hand side in r1 and the right hand side
6923 // in r0.
ager@chromium.org357bf652010-04-12 11:30:10 +00006924 if (lhs.is(r0)) {
6925 __ Swap(r0, r1, ip);
6926 }
6927
6928 if (ShouldGenerateFPCode()) {
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006929 Label r0_is_smi, r1_is_smi, finished_loading_r0, finished_loading_r1;
6930
ager@chromium.org357bf652010-04-12 11:30:10 +00006931 if (runtime_operands_type_ == BinaryOpIC::DEFAULT) {
6932 switch (op_) {
6933 case Token::ADD:
6934 case Token::SUB:
6935 case Token::MUL:
6936 case Token::DIV:
6937 GenerateTypeTransition(masm);
6938 break;
6939
6940 default:
6941 break;
6942 }
6943 }
6944
6945 if (mode_ == NO_OVERWRITE) {
6946 // In the case where there is no chance of an overwritable float we may as
6947 // well do the allocation immediately while r0 and r1 are untouched.
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006948 __ AllocateHeapNumber(r5, r6, r7, &slow);
ager@chromium.org357bf652010-04-12 11:30:10 +00006949 }
6950
6951 // Move r0 to a double in r2-r3.
6952 __ tst(r0, Operand(kSmiTagMask));
6953 __ b(eq, &r0_is_smi); // It's a Smi so don't check it's a heap number.
6954 __ CompareObjectType(r0, r4, r4, HEAP_NUMBER_TYPE);
6955 __ b(ne, &slow);
6956 if (mode_ == OVERWRITE_RIGHT) {
6957 __ mov(r5, Operand(r0)); // Overwrite this heap number.
6958 }
6959 if (use_fp_registers) {
6960 CpuFeatures::Scope scope(VFP3);
6961 // Load the double from tagged HeapNumber r0 to d7.
6962 __ sub(r7, r0, Operand(kHeapObjectTag));
6963 __ vldr(d7, r7, HeapNumber::kValueOffset);
6964 } else {
6965 // Calling convention says that second double is in r2 and r3.
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00006966 __ ldrd(r2, FieldMemOperand(r0, HeapNumber::kValueOffset));
ager@chromium.org357bf652010-04-12 11:30:10 +00006967 }
6968 __ jmp(&finished_loading_r0);
6969 __ bind(&r0_is_smi);
6970 if (mode_ == OVERWRITE_RIGHT) {
6971 // We can't overwrite a Smi so get address of new heap number into r5.
6972 __ AllocateHeapNumber(r5, r6, r7, &slow);
6973 }
6974
6975 if (use_fp_registers) {
6976 CpuFeatures::Scope scope(VFP3);
6977 // Convert smi in r0 to double in d7.
6978 __ mov(r7, Operand(r0, ASR, kSmiTagSize));
6979 __ vmov(s15, r7);
6980 __ vcvt_f64_s32(d7, s15);
6981 } else {
6982 // Write Smi from r0 to r3 and r2 in double format.
6983 __ mov(r7, Operand(r0));
6984 ConvertToDoubleStub stub3(r3, r2, r7, r6);
6985 __ push(lr);
6986 __ Call(stub3.GetCode(), RelocInfo::CODE_TARGET);
6987 __ pop(lr);
6988 }
6989
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006990 // HEAP_NUMBERS stub is slower than GENERIC on a pair of smis.
6991 // r0 is known to be a smi. If r1 is also a smi then switch to GENERIC.
6992 Label r1_is_not_smi;
6993 if (runtime_operands_type_ == BinaryOpIC::HEAP_NUMBERS) {
6994 __ tst(r1, Operand(kSmiTagMask));
6995 __ b(ne, &r1_is_not_smi);
6996 GenerateTypeTransition(masm);
6997 __ jmp(&r1_is_smi);
6998 }
6999
ager@chromium.org357bf652010-04-12 11:30:10 +00007000 __ bind(&finished_loading_r0);
7001
7002 // Move r1 to a double in r0-r1.
7003 __ tst(r1, Operand(kSmiTagMask));
7004 __ 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 +00007005 __ bind(&r1_is_not_smi);
ager@chromium.org357bf652010-04-12 11:30:10 +00007006 __ CompareObjectType(r1, r4, r4, HEAP_NUMBER_TYPE);
7007 __ b(ne, &slow);
7008 if (mode_ == OVERWRITE_LEFT) {
7009 __ mov(r5, Operand(r1)); // Overwrite this heap number.
7010 }
7011 if (use_fp_registers) {
7012 CpuFeatures::Scope scope(VFP3);
7013 // Load the double from tagged HeapNumber r1 to d6.
7014 __ sub(r7, r1, Operand(kHeapObjectTag));
7015 __ vldr(d6, r7, HeapNumber::kValueOffset);
7016 } else {
7017 // Calling convention says that first double is in r0 and r1.
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00007018 __ ldrd(r0, FieldMemOperand(r1, HeapNumber::kValueOffset));
ager@chromium.org357bf652010-04-12 11:30:10 +00007019 }
7020 __ jmp(&finished_loading_r1);
7021 __ bind(&r1_is_smi);
7022 if (mode_ == OVERWRITE_LEFT) {
7023 // We can't overwrite a Smi so get address of new heap number into r5.
7024 __ AllocateHeapNumber(r5, r6, r7, &slow);
7025 }
7026
7027 if (use_fp_registers) {
7028 CpuFeatures::Scope scope(VFP3);
7029 // Convert smi in r1 to double in d6.
7030 __ mov(r7, Operand(r1, ASR, kSmiTagSize));
7031 __ vmov(s13, r7);
7032 __ vcvt_f64_s32(d6, s13);
7033 } else {
7034 // Write Smi from r1 to r1 and r0 in double format.
7035 __ mov(r7, Operand(r1));
7036 ConvertToDoubleStub stub4(r1, r0, r7, r6);
7037 __ push(lr);
7038 __ Call(stub4.GetCode(), RelocInfo::CODE_TARGET);
7039 __ pop(lr);
7040 }
7041
7042 __ bind(&finished_loading_r1);
7043
7044 __ bind(&do_the_call);
7045 // If we are inlining the operation using VFP3 instructions for
7046 // add, subtract, multiply, or divide, the arguments are in d6 and d7.
7047 if (use_fp_registers) {
7048 CpuFeatures::Scope scope(VFP3);
7049 // ARMv7 VFP3 instructions to implement
7050 // double precision, add, subtract, multiply, divide.
7051
7052 if (Token::MUL == op_) {
7053 __ vmul(d5, d6, d7);
7054 } else if (Token::DIV == op_) {
7055 __ vdiv(d5, d6, d7);
7056 } else if (Token::ADD == op_) {
7057 __ vadd(d5, d6, d7);
7058 } else if (Token::SUB == op_) {
7059 __ vsub(d5, d6, d7);
7060 } else {
7061 UNREACHABLE();
7062 }
7063 __ sub(r0, r5, Operand(kHeapObjectTag));
7064 __ vstr(d5, r0, HeapNumber::kValueOffset);
7065 __ add(r0, r0, Operand(kHeapObjectTag));
7066 __ mov(pc, lr);
7067 } else {
7068 // If we did not inline the operation, then the arguments are in:
7069 // r0: Left value (least significant part of mantissa).
7070 // r1: Left value (sign, exponent, top of mantissa).
7071 // r2: Right value (least significant part of mantissa).
7072 // r3: Right value (sign, exponent, top of mantissa).
7073 // r5: Address of heap number for result.
7074
7075 __ push(lr); // For later.
7076 __ PrepareCallCFunction(4, r4); // Two doubles count as 4 arguments.
7077 // Call C routine that may not cause GC or other trouble. r5 is callee
7078 // save.
7079 __ CallCFunction(ExternalReference::double_fp_operation(op_), 4);
7080 // Store answer in the overwritable heap number.
7081 #if !defined(USE_ARM_EABI)
7082 // Double returned in fp coprocessor register 0 and 1, encoded as register
7083 // cr8. Offsets must be divisible by 4 for coprocessor so we need to
7084 // substract the tag from r5.
7085 __ sub(r4, r5, Operand(kHeapObjectTag));
7086 __ stc(p1, cr8, MemOperand(r4, HeapNumber::kValueOffset));
7087 #else
7088 // Double returned in registers 0 and 1.
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00007089 __ strd(r0, FieldMemOperand(r5, HeapNumber::kValueOffset));
ager@chromium.org357bf652010-04-12 11:30:10 +00007090 #endif
7091 __ mov(r0, Operand(r5));
7092 // And we are done.
7093 __ pop(pc);
7094 }
7095 }
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00007096
7097
7098 if (lhs.is(r0)) {
7099 __ b(&slow);
7100 __ bind(&slow_reverse);
7101 __ Swap(r0, r1, ip);
7102 }
7103
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007104 // We jump to here if something goes wrong (one param is not a number of any
7105 // sort or new-space allocation fails).
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007106 __ bind(&slow);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007107
7108 // Push arguments to the stack
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00007109 __ Push(r1, r0);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007110
ager@chromium.org357bf652010-04-12 11:30:10 +00007111 if (Token::ADD == op_) {
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007112 // Test for string arguments before calling runtime.
7113 // r1 : first argument
7114 // r0 : second argument
7115 // sp[0] : second argument
ager@chromium.org5c838252010-02-19 08:53:10 +00007116 // sp[4] : first argument
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007117
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007118 Label not_strings, not_string1, string1, string1_smi2;
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007119 __ tst(r1, Operand(kSmiTagMask));
7120 __ b(eq, &not_string1);
7121 __ CompareObjectType(r1, r2, r2, FIRST_NONSTRING_TYPE);
7122 __ b(ge, &not_string1);
7123
7124 // First argument is a a string, test second.
7125 __ tst(r0, Operand(kSmiTagMask));
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007126 __ b(eq, &string1_smi2);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007127 __ CompareObjectType(r0, r2, r2, FIRST_NONSTRING_TYPE);
7128 __ b(ge, &string1);
7129
7130 // First and second argument are strings.
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007131 StringAddStub string_add_stub(NO_STRING_CHECK_IN_STUB);
7132 __ TailCallStub(&string_add_stub);
7133
7134 __ bind(&string1_smi2);
7135 // First argument is a string, second is a smi. Try to lookup the number
7136 // string for the smi in the number string cache.
7137 NumberToStringStub::GenerateLookupNumberStringCache(
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00007138 masm, r0, r2, r4, r5, r6, true, &string1);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007139
7140 // Replace second argument on stack and tailcall string add stub to make
7141 // the result.
7142 __ str(r2, MemOperand(sp, 0));
7143 __ TailCallStub(&string_add_stub);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007144
7145 // Only first argument is a string.
7146 __ bind(&string1);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007147 __ InvokeBuiltin(Builtins::STRING_ADD_LEFT, JUMP_JS);
7148
7149 // First argument was not a string, test second.
7150 __ bind(&not_string1);
7151 __ tst(r0, Operand(kSmiTagMask));
7152 __ b(eq, &not_strings);
7153 __ CompareObjectType(r0, r2, r2, FIRST_NONSTRING_TYPE);
7154 __ b(ge, &not_strings);
7155
7156 // Only second argument is a string.
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007157 __ InvokeBuiltin(Builtins::STRING_ADD_RIGHT, JUMP_JS);
7158
7159 __ bind(&not_strings);
7160 }
7161
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007162 __ InvokeBuiltin(builtin, JUMP_JS); // Tail call. No return.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007163}
7164
7165
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007166// Tries to get a signed int32 out of a double precision floating point heap
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007167// number. Rounds towards 0. Fastest for doubles that are in the ranges
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007168// -0x7fffffff to -0x40000000 or 0x40000000 to 0x7fffffff. This corresponds
7169// almost to the range of signed int32 values that are not Smis. Jumps to the
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007170// label 'slow' if the double isn't in the range -0x80000000.0 to 0x80000000.0
7171// (excluding the endpoints).
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007172static void GetInt32(MacroAssembler* masm,
7173 Register source,
7174 Register dest,
7175 Register scratch,
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007176 Register scratch2,
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007177 Label* slow) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007178 Label right_exponent, done;
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007179 // Get exponent word.
7180 __ ldr(scratch, FieldMemOperand(source, HeapNumber::kExponentOffset));
7181 // Get exponent alone in scratch2.
7182 __ and_(scratch2, scratch, Operand(HeapNumber::kExponentMask));
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007183 // Load dest with zero. We use this either for the final shift or
7184 // for the answer.
7185 __ mov(dest, Operand(0));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007186 // Check whether the exponent matches a 32 bit signed int that is not a Smi.
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007187 // A non-Smi integer is 1.xxx * 2^30 so the exponent is 30 (biased). This is
7188 // the exponent that we are fastest at and also the highest exponent we can
7189 // handle here.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007190 const uint32_t non_smi_exponent =
7191 (HeapNumber::kExponentBias + 30) << HeapNumber::kExponentShift;
7192 __ cmp(scratch2, Operand(non_smi_exponent));
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007193 // If we have a match of the int32-but-not-Smi exponent then skip some logic.
7194 __ b(eq, &right_exponent);
7195 // If the exponent is higher than that then go to slow case. This catches
7196 // numbers that don't fit in a signed int32, infinities and NaNs.
7197 __ b(gt, slow);
7198
7199 // We know the exponent is smaller than 30 (biased). If it is less than
7200 // 0 (biased) then the number is smaller in magnitude than 1.0 * 2^0, ie
7201 // it rounds to zero.
7202 const uint32_t zero_exponent =
7203 (HeapNumber::kExponentBias + 0) << HeapNumber::kExponentShift;
7204 __ sub(scratch2, scratch2, Operand(zero_exponent), SetCC);
7205 // Dest already has a Smi zero.
7206 __ b(lt, &done);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007207 if (!CpuFeatures::IsSupported(VFP3)) {
7208 // We have a shifted exponent between 0 and 30 in scratch2.
7209 __ mov(dest, Operand(scratch2, LSR, HeapNumber::kExponentShift));
7210 // We now have the exponent in dest. Subtract from 30 to get
7211 // how much to shift down.
7212 __ rsb(dest, dest, Operand(30));
7213 }
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007214 __ bind(&right_exponent);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007215 if (CpuFeatures::IsSupported(VFP3)) {
7216 CpuFeatures::Scope scope(VFP3);
7217 // ARMv7 VFP3 instructions implementing double precision to integer
7218 // conversion using round to zero.
7219 __ ldr(scratch2, FieldMemOperand(source, HeapNumber::kMantissaOffset));
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00007220 __ vmov(d7, scratch2, scratch);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007221 __ vcvt_s32_f64(s15, d7);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00007222 __ vmov(dest, s15);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007223 } else {
7224 // Get the top bits of the mantissa.
7225 __ and_(scratch2, scratch, Operand(HeapNumber::kMantissaMask));
7226 // Put back the implicit 1.
7227 __ orr(scratch2, scratch2, Operand(1 << HeapNumber::kExponentShift));
7228 // Shift up the mantissa bits to take up the space the exponent used to
7229 // take. We just orred in the implicit bit so that took care of one and
7230 // we want to leave the sign bit 0 so we subtract 2 bits from the shift
7231 // distance.
7232 const int shift_distance = HeapNumber::kNonMantissaBitsInTopWord - 2;
7233 __ mov(scratch2, Operand(scratch2, LSL, shift_distance));
7234 // Put sign in zero flag.
7235 __ tst(scratch, Operand(HeapNumber::kSignMask));
7236 // Get the second half of the double. For some exponents we don't
7237 // actually need this because the bits get shifted out again, but
7238 // it's probably slower to test than just to do it.
7239 __ ldr(scratch, FieldMemOperand(source, HeapNumber::kMantissaOffset));
7240 // Shift down 22 bits to get the last 10 bits.
7241 __ orr(scratch, scratch2, Operand(scratch, LSR, 32 - shift_distance));
7242 // Move down according to the exponent.
7243 __ mov(dest, Operand(scratch, LSR, dest));
7244 // Fix sign if sign bit was set.
7245 __ rsb(dest, dest, Operand(0), LeaveCC, ne);
7246 }
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007247 __ bind(&done);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007248}
7249
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007250// For bitwise ops where the inputs are not both Smis we here try to determine
7251// whether both inputs are either Smis or at least heap numbers that can be
7252// represented by a 32 bit signed value. We truncate towards zero as required
7253// by the ES spec. If this is the case we do the bitwise op and see if the
7254// result is a Smi. If so, great, otherwise we try to find a heap number to
7255// write the answer into (either by allocating or by overwriting).
ager@chromium.org357bf652010-04-12 11:30:10 +00007256// On entry the operands are in lhs and rhs. On exit the answer is in r0.
7257void GenericBinaryOpStub::HandleNonSmiBitwiseOp(MacroAssembler* masm,
7258 Register lhs,
7259 Register rhs) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007260 Label slow, result_not_a_smi;
ager@chromium.org357bf652010-04-12 11:30:10 +00007261 Label rhs_is_smi, lhs_is_smi;
7262 Label done_checking_rhs, done_checking_lhs;
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007263
ager@chromium.org357bf652010-04-12 11:30:10 +00007264 __ tst(lhs, Operand(kSmiTagMask));
7265 __ b(eq, &lhs_is_smi); // It's a Smi so don't check it's a heap number.
7266 __ CompareObjectType(lhs, r4, r4, HEAP_NUMBER_TYPE);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007267 __ b(ne, &slow);
ager@chromium.org357bf652010-04-12 11:30:10 +00007268 GetInt32(masm, lhs, r3, r5, r4, &slow);
7269 __ jmp(&done_checking_lhs);
7270 __ bind(&lhs_is_smi);
7271 __ mov(r3, Operand(lhs, ASR, 1));
7272 __ bind(&done_checking_lhs);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007273
ager@chromium.org357bf652010-04-12 11:30:10 +00007274 __ tst(rhs, Operand(kSmiTagMask));
7275 __ b(eq, &rhs_is_smi); // It's a Smi so don't check it's a heap number.
7276 __ CompareObjectType(rhs, r4, r4, HEAP_NUMBER_TYPE);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007277 __ b(ne, &slow);
ager@chromium.org357bf652010-04-12 11:30:10 +00007278 GetInt32(masm, rhs, r2, r5, r4, &slow);
7279 __ jmp(&done_checking_rhs);
7280 __ bind(&rhs_is_smi);
7281 __ mov(r2, Operand(rhs, ASR, 1));
7282 __ bind(&done_checking_rhs);
7283
7284 ASSERT(((lhs.is(r0) && rhs.is(r1)) || (lhs.is(r1) && rhs.is(r0))));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007285
7286 // r0 and r1: Original operands (Smi or heap numbers).
7287 // r2 and r3: Signed int32 operands.
7288 switch (op_) {
7289 case Token::BIT_OR: __ orr(r2, r2, Operand(r3)); break;
7290 case Token::BIT_XOR: __ eor(r2, r2, Operand(r3)); break;
7291 case Token::BIT_AND: __ and_(r2, r2, Operand(r3)); break;
7292 case Token::SAR:
7293 // Use only the 5 least significant bits of the shift count.
7294 __ and_(r2, r2, Operand(0x1f));
7295 __ mov(r2, Operand(r3, ASR, r2));
7296 break;
7297 case Token::SHR:
7298 // Use only the 5 least significant bits of the shift count.
7299 __ and_(r2, r2, Operand(0x1f));
7300 __ mov(r2, Operand(r3, LSR, r2), SetCC);
7301 // SHR is special because it is required to produce a positive answer.
7302 // The code below for writing into heap numbers isn't capable of writing
7303 // the register as an unsigned int so we go to slow case if we hit this
7304 // case.
7305 __ b(mi, &slow);
7306 break;
7307 case Token::SHL:
7308 // Use only the 5 least significant bits of the shift count.
7309 __ and_(r2, r2, Operand(0x1f));
7310 __ mov(r2, Operand(r3, LSL, r2));
7311 break;
7312 default: UNREACHABLE();
7313 }
7314 // check that the *signed* result fits in a smi
7315 __ add(r3, r2, Operand(0x40000000), SetCC);
7316 __ b(mi, &result_not_a_smi);
7317 __ mov(r0, Operand(r2, LSL, kSmiTagSize));
7318 __ Ret();
7319
7320 Label have_to_allocate, got_a_heap_number;
7321 __ bind(&result_not_a_smi);
7322 switch (mode_) {
7323 case OVERWRITE_RIGHT: {
ager@chromium.org357bf652010-04-12 11:30:10 +00007324 __ tst(rhs, Operand(kSmiTagMask));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007325 __ b(eq, &have_to_allocate);
ager@chromium.org357bf652010-04-12 11:30:10 +00007326 __ mov(r5, Operand(rhs));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007327 break;
7328 }
7329 case OVERWRITE_LEFT: {
ager@chromium.org357bf652010-04-12 11:30:10 +00007330 __ tst(lhs, Operand(kSmiTagMask));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007331 __ b(eq, &have_to_allocate);
ager@chromium.org357bf652010-04-12 11:30:10 +00007332 __ mov(r5, Operand(lhs));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007333 break;
7334 }
7335 case NO_OVERWRITE: {
7336 // Get a new heap number in r5. r6 and r7 are scratch.
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007337 __ AllocateHeapNumber(r5, r6, r7, &slow);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007338 }
7339 default: break;
7340 }
7341 __ bind(&got_a_heap_number);
7342 // r2: Answer as signed int32.
7343 // r5: Heap number to write answer into.
7344
7345 // Nothing can go wrong now, so move the heap number to r0, which is the
7346 // result.
7347 __ mov(r0, Operand(r5));
7348
7349 // Tail call that writes the int32 in r2 to the heap number in r0, using
7350 // r3 as scratch. r0 is preserved and returned.
7351 WriteInt32ToHeapNumberStub stub(r2, r0, r3);
7352 __ Jump(stub.GetCode(), RelocInfo::CODE_TARGET);
7353
7354 if (mode_ != NO_OVERWRITE) {
7355 __ bind(&have_to_allocate);
7356 // Get a new heap number in r5. r6 and r7 are scratch.
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007357 __ AllocateHeapNumber(r5, r6, r7, &slow);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007358 __ jmp(&got_a_heap_number);
7359 }
7360
7361 // If all else failed then we go to the runtime system.
7362 __ bind(&slow);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00007363 __ Push(lhs, rhs); // Restore stack.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007364 switch (op_) {
7365 case Token::BIT_OR:
7366 __ InvokeBuiltin(Builtins::BIT_OR, JUMP_JS);
7367 break;
7368 case Token::BIT_AND:
7369 __ InvokeBuiltin(Builtins::BIT_AND, JUMP_JS);
7370 break;
7371 case Token::BIT_XOR:
7372 __ InvokeBuiltin(Builtins::BIT_XOR, JUMP_JS);
7373 break;
7374 case Token::SAR:
7375 __ InvokeBuiltin(Builtins::SAR, JUMP_JS);
7376 break;
7377 case Token::SHR:
7378 __ InvokeBuiltin(Builtins::SHR, JUMP_JS);
7379 break;
7380 case Token::SHL:
7381 __ InvokeBuiltin(Builtins::SHL, JUMP_JS);
7382 break;
7383 default:
7384 UNREACHABLE();
7385 }
7386}
7387
7388
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007389// Can we multiply by x with max two shifts and an add.
7390// This answers yes to all integers from 2 to 10.
7391static bool IsEasyToMultiplyBy(int x) {
7392 if (x < 2) return false; // Avoid special cases.
7393 if (x > (Smi::kMaxValue + 1) >> 2) return false; // Almost always overflows.
7394 if (IsPowerOf2(x)) return true; // Simple shift.
7395 if (PopCountLessThanEqual2(x)) return true; // Shift and add and shift.
7396 if (IsPowerOf2(x + 1)) return true; // Patterns like 11111.
7397 return false;
7398}
7399
7400
7401// Can multiply by anything that IsEasyToMultiplyBy returns true for.
7402// Source and destination may be the same register. This routine does
7403// not set carry and overflow the way a mul instruction would.
7404static void MultiplyByKnownInt(MacroAssembler* masm,
7405 Register source,
7406 Register destination,
7407 int known_int) {
7408 if (IsPowerOf2(known_int)) {
7409 __ mov(destination, Operand(source, LSL, BitPosition(known_int)));
7410 } else if (PopCountLessThanEqual2(known_int)) {
7411 int first_bit = BitPosition(known_int);
7412 int second_bit = BitPosition(known_int ^ (1 << first_bit));
7413 __ add(destination, source, Operand(source, LSL, second_bit - first_bit));
7414 if (first_bit != 0) {
7415 __ mov(destination, Operand(destination, LSL, first_bit));
7416 }
7417 } else {
7418 ASSERT(IsPowerOf2(known_int + 1)); // Patterns like 1111.
7419 int the_bit = BitPosition(known_int + 1);
7420 __ rsb(destination, source, Operand(source, LSL, the_bit));
7421 }
7422}
7423
7424
7425// This function (as opposed to MultiplyByKnownInt) takes the known int in a
7426// a register for the cases where it doesn't know a good trick, and may deliver
7427// a result that needs shifting.
7428static void MultiplyByKnownInt2(
7429 MacroAssembler* masm,
7430 Register result,
7431 Register source,
7432 Register known_int_register, // Smi tagged.
7433 int known_int,
7434 int* required_shift) { // Including Smi tag shift
7435 switch (known_int) {
7436 case 3:
7437 __ add(result, source, Operand(source, LSL, 1));
7438 *required_shift = 1;
7439 break;
7440 case 5:
7441 __ add(result, source, Operand(source, LSL, 2));
7442 *required_shift = 1;
7443 break;
7444 case 6:
7445 __ add(result, source, Operand(source, LSL, 1));
7446 *required_shift = 2;
7447 break;
7448 case 7:
7449 __ rsb(result, source, Operand(source, LSL, 3));
7450 *required_shift = 1;
7451 break;
7452 case 9:
7453 __ add(result, source, Operand(source, LSL, 3));
7454 *required_shift = 1;
7455 break;
7456 case 10:
7457 __ add(result, source, Operand(source, LSL, 2));
7458 *required_shift = 2;
7459 break;
7460 default:
7461 ASSERT(!IsPowerOf2(known_int)); // That would be very inefficient.
7462 __ mul(result, source, known_int_register);
7463 *required_shift = 0;
7464 }
7465}
7466
7467
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00007468const char* GenericBinaryOpStub::GetName() {
7469 if (name_ != NULL) return name_;
7470 const int len = 100;
7471 name_ = Bootstrapper::AllocateAutoDeletedArray(len);
7472 if (name_ == NULL) return "OOM";
7473 const char* op_name = Token::Name(op_);
7474 const char* overwrite_name;
7475 switch (mode_) {
7476 case NO_OVERWRITE: overwrite_name = "Alloc"; break;
7477 case OVERWRITE_RIGHT: overwrite_name = "OverwriteRight"; break;
7478 case OVERWRITE_LEFT: overwrite_name = "OverwriteLeft"; break;
7479 default: overwrite_name = "UnknownOverwrite"; break;
7480 }
7481
7482 OS::SNPrintF(Vector<char>(name_, len),
7483 "GenericBinaryOpStub_%s_%s%s",
7484 op_name,
7485 overwrite_name,
7486 specialized_on_rhs_ ? "_ConstantRhs" : 0);
7487 return name_;
7488}
7489
7490
ager@chromium.org5c838252010-02-19 08:53:10 +00007491
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007492void GenericBinaryOpStub::Generate(MacroAssembler* masm) {
ager@chromium.org357bf652010-04-12 11:30:10 +00007493 // lhs_ : x
7494 // rhs_ : y
7495 // r0 : result
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007496
ager@chromium.org357bf652010-04-12 11:30:10 +00007497 Register result = r0;
7498 Register lhs = lhs_;
7499 Register rhs = rhs_;
7500
7501 // This code can't cope with other register allocations yet.
7502 ASSERT(result.is(r0) &&
7503 ((lhs.is(r0) && rhs.is(r1)) ||
7504 (lhs.is(r1) && rhs.is(r0))));
7505
7506 Register smi_test_reg = VirtualFrame::scratch0();
7507 Register scratch = VirtualFrame::scratch1();
7508
7509 // All ops need to know whether we are dealing with two Smis. Set up
7510 // smi_test_reg to tell us that.
7511 if (ShouldGenerateSmiCode()) {
7512 __ orr(smi_test_reg, lhs, Operand(rhs));
7513 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007514
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007515 switch (op_) {
7516 case Token::ADD: {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007517 Label not_smi;
7518 // Fast path.
ager@chromium.org357bf652010-04-12 11:30:10 +00007519 if (ShouldGenerateSmiCode()) {
7520 ASSERT(kSmiTag == 0); // Adjust code below.
7521 __ tst(smi_test_reg, Operand(kSmiTagMask));
7522 __ b(ne, &not_smi);
7523 __ add(r0, r1, Operand(r0), SetCC); // Add y optimistically.
7524 // Return if no overflow.
7525 __ Ret(vc);
7526 __ sub(r0, r0, Operand(r1)); // Revert optimistic add.
7527 }
7528 HandleBinaryOpSlowCases(masm, &not_smi, lhs, rhs, Builtins::ADD);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007529 break;
7530 }
7531
7532 case Token::SUB: {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007533 Label not_smi;
7534 // Fast path.
ager@chromium.org357bf652010-04-12 11:30:10 +00007535 if (ShouldGenerateSmiCode()) {
7536 ASSERT(kSmiTag == 0); // Adjust code below.
7537 __ tst(smi_test_reg, Operand(kSmiTagMask));
7538 __ b(ne, &not_smi);
7539 if (lhs.is(r1)) {
7540 __ sub(r0, r1, Operand(r0), SetCC); // Subtract y optimistically.
7541 // Return if no overflow.
7542 __ Ret(vc);
7543 __ sub(r0, r1, Operand(r0)); // Revert optimistic subtract.
7544 } else {
7545 __ sub(r0, r0, Operand(r1), SetCC); // Subtract y optimistically.
7546 // Return if no overflow.
7547 __ Ret(vc);
7548 __ add(r0, r0, Operand(r1)); // Revert optimistic subtract.
7549 }
7550 }
7551 HandleBinaryOpSlowCases(masm, &not_smi, lhs, rhs, Builtins::SUB);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007552 break;
7553 }
7554
7555 case Token::MUL: {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007556 Label not_smi, slow;
ager@chromium.org357bf652010-04-12 11:30:10 +00007557 if (ShouldGenerateSmiCode()) {
7558 ASSERT(kSmiTag == 0); // adjust code below
7559 __ tst(smi_test_reg, Operand(kSmiTagMask));
7560 Register scratch2 = smi_test_reg;
7561 smi_test_reg = no_reg;
7562 __ b(ne, &not_smi);
7563 // Remove tag from one operand (but keep sign), so that result is Smi.
7564 __ mov(ip, Operand(rhs, ASR, kSmiTagSize));
7565 // Do multiplication
7566 // scratch = lower 32 bits of ip * lhs.
7567 __ smull(scratch, scratch2, lhs, ip);
7568 // Go slow on overflows (overflow bit is not set).
7569 __ mov(ip, Operand(scratch, ASR, 31));
7570 // No overflow if higher 33 bits are identical.
7571 __ cmp(ip, Operand(scratch2));
7572 __ b(ne, &slow);
7573 // Go slow on zero result to handle -0.
7574 __ tst(scratch, Operand(scratch));
7575 __ mov(result, Operand(scratch), LeaveCC, ne);
7576 __ Ret(ne);
7577 // We need -0 if we were multiplying a negative number with 0 to get 0.
7578 // We know one of them was zero.
7579 __ add(scratch2, rhs, Operand(lhs), SetCC);
7580 __ mov(result, Operand(Smi::FromInt(0)), LeaveCC, pl);
7581 __ Ret(pl); // Return Smi 0 if the non-zero one was positive.
7582 // Slow case. We fall through here if we multiplied a negative number
7583 // with 0, because that would mean we should produce -0.
7584 __ bind(&slow);
7585 }
7586 HandleBinaryOpSlowCases(masm, &not_smi, lhs, rhs, Builtins::MUL);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007587 break;
7588 }
7589
7590 case Token::DIV:
7591 case Token::MOD: {
7592 Label not_smi;
ager@chromium.org357bf652010-04-12 11:30:10 +00007593 if (ShouldGenerateSmiCode() && specialized_on_rhs_) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007594 Label smi_is_unsuitable;
ager@chromium.org357bf652010-04-12 11:30:10 +00007595 __ BranchOnNotSmi(lhs, &not_smi);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007596 if (IsPowerOf2(constant_rhs_)) {
7597 if (op_ == Token::MOD) {
ager@chromium.org357bf652010-04-12 11:30:10 +00007598 __ and_(rhs,
7599 lhs,
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007600 Operand(0x80000000u | ((constant_rhs_ << kSmiTagSize) - 1)),
7601 SetCC);
7602 // We now have the answer, but if the input was negative we also
7603 // have the sign bit. Our work is done if the result is
7604 // positive or zero:
ager@chromium.org357bf652010-04-12 11:30:10 +00007605 if (!rhs.is(r0)) {
7606 __ mov(r0, rhs, LeaveCC, pl);
7607 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007608 __ Ret(pl);
7609 // A mod of a negative left hand side must return a negative number.
7610 // Unfortunately if the answer is 0 then we must return -0. And we
ager@chromium.org357bf652010-04-12 11:30:10 +00007611 // already optimistically trashed rhs so we may need to restore it.
7612 __ eor(rhs, rhs, Operand(0x80000000u), SetCC);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007613 // Next two instructions are conditional on the answer being -0.
ager@chromium.org357bf652010-04-12 11:30:10 +00007614 __ mov(rhs, Operand(Smi::FromInt(constant_rhs_)), LeaveCC, eq);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007615 __ b(eq, &smi_is_unsuitable);
7616 // We need to subtract the dividend. Eg. -3 % 4 == -3.
ager@chromium.org357bf652010-04-12 11:30:10 +00007617 __ sub(result, rhs, Operand(Smi::FromInt(constant_rhs_)));
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007618 } else {
7619 ASSERT(op_ == Token::DIV);
ager@chromium.org357bf652010-04-12 11:30:10 +00007620 __ tst(lhs,
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007621 Operand(0x80000000u | ((constant_rhs_ << kSmiTagSize) - 1)));
7622 __ b(ne, &smi_is_unsuitable); // Go slow on negative or remainder.
7623 int shift = 0;
7624 int d = constant_rhs_;
7625 while ((d & 1) == 0) {
7626 d >>= 1;
7627 shift++;
7628 }
ager@chromium.org357bf652010-04-12 11:30:10 +00007629 __ mov(r0, Operand(lhs, LSR, shift));
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007630 __ bic(r0, r0, Operand(kSmiTagMask));
7631 }
7632 } else {
7633 // Not a power of 2.
ager@chromium.org357bf652010-04-12 11:30:10 +00007634 __ tst(lhs, Operand(0x80000000u));
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007635 __ b(ne, &smi_is_unsuitable);
7636 // Find a fixed point reciprocal of the divisor so we can divide by
7637 // multiplying.
7638 double divisor = 1.0 / constant_rhs_;
7639 int shift = 32;
7640 double scale = 4294967296.0; // 1 << 32.
7641 uint32_t mul;
7642 // Maximise the precision of the fixed point reciprocal.
7643 while (true) {
7644 mul = static_cast<uint32_t>(scale * divisor);
7645 if (mul >= 0x7fffffff) break;
7646 scale *= 2.0;
7647 shift++;
7648 }
7649 mul++;
ager@chromium.org357bf652010-04-12 11:30:10 +00007650 Register scratch2 = smi_test_reg;
7651 smi_test_reg = no_reg;
7652 __ mov(scratch2, Operand(mul));
7653 __ umull(scratch, scratch2, scratch2, lhs);
7654 __ mov(scratch2, Operand(scratch2, LSR, shift - 31));
7655 // scratch2 is lhs / rhs. scratch2 is not Smi tagged.
7656 // rhs is still the known rhs. rhs is Smi tagged.
7657 // lhs is still the unkown lhs. lhs is Smi tagged.
7658 int required_scratch_shift = 0; // Including the Smi tag shift of 1.
7659 // scratch = scratch2 * rhs.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007660 MultiplyByKnownInt2(masm,
ager@chromium.org357bf652010-04-12 11:30:10 +00007661 scratch,
7662 scratch2,
7663 rhs,
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007664 constant_rhs_,
ager@chromium.org357bf652010-04-12 11:30:10 +00007665 &required_scratch_shift);
7666 // scratch << required_scratch_shift is now the Smi tagged rhs *
7667 // (lhs / rhs) where / indicates integer division.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007668 if (op_ == Token::DIV) {
ager@chromium.org357bf652010-04-12 11:30:10 +00007669 __ cmp(lhs, Operand(scratch, LSL, required_scratch_shift));
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007670 __ b(ne, &smi_is_unsuitable); // There was a remainder.
ager@chromium.org357bf652010-04-12 11:30:10 +00007671 __ mov(result, Operand(scratch2, LSL, kSmiTagSize));
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007672 } else {
7673 ASSERT(op_ == Token::MOD);
ager@chromium.org357bf652010-04-12 11:30:10 +00007674 __ sub(result, lhs, Operand(scratch, LSL, required_scratch_shift));
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007675 }
7676 }
7677 __ Ret();
7678 __ bind(&smi_is_unsuitable);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007679 }
ager@chromium.org357bf652010-04-12 11:30:10 +00007680 HandleBinaryOpSlowCases(
7681 masm,
7682 &not_smi,
7683 lhs,
7684 rhs,
7685 op_ == Token::MOD ? Builtins::MOD : Builtins::DIV);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007686 break;
7687 }
7688
7689 case Token::BIT_OR:
7690 case Token::BIT_AND:
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007691 case Token::BIT_XOR:
7692 case Token::SAR:
7693 case Token::SHR:
7694 case Token::SHL: {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007695 Label slow;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007696 ASSERT(kSmiTag == 0); // adjust code below
ager@chromium.org357bf652010-04-12 11:30:10 +00007697 __ tst(smi_test_reg, Operand(kSmiTagMask));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007698 __ b(ne, &slow);
ager@chromium.org357bf652010-04-12 11:30:10 +00007699 Register scratch2 = smi_test_reg;
7700 smi_test_reg = no_reg;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007701 switch (op_) {
ager@chromium.org357bf652010-04-12 11:30:10 +00007702 case Token::BIT_OR: __ orr(result, rhs, Operand(lhs)); break;
7703 case Token::BIT_AND: __ and_(result, rhs, Operand(lhs)); break;
7704 case Token::BIT_XOR: __ eor(result, rhs, Operand(lhs)); break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007705 case Token::SAR:
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007706 // Remove tags from right operand.
ager@chromium.org357bf652010-04-12 11:30:10 +00007707 __ GetLeastBitsFromSmi(scratch2, rhs, 5);
7708 __ mov(result, Operand(lhs, ASR, scratch2));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007709 // Smi tag result.
ager@chromium.org357bf652010-04-12 11:30:10 +00007710 __ bic(result, result, Operand(kSmiTagMask));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007711 break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007712 case Token::SHR:
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007713 // Remove tags from operands. We can't do this on a 31 bit number
7714 // because then the 0s get shifted into bit 30 instead of bit 31.
ager@chromium.org357bf652010-04-12 11:30:10 +00007715 __ mov(scratch, Operand(lhs, ASR, kSmiTagSize)); // x
7716 __ GetLeastBitsFromSmi(scratch2, rhs, 5);
7717 __ mov(scratch, Operand(scratch, LSR, scratch2));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007718 // Unsigned shift is not allowed to produce a negative number, so
7719 // check the sign bit and the sign bit after Smi tagging.
ager@chromium.org357bf652010-04-12 11:30:10 +00007720 __ tst(scratch, Operand(0xc0000000));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007721 __ b(ne, &slow);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007722 // Smi tag result.
ager@chromium.org357bf652010-04-12 11:30:10 +00007723 __ mov(result, Operand(scratch, LSL, kSmiTagSize));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007724 break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007725 case Token::SHL:
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007726 // Remove tags from operands.
ager@chromium.org357bf652010-04-12 11:30:10 +00007727 __ mov(scratch, Operand(lhs, ASR, kSmiTagSize)); // x
7728 __ GetLeastBitsFromSmi(scratch2, rhs, 5);
7729 __ mov(scratch, Operand(scratch, LSL, scratch2));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007730 // Check that the signed result fits in a Smi.
ager@chromium.org357bf652010-04-12 11:30:10 +00007731 __ add(scratch2, scratch, Operand(0x40000000), SetCC);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007732 __ b(mi, &slow);
ager@chromium.org357bf652010-04-12 11:30:10 +00007733 __ mov(result, Operand(scratch, LSL, kSmiTagSize));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007734 break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007735 default: UNREACHABLE();
7736 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007737 __ Ret();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007738 __ bind(&slow);
ager@chromium.org357bf652010-04-12 11:30:10 +00007739 HandleNonSmiBitwiseOp(masm, lhs, rhs);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007740 break;
7741 }
7742
7743 default: UNREACHABLE();
7744 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007745 // This code should be unreachable.
7746 __ stop("Unreachable");
ager@chromium.org357bf652010-04-12 11:30:10 +00007747
7748 // Generate an unreachable reference to the DEFAULT stub so that it can be
7749 // found at the end of this stub when clearing ICs at GC.
7750 // TODO(kaznacheev): Check performance impact and get rid of this.
7751 if (runtime_operands_type_ != BinaryOpIC::DEFAULT) {
7752 GenericBinaryOpStub uninit(MinorKey(), BinaryOpIC::DEFAULT);
7753 __ CallStub(&uninit);
7754 }
7755}
7756
7757
7758void GenericBinaryOpStub::GenerateTypeTransition(MacroAssembler* masm) {
7759 Label get_result;
7760
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00007761 __ Push(r1, r0);
ager@chromium.org357bf652010-04-12 11:30:10 +00007762
7763 // Internal frame is necessary to handle exceptions properly.
7764 __ EnterInternalFrame();
7765 // Call the stub proper to get the result in r0.
7766 __ Call(&get_result);
7767 __ LeaveInternalFrame();
7768
7769 __ push(r0);
7770
7771 __ mov(r0, Operand(Smi::FromInt(MinorKey())));
7772 __ push(r0);
7773 __ mov(r0, Operand(Smi::FromInt(op_)));
7774 __ push(r0);
7775 __ mov(r0, Operand(Smi::FromInt(runtime_operands_type_)));
7776 __ push(r0);
7777
7778 __ TailCallExternalReference(
7779 ExternalReference(IC_Utility(IC::kBinaryOp_Patch)),
7780 6,
7781 1);
7782
7783 // The entry point for the result calculation is assumed to be immediately
7784 // after this sequence.
7785 __ bind(&get_result);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007786}
7787
7788
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00007789Handle<Code> GetBinaryOpStub(int key, BinaryOpIC::TypeInfo type_info) {
ager@chromium.org357bf652010-04-12 11:30:10 +00007790 GenericBinaryOpStub stub(key, type_info);
7791 return stub.GetCode();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00007792}
7793
7794
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007795void StackCheckStub::Generate(MacroAssembler* masm) {
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00007796 // Do tail-call to runtime routine. Runtime routines expect at least one
7797 // argument, so give it a Smi.
7798 __ mov(r0, Operand(Smi::FromInt(0)));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007799 __ push(r0);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00007800 __ TailCallRuntime(Runtime::kStackGuard, 1, 1);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007801
7802 __ StubReturn(1);
7803}
7804
7805
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007806void GenericUnaryOpStub::Generate(MacroAssembler* masm) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007807 Label slow, done;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007808
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007809 if (op_ == Token::SUB) {
7810 // Check whether the value is a smi.
7811 Label try_float;
7812 __ tst(r0, Operand(kSmiTagMask));
7813 __ b(ne, &try_float);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007814
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007815 // Go slow case if the value of the expression is zero
7816 // to make sure that we switch between 0 and -0.
7817 __ cmp(r0, Operand(0));
7818 __ b(eq, &slow);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007819
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007820 // The value of the expression is a smi that is not zero. Try
7821 // optimistic subtraction '0 - value'.
7822 __ rsb(r1, r0, Operand(0), SetCC);
7823 __ b(vs, &slow);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007824
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007825 __ mov(r0, Operand(r1)); // Set r0 to result.
7826 __ b(&done);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007827
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007828 __ bind(&try_float);
7829 __ CompareObjectType(r0, r1, r1, HEAP_NUMBER_TYPE);
7830 __ b(ne, &slow);
7831 // r0 is a heap number. Get a new heap number in r1.
7832 if (overwrite_) {
7833 __ ldr(r2, FieldMemOperand(r0, HeapNumber::kExponentOffset));
7834 __ eor(r2, r2, Operand(HeapNumber::kSignMask)); // Flip sign.
7835 __ str(r2, FieldMemOperand(r0, HeapNumber::kExponentOffset));
7836 } else {
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007837 __ AllocateHeapNumber(r1, r2, r3, &slow);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007838 __ ldr(r3, FieldMemOperand(r0, HeapNumber::kMantissaOffset));
7839 __ ldr(r2, FieldMemOperand(r0, HeapNumber::kExponentOffset));
7840 __ str(r3, FieldMemOperand(r1, HeapNumber::kMantissaOffset));
7841 __ eor(r2, r2, Operand(HeapNumber::kSignMask)); // Flip sign.
7842 __ str(r2, FieldMemOperand(r1, HeapNumber::kExponentOffset));
7843 __ mov(r0, Operand(r1));
7844 }
7845 } else if (op_ == Token::BIT_NOT) {
7846 // Check if the operand is a heap number.
7847 __ CompareObjectType(r0, r1, r1, HEAP_NUMBER_TYPE);
7848 __ b(ne, &slow);
7849
7850 // Convert the heap number is r0 to an untagged integer in r1.
7851 GetInt32(masm, r0, r1, r2, r3, &slow);
7852
7853 // Do the bitwise operation (move negated) and check if the result
7854 // fits in a smi.
7855 Label try_float;
7856 __ mvn(r1, Operand(r1));
7857 __ add(r2, r1, Operand(0x40000000), SetCC);
7858 __ b(mi, &try_float);
7859 __ mov(r0, Operand(r1, LSL, kSmiTagSize));
7860 __ b(&done);
7861
7862 __ bind(&try_float);
7863 if (!overwrite_) {
7864 // Allocate a fresh heap number, but don't overwrite r0 until
7865 // we're sure we can do it without going through the slow case
7866 // that needs the value in r0.
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007867 __ AllocateHeapNumber(r2, r3, r4, &slow);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007868 __ mov(r0, Operand(r2));
7869 }
7870
7871 // WriteInt32ToHeapNumberStub does not trigger GC, so we do not
7872 // have to set up a frame.
7873 WriteInt32ToHeapNumberStub stub(r1, r0, r2);
7874 __ push(lr);
7875 __ Call(stub.GetCode(), RelocInfo::CODE_TARGET);
7876 __ pop(lr);
7877 } else {
7878 UNIMPLEMENTED();
7879 }
7880
7881 __ bind(&done);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007882 __ StubReturn(1);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007883
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007884 // Handle the slow case by jumping to the JavaScript builtin.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007885 __ bind(&slow);
7886 __ push(r0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007887 switch (op_) {
7888 case Token::SUB:
7889 __ InvokeBuiltin(Builtins::UNARY_MINUS, JUMP_JS);
7890 break;
7891 case Token::BIT_NOT:
7892 __ InvokeBuiltin(Builtins::BIT_NOT, JUMP_JS);
7893 break;
7894 default:
7895 UNREACHABLE();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007896 }
ager@chromium.orga1645e22009-09-09 19:27:10 +00007897}
7898
7899
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007900void CEntryStub::GenerateThrowTOS(MacroAssembler* masm) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007901 // r0 holds the exception.
7902
7903 // Adjust this code if not the case.
7904 ASSERT(StackHandlerConstants::kSize == 4 * kPointerSize);
7905
7906 // Drop the sp to the top of the handler.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007907 __ mov(r3, Operand(ExternalReference(Top::k_handler_address)));
7908 __ ldr(sp, MemOperand(r3));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007909
7910 // Restore the next handler and frame pointer, discard handler state.
7911 ASSERT(StackHandlerConstants::kNextOffset == 0);
7912 __ pop(r2);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007913 __ str(r2, MemOperand(r3));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007914 ASSERT(StackHandlerConstants::kFPOffset == 2 * kPointerSize);
7915 __ ldm(ia_w, sp, r3.bit() | fp.bit()); // r3: discarded state.
7916
7917 // Before returning we restore the context from the frame pointer if
7918 // not NULL. The frame pointer is NULL in the exception handler of a
7919 // JS entry frame.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007920 __ cmp(fp, Operand(0));
7921 // Set cp to NULL if fp is NULL.
7922 __ mov(cp, Operand(0), LeaveCC, eq);
7923 // Restore cp otherwise.
7924 __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset), ne);
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007925#ifdef DEBUG
7926 if (FLAG_debug_code) {
7927 __ mov(lr, Operand(pc));
7928 }
7929#endif
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007930 ASSERT(StackHandlerConstants::kPCOffset == 3 * kPointerSize);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007931 __ pop(pc);
7932}
7933
7934
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00007935void CEntryStub::GenerateThrowUncatchable(MacroAssembler* masm,
7936 UncatchableExceptionType type) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007937 // Adjust this code if not the case.
7938 ASSERT(StackHandlerConstants::kSize == 4 * kPointerSize);
7939
7940 // Drop sp to the top stack handler.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007941 __ mov(r3, Operand(ExternalReference(Top::k_handler_address)));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007942 __ ldr(sp, MemOperand(r3));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007943
7944 // Unwind the handlers until the ENTRY handler is found.
7945 Label loop, done;
7946 __ bind(&loop);
7947 // Load the type of the current stack handler.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007948 const int kStateOffset = StackHandlerConstants::kStateOffset;
7949 __ ldr(r2, MemOperand(sp, kStateOffset));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007950 __ cmp(r2, Operand(StackHandler::ENTRY));
7951 __ b(eq, &done);
7952 // Fetch the next handler in the list.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007953 const int kNextOffset = StackHandlerConstants::kNextOffset;
7954 __ ldr(sp, MemOperand(sp, kNextOffset));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007955 __ jmp(&loop);
7956 __ bind(&done);
7957
7958 // Set the top handler address to next handler past the current ENTRY handler.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007959 ASSERT(StackHandlerConstants::kNextOffset == 0);
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00007960 __ pop(r2);
7961 __ str(r2, MemOperand(r3));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007962
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00007963 if (type == OUT_OF_MEMORY) {
7964 // Set external caught exception to false.
7965 ExternalReference external_caught(Top::k_external_caught_exception_address);
7966 __ mov(r0, Operand(false));
7967 __ mov(r2, Operand(external_caught));
7968 __ str(r0, MemOperand(r2));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007969
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00007970 // Set pending exception and r0 to out of memory exception.
7971 Failure* out_of_memory = Failure::OutOfMemoryException();
7972 __ mov(r0, Operand(reinterpret_cast<int32_t>(out_of_memory)));
7973 __ mov(r2, Operand(ExternalReference(Top::k_pending_exception_address)));
7974 __ str(r0, MemOperand(r2));
7975 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007976
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007977 // Stack layout at this point. See also StackHandlerConstants.
7978 // sp -> state (ENTRY)
7979 // fp
7980 // lr
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007981
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007982 // Discard handler state (r2 is not used) and restore frame pointer.
7983 ASSERT(StackHandlerConstants::kFPOffset == 2 * kPointerSize);
7984 __ ldm(ia_w, sp, r2.bit() | fp.bit()); // r2: discarded state.
7985 // Before returning we restore the context from the frame pointer if
7986 // not NULL. The frame pointer is NULL in the exception handler of a
7987 // JS entry frame.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007988 __ cmp(fp, Operand(0));
7989 // Set cp to NULL if fp is NULL.
7990 __ mov(cp, Operand(0), LeaveCC, eq);
7991 // Restore cp otherwise.
7992 __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset), ne);
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007993#ifdef DEBUG
7994 if (FLAG_debug_code) {
7995 __ mov(lr, Operand(pc));
7996 }
7997#endif
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007998 ASSERT(StackHandlerConstants::kPCOffset == 3 * kPointerSize);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007999 __ pop(pc);
8000}
8001
8002
8003void CEntryStub::GenerateCore(MacroAssembler* masm,
8004 Label* throw_normal_exception,
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00008005 Label* throw_termination_exception,
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008006 Label* throw_out_of_memory_exception,
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00008007 bool do_gc,
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00008008 bool always_allocate,
8009 int frame_alignment_skew) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008010 // r0: result parameter for PerformGC, if any
8011 // r4: number of arguments including receiver (C callee-saved)
8012 // r5: pointer to builtin function (C callee-saved)
8013 // r6: pointer to the first argument (C callee-saved)
8014
8015 if (do_gc) {
8016 // Passing r0.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00008017 __ PrepareCallCFunction(1, r1);
8018 __ CallCFunction(ExternalReference::perform_gc_function(), 1);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008019 }
8020
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00008021 ExternalReference scope_depth =
8022 ExternalReference::heap_always_allocate_scope_depth();
8023 if (always_allocate) {
8024 __ mov(r0, Operand(scope_depth));
8025 __ ldr(r1, MemOperand(r0));
8026 __ add(r1, r1, Operand(1));
8027 __ str(r1, MemOperand(r0));
8028 }
8029
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008030 // Call C built-in.
8031 // r0 = argc, r1 = argv
8032 __ mov(r0, Operand(r4));
8033 __ mov(r1, Operand(r6));
8034
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00008035 int frame_alignment = MacroAssembler::ActivationFrameAlignment();
8036 int frame_alignment_mask = frame_alignment - 1;
8037#if defined(V8_HOST_ARCH_ARM)
8038 if (FLAG_debug_code) {
8039 if (frame_alignment > kPointerSize) {
8040 Label alignment_as_expected;
8041 ASSERT(IsPowerOf2(frame_alignment));
8042 __ sub(r2, sp, Operand(frame_alignment_skew));
8043 __ tst(r2, Operand(frame_alignment_mask));
8044 __ b(eq, &alignment_as_expected);
8045 // Don't use Check here, as it will call Runtime_Abort re-entering here.
8046 __ stop("Unexpected alignment");
8047 __ bind(&alignment_as_expected);
8048 }
8049 }
8050#endif
8051
8052 // Just before the call (jump) below lr is pushed, so the actual alignment is
8053 // adding one to the current skew.
8054 int alignment_before_call =
8055 (frame_alignment_skew + kPointerSize) & frame_alignment_mask;
8056 if (alignment_before_call > 0) {
8057 // Push until the alignment before the call is met.
8058 __ mov(r2, Operand(0));
8059 for (int i = alignment_before_call;
8060 (i & frame_alignment_mask) != 0;
8061 i += kPointerSize) {
8062 __ push(r2);
8063 }
8064 }
8065
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008066 // TODO(1242173): To let the GC traverse the return address of the exit
8067 // frames, we need to know where the return address is. Right now,
8068 // we push it on the stack to be able to find it again, but we never
8069 // restore from it in case of changes, which makes it impossible to
8070 // support moving the C entry code stub. This should be fixed, but currently
8071 // this is OK because the CEntryStub gets generated so early in the V8 boot
8072 // sequence that it is not moving ever.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00008073 masm->add(lr, pc, Operand(4)); // Compute return address: (pc + 8) + 4
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00008074 masm->push(lr);
8075 masm->Jump(r5);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00008076
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00008077 // Restore sp back to before aligning the stack.
8078 if (alignment_before_call > 0) {
8079 __ add(sp, sp, Operand(alignment_before_call));
8080 }
8081
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00008082 if (always_allocate) {
8083 // It's okay to clobber r2 and r3 here. Don't mess with r0 and r1
8084 // though (contain the result).
8085 __ mov(r2, Operand(scope_depth));
8086 __ ldr(r3, MemOperand(r2));
8087 __ sub(r3, r3, Operand(1));
8088 __ str(r3, MemOperand(r2));
8089 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008090
8091 // check for failure result
8092 Label failure_returned;
8093 ASSERT(((kFailureTag + 1) & kFailureTagMask) == 0);
8094 // Lower 2 bits of r2 are 0 iff r0 has failure tag.
8095 __ add(r2, r0, Operand(1));
8096 __ tst(r2, Operand(kFailureTagMask));
8097 __ b(eq, &failure_returned);
8098
8099 // Exit C frame and return.
8100 // r0:r1: result
8101 // sp: stack pointer
8102 // fp: frame pointer
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008103 __ LeaveExitFrame(mode_);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008104
8105 // check if we should retry or throw exception
8106 Label retry;
8107 __ bind(&failure_returned);
8108 ASSERT(Failure::RETRY_AFTER_GC == 0);
8109 __ tst(r0, Operand(((1 << kFailureTypeTagSize) - 1) << kFailureTagSize));
8110 __ b(eq, &retry);
8111
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00008112 // Special handling of out of memory exceptions.
8113 Failure* out_of_memory = Failure::OutOfMemoryException();
8114 __ cmp(r0, Operand(reinterpret_cast<int32_t>(out_of_memory)));
8115 __ b(eq, throw_out_of_memory_exception);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008116
8117 // Retrieve the pending exception and clear the variable.
ager@chromium.org32912102009-01-16 10:38:43 +00008118 __ mov(ip, Operand(ExternalReference::the_hole_value_location()));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008119 __ ldr(r3, MemOperand(ip));
ager@chromium.org32912102009-01-16 10:38:43 +00008120 __ mov(ip, Operand(ExternalReference(Top::k_pending_exception_address)));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008121 __ ldr(r0, MemOperand(ip));
8122 __ str(r3, MemOperand(ip));
8123
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00008124 // Special handling of termination exceptions which are uncatchable
8125 // by javascript code.
8126 __ cmp(r0, Operand(Factory::termination_exception()));
8127 __ b(eq, throw_termination_exception);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008128
8129 // Handle normal exception.
8130 __ jmp(throw_normal_exception);
8131
8132 __ bind(&retry); // pass last failure (r0) as parameter (r0) when retrying
8133}
8134
8135
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008136void CEntryStub::Generate(MacroAssembler* masm) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008137 // Called from JavaScript; parameters are on stack as if calling JS function
8138 // r0: number of arguments including receiver
8139 // r1: pointer to builtin function
8140 // fp: frame pointer (restored after C call)
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008141 // sp: stack pointer (restored as callee's sp after C call)
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008142 // cp: current context (C callee-saved)
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008143
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008144 // Result returned in r0 or r0+r1 by default.
8145
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008146 // NOTE: Invocations of builtins may return failure objects
8147 // instead of a proper result. The builtin entry handles
8148 // this by performing a garbage collection and retrying the
8149 // builtin once.
8150
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008151 // Enter the exit frame that transitions from JavaScript to C++.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008152 __ EnterExitFrame(mode_);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008153
8154 // r4: number of arguments (C callee-saved)
8155 // r5: pointer to builtin function (C callee-saved)
8156 // r6: pointer to first argument (C callee-saved)
8157
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008158 Label throw_normal_exception;
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00008159 Label throw_termination_exception;
8160 Label throw_out_of_memory_exception;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008161
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00008162 // Call into the runtime system.
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00008163 GenerateCore(masm,
8164 &throw_normal_exception,
8165 &throw_termination_exception,
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008166 &throw_out_of_memory_exception,
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00008167 false,
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00008168 false,
8169 -kPointerSize);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00008170
8171 // Do space-specific GC and retry runtime call.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008172 GenerateCore(masm,
8173 &throw_normal_exception,
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00008174 &throw_termination_exception,
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008175 &throw_out_of_memory_exception,
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00008176 true,
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00008177 false,
8178 0);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00008179
8180 // Do full GC and retry runtime call one final time.
8181 Failure* failure = Failure::InternalError();
8182 __ mov(r0, Operand(reinterpret_cast<int32_t>(failure)));
8183 GenerateCore(masm,
8184 &throw_normal_exception,
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00008185 &throw_termination_exception,
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00008186 &throw_out_of_memory_exception,
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00008187 true,
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00008188 true,
8189 kPointerSize);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008190
8191 __ bind(&throw_out_of_memory_exception);
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00008192 GenerateThrowUncatchable(masm, OUT_OF_MEMORY);
8193
8194 __ bind(&throw_termination_exception);
8195 GenerateThrowUncatchable(masm, TERMINATION);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008196
8197 __ bind(&throw_normal_exception);
8198 GenerateThrowTOS(masm);
8199}
8200
8201
8202void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) {
8203 // r0: code entry
8204 // r1: function
8205 // r2: receiver
8206 // r3: argc
8207 // [sp+0]: argv
8208
8209 Label invoke, exit;
8210
8211 // Called from C, so do not pop argc and args on exit (preserve sp)
8212 // No need to save register-passed args
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008213 // Save callee-saved registers (incl. cp and fp), sp, and lr
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008214 __ stm(db_w, sp, kCalleeSaved | lr.bit());
8215
8216 // Get address of argv, see stm above.
8217 // r0: code entry
8218 // r1: function
8219 // r2: receiver
8220 // r3: argc
ager@chromium.org5c838252010-02-19 08:53:10 +00008221 __ ldr(r4, MemOperand(sp, (kNumCalleeSaved + 1) * kPointerSize)); // argv
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008222
8223 // Push a frame with special values setup to mark it as an entry frame.
8224 // r0: code entry
8225 // r1: function
8226 // r2: receiver
8227 // r3: argc
8228 // r4: argv
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008229 __ mov(r8, Operand(-1)); // Push a bad frame pointer to fail if it is used.
ager@chromium.org18ad94b2009-09-02 08:22:29 +00008230 int marker = is_construct ? StackFrame::ENTRY_CONSTRUCT : StackFrame::ENTRY;
8231 __ mov(r7, Operand(Smi::FromInt(marker)));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008232 __ mov(r6, Operand(Smi::FromInt(marker)));
8233 __ mov(r5, Operand(ExternalReference(Top::k_c_entry_fp_address)));
8234 __ ldr(r5, MemOperand(r5));
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00008235 __ Push(r8, r7, r6, r5);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008236
8237 // Setup frame pointer for the frame to be pushed.
8238 __ add(fp, sp, Operand(-EntryFrameConstants::kCallerFPOffset));
8239
8240 // Call a faked try-block that does the invoke.
8241 __ bl(&invoke);
8242
8243 // Caught exception: Store result (exception) in the pending
8244 // exception field in the JSEnv and return a failure sentinel.
8245 // Coming in here the fp will be invalid because the PushTryHandler below
8246 // sets it to 0 to signal the existence of the JSEntry frame.
ager@chromium.org32912102009-01-16 10:38:43 +00008247 __ mov(ip, Operand(ExternalReference(Top::k_pending_exception_address)));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008248 __ str(r0, MemOperand(ip));
ager@chromium.org3bf7b912008-11-17 09:09:45 +00008249 __ mov(r0, Operand(reinterpret_cast<int32_t>(Failure::Exception())));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008250 __ b(&exit);
8251
8252 // Invoke: Link this frame into the handler chain.
8253 __ bind(&invoke);
8254 // Must preserve r0-r4, r5-r7 are available.
8255 __ PushTryHandler(IN_JS_ENTRY, JS_ENTRY_HANDLER);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008256 // If an exception not caught by another handler occurs, this handler
8257 // returns control to the code after the bl(&invoke) above, which
8258 // restores all kCalleeSaved registers (including cp and fp) to their
8259 // saved values before returning a failure to C.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008260
8261 // Clear any pending exceptions.
8262 __ mov(ip, Operand(ExternalReference::the_hole_value_location()));
8263 __ ldr(r5, MemOperand(ip));
ager@chromium.org32912102009-01-16 10:38:43 +00008264 __ mov(ip, Operand(ExternalReference(Top::k_pending_exception_address)));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008265 __ str(r5, MemOperand(ip));
8266
8267 // Invoke the function by calling through JS entry trampoline builtin.
8268 // Notice that we cannot store a reference to the trampoline code directly in
8269 // this stub, because runtime stubs are not traversed when doing GC.
8270
8271 // Expected registers by Builtins::JSEntryTrampoline
8272 // r0: code entry
8273 // r1: function
8274 // r2: receiver
8275 // r3: argc
8276 // r4: argv
8277 if (is_construct) {
8278 ExternalReference construct_entry(Builtins::JSConstructEntryTrampoline);
8279 __ mov(ip, Operand(construct_entry));
8280 } else {
8281 ExternalReference entry(Builtins::JSEntryTrampoline);
8282 __ mov(ip, Operand(entry));
8283 }
8284 __ ldr(ip, MemOperand(ip)); // deref address
8285
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008286 // Branch and link to JSEntryTrampoline. We don't use the double underscore
8287 // macro for the add instruction because we don't want the coverage tool
8288 // inserting instructions here after we read the pc.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008289 __ mov(lr, Operand(pc));
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008290 masm->add(pc, ip, Operand(Code::kHeaderSize - kHeapObjectTag));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008291
8292 // Unlink this frame from the handler chain. When reading the
8293 // address of the next handler, there is no need to use the address
8294 // displacement since the current stack pointer (sp) points directly
8295 // to the stack handler.
8296 __ ldr(r3, MemOperand(sp, StackHandlerConstants::kNextOffset));
8297 __ mov(ip, Operand(ExternalReference(Top::k_handler_address)));
8298 __ str(r3, MemOperand(ip));
8299 // No need to restore registers
8300 __ add(sp, sp, Operand(StackHandlerConstants::kSize));
8301
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008302
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008303 __ bind(&exit); // r0 holds result
8304 // Restore the top frame descriptors from the stack.
8305 __ pop(r3);
8306 __ mov(ip, Operand(ExternalReference(Top::k_c_entry_fp_address)));
8307 __ str(r3, MemOperand(ip));
8308
8309 // Reset the stack to the callee saved registers.
8310 __ add(sp, sp, Operand(-EntryFrameConstants::kCallerFPOffset));
8311
8312 // Restore callee-saved registers and return.
8313#ifdef DEBUG
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008314 if (FLAG_debug_code) {
8315 __ mov(lr, Operand(pc));
8316 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008317#endif
8318 __ ldm(ia_w, sp, kCalleeSaved | pc.bit());
8319}
8320
8321
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008322// This stub performs an instanceof, calling the builtin function if
8323// necessary. Uses r1 for the object, r0 for the function that it may
8324// be an instance of (these are fetched from the stack).
8325void InstanceofStub::Generate(MacroAssembler* masm) {
8326 // Get the object - slow case for smis (we may need to throw an exception
8327 // depending on the rhs).
8328 Label slow, loop, is_instance, is_not_instance;
8329 __ ldr(r0, MemOperand(sp, 1 * kPointerSize));
8330 __ BranchOnSmi(r0, &slow);
8331
8332 // Check that the left hand is a JS object and put map in r3.
8333 __ CompareObjectType(r0, r3, r2, FIRST_JS_OBJECT_TYPE);
8334 __ b(lt, &slow);
8335 __ cmp(r2, Operand(LAST_JS_OBJECT_TYPE));
8336 __ b(gt, &slow);
8337
8338 // Get the prototype of the function (r4 is result, r2 is scratch).
ager@chromium.org5c838252010-02-19 08:53:10 +00008339 __ ldr(r1, MemOperand(sp, 0));
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00008340 // r1 is function, r3 is map.
8341
8342 // Look up the function and the map in the instanceof cache.
8343 Label miss;
8344 __ LoadRoot(ip, Heap::kInstanceofCacheFunctionRootIndex);
8345 __ cmp(r1, ip);
8346 __ b(ne, &miss);
8347 __ LoadRoot(ip, Heap::kInstanceofCacheMapRootIndex);
8348 __ cmp(r3, ip);
8349 __ b(ne, &miss);
8350 __ LoadRoot(r0, Heap::kInstanceofCacheAnswerRootIndex);
8351 __ pop();
8352 __ pop();
8353 __ mov(pc, Operand(lr));
8354
8355 __ bind(&miss);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008356 __ TryGetFunctionPrototype(r1, r4, r2, &slow);
8357
8358 // Check that the function prototype is a JS object.
8359 __ BranchOnSmi(r4, &slow);
8360 __ CompareObjectType(r4, r5, r5, FIRST_JS_OBJECT_TYPE);
8361 __ b(lt, &slow);
8362 __ cmp(r5, Operand(LAST_JS_OBJECT_TYPE));
8363 __ b(gt, &slow);
8364
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00008365 __ StoreRoot(r1, Heap::kInstanceofCacheFunctionRootIndex);
8366 __ StoreRoot(r3, Heap::kInstanceofCacheMapRootIndex);
8367
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008368 // Register mapping: r3 is object map and r4 is function prototype.
8369 // Get prototype of object into r2.
8370 __ ldr(r2, FieldMemOperand(r3, Map::kPrototypeOffset));
8371
8372 // Loop through the prototype chain looking for the function prototype.
8373 __ bind(&loop);
8374 __ cmp(r2, Operand(r4));
8375 __ b(eq, &is_instance);
ager@chromium.orgab99eea2009-08-25 07:05:41 +00008376 __ LoadRoot(ip, Heap::kNullValueRootIndex);
8377 __ cmp(r2, ip);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008378 __ b(eq, &is_not_instance);
8379 __ ldr(r2, FieldMemOperand(r2, HeapObject::kMapOffset));
8380 __ ldr(r2, FieldMemOperand(r2, Map::kPrototypeOffset));
8381 __ jmp(&loop);
8382
8383 __ bind(&is_instance);
8384 __ mov(r0, Operand(Smi::FromInt(0)));
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00008385 __ StoreRoot(r0, Heap::kInstanceofCacheAnswerRootIndex);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008386 __ pop();
8387 __ pop();
8388 __ mov(pc, Operand(lr)); // Return.
8389
8390 __ bind(&is_not_instance);
8391 __ mov(r0, Operand(Smi::FromInt(1)));
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00008392 __ StoreRoot(r0, Heap::kInstanceofCacheAnswerRootIndex);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008393 __ pop();
8394 __ pop();
8395 __ mov(pc, Operand(lr)); // Return.
8396
8397 // Slow-case. Tail call builtin.
8398 __ bind(&slow);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008399 __ InvokeBuiltin(Builtins::INSTANCE_OF, JUMP_JS);
8400}
8401
8402
ager@chromium.org7c537e22008-10-16 08:43:32 +00008403void ArgumentsAccessStub::GenerateReadElement(MacroAssembler* masm) {
8404 // The displacement is the offset of the last parameter (if any)
8405 // relative to the frame pointer.
8406 static const int kDisplacement =
8407 StandardFrameConstants::kCallerSPOffset - kPointerSize;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008408
ager@chromium.org7c537e22008-10-16 08:43:32 +00008409 // Check that the key is a smi.
8410 Label slow;
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008411 __ BranchOnNotSmi(r1, &slow);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008412
ager@chromium.org7c537e22008-10-16 08:43:32 +00008413 // Check if the calling frame is an arguments adaptor frame.
8414 Label adaptor;
8415 __ ldr(r2, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
8416 __ ldr(r3, MemOperand(r2, StandardFrameConstants::kContextOffset));
ager@chromium.org18ad94b2009-09-02 08:22:29 +00008417 __ cmp(r3, Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
ager@chromium.org7c537e22008-10-16 08:43:32 +00008418 __ b(eq, &adaptor);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008419
ager@chromium.org7c537e22008-10-16 08:43:32 +00008420 // Check index against formal parameters count limit passed in
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00008421 // through register r0. Use unsigned comparison to get negative
ager@chromium.org7c537e22008-10-16 08:43:32 +00008422 // check for free.
8423 __ cmp(r1, r0);
8424 __ b(cs, &slow);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008425
ager@chromium.org7c537e22008-10-16 08:43:32 +00008426 // Read the argument from the stack and return it.
8427 __ sub(r3, r0, r1);
8428 __ add(r3, fp, Operand(r3, LSL, kPointerSizeLog2 - kSmiTagSize));
8429 __ ldr(r0, MemOperand(r3, kDisplacement));
ager@chromium.org9085a012009-05-11 19:22:57 +00008430 __ Jump(lr);
ager@chromium.org7c537e22008-10-16 08:43:32 +00008431
8432 // Arguments adaptor case: Check index against actual arguments
8433 // limit found in the arguments adaptor frame. Use unsigned
8434 // comparison to get negative check for free.
8435 __ bind(&adaptor);
8436 __ ldr(r0, MemOperand(r2, ArgumentsAdaptorFrameConstants::kLengthOffset));
8437 __ cmp(r1, r0);
8438 __ b(cs, &slow);
8439
8440 // Read the argument from the adaptor frame and return it.
8441 __ sub(r3, r0, r1);
8442 __ add(r3, r2, Operand(r3, LSL, kPointerSizeLog2 - kSmiTagSize));
8443 __ ldr(r0, MemOperand(r3, kDisplacement));
ager@chromium.org9085a012009-05-11 19:22:57 +00008444 __ Jump(lr);
ager@chromium.org7c537e22008-10-16 08:43:32 +00008445
8446 // Slow-case: Handle non-smi or out-of-bounds access to arguments
8447 // by calling the runtime system.
8448 __ bind(&slow);
8449 __ push(r1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00008450 __ TailCallRuntime(Runtime::kGetArgumentsProperty, 1, 1);
ager@chromium.org7c537e22008-10-16 08:43:32 +00008451}
8452
8453
8454void ArgumentsAccessStub::GenerateNewObject(MacroAssembler* masm) {
ager@chromium.org5c838252010-02-19 08:53:10 +00008455 // sp[0] : number of parameters
8456 // sp[4] : receiver displacement
8457 // sp[8] : function
8458
ager@chromium.org7c537e22008-10-16 08:43:32 +00008459 // Check if the calling frame is an arguments adaptor frame.
ager@chromium.org5c838252010-02-19 08:53:10 +00008460 Label adaptor_frame, try_allocate, runtime;
ager@chromium.org7c537e22008-10-16 08:43:32 +00008461 __ ldr(r2, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
8462 __ ldr(r3, MemOperand(r2, StandardFrameConstants::kContextOffset));
ager@chromium.org18ad94b2009-09-02 08:22:29 +00008463 __ cmp(r3, Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
ager@chromium.org5c838252010-02-19 08:53:10 +00008464 __ b(eq, &adaptor_frame);
8465
8466 // Get the length from the frame.
8467 __ ldr(r1, MemOperand(sp, 0));
8468 __ b(&try_allocate);
ager@chromium.org7c537e22008-10-16 08:43:32 +00008469
8470 // Patch the arguments.length and the parameters pointer.
ager@chromium.org5c838252010-02-19 08:53:10 +00008471 __ bind(&adaptor_frame);
8472 __ ldr(r1, MemOperand(r2, ArgumentsAdaptorFrameConstants::kLengthOffset));
8473 __ str(r1, MemOperand(sp, 0));
8474 __ add(r3, r2, Operand(r1, LSL, kPointerSizeLog2 - kSmiTagSize));
ager@chromium.org7c537e22008-10-16 08:43:32 +00008475 __ add(r3, r3, Operand(StandardFrameConstants::kCallerSPOffset));
8476 __ str(r3, MemOperand(sp, 1 * kPointerSize));
8477
ager@chromium.org5c838252010-02-19 08:53:10 +00008478 // Try the new space allocation. Start out with computing the size
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00008479 // of the arguments object and the elements array in words.
ager@chromium.org5c838252010-02-19 08:53:10 +00008480 Label add_arguments_object;
8481 __ bind(&try_allocate);
8482 __ cmp(r1, Operand(0));
8483 __ b(eq, &add_arguments_object);
8484 __ mov(r1, Operand(r1, LSR, kSmiTagSize));
8485 __ add(r1, r1, Operand(FixedArray::kHeaderSize / kPointerSize));
8486 __ bind(&add_arguments_object);
8487 __ add(r1, r1, Operand(Heap::kArgumentsObjectSize / kPointerSize));
8488
8489 // Do the allocation of both objects in one go.
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00008490 __ AllocateInNewSpace(
8491 r1,
8492 r0,
8493 r2,
8494 r3,
8495 &runtime,
8496 static_cast<AllocationFlags>(TAG_OBJECT | SIZE_IN_WORDS));
ager@chromium.org5c838252010-02-19 08:53:10 +00008497
8498 // Get the arguments boilerplate from the current (global) context.
8499 int offset = Context::SlotOffset(Context::ARGUMENTS_BOILERPLATE_INDEX);
8500 __ ldr(r4, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_INDEX)));
8501 __ ldr(r4, FieldMemOperand(r4, GlobalObject::kGlobalContextOffset));
8502 __ ldr(r4, MemOperand(r4, offset));
8503
8504 // Copy the JS object part.
8505 for (int i = 0; i < JSObject::kHeaderSize; i += kPointerSize) {
8506 __ ldr(r3, FieldMemOperand(r4, i));
8507 __ str(r3, FieldMemOperand(r0, i));
8508 }
8509
8510 // Setup the callee in-object property.
8511 ASSERT(Heap::arguments_callee_index == 0);
8512 __ ldr(r3, MemOperand(sp, 2 * kPointerSize));
8513 __ str(r3, FieldMemOperand(r0, JSObject::kHeaderSize));
8514
8515 // Get the length (smi tagged) and set that as an in-object property too.
8516 ASSERT(Heap::arguments_length_index == 1);
8517 __ ldr(r1, MemOperand(sp, 0 * kPointerSize));
8518 __ str(r1, FieldMemOperand(r0, JSObject::kHeaderSize + kPointerSize));
8519
8520 // If there are no actual arguments, we're done.
8521 Label done;
8522 __ cmp(r1, Operand(0));
8523 __ b(eq, &done);
8524
8525 // Get the parameters pointer from the stack and untag the length.
8526 __ ldr(r2, MemOperand(sp, 1 * kPointerSize));
8527 __ mov(r1, Operand(r1, LSR, kSmiTagSize));
8528
8529 // Setup the elements pointer in the allocated arguments object and
8530 // initialize the header in the elements fixed array.
8531 __ add(r4, r0, Operand(Heap::kArgumentsObjectSize));
8532 __ str(r4, FieldMemOperand(r0, JSObject::kElementsOffset));
8533 __ LoadRoot(r3, Heap::kFixedArrayMapRootIndex);
8534 __ str(r3, FieldMemOperand(r4, FixedArray::kMapOffset));
8535 __ str(r1, FieldMemOperand(r4, FixedArray::kLengthOffset));
8536
8537 // Copy the fixed array slots.
8538 Label loop;
8539 // Setup r4 to point to the first array slot.
8540 __ add(r4, r4, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
8541 __ bind(&loop);
8542 // Pre-decrement r2 with kPointerSize on each iteration.
8543 // Pre-decrement in order to skip receiver.
8544 __ ldr(r3, MemOperand(r2, kPointerSize, NegPreIndex));
8545 // Post-increment r4 with kPointerSize on each iteration.
8546 __ str(r3, MemOperand(r4, kPointerSize, PostIndex));
8547 __ sub(r1, r1, Operand(1));
8548 __ cmp(r1, Operand(0));
8549 __ b(ne, &loop);
8550
8551 // Return and remove the on-stack parameters.
8552 __ bind(&done);
8553 __ add(sp, sp, Operand(3 * kPointerSize));
8554 __ Ret();
8555
ager@chromium.org7c537e22008-10-16 08:43:32 +00008556 // Do the runtime call to allocate the arguments object.
8557 __ bind(&runtime);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00008558 __ TailCallRuntime(Runtime::kNewArgumentsFast, 3, 1);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008559}
8560
8561
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00008562void RegExpExecStub::Generate(MacroAssembler* masm) {
8563 // Just jump directly to runtime if native RegExp is not selected at compile
8564 // time or if regexp entry in generated code is turned off runtime switch or
8565 // at compilation.
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00008566#ifdef V8_INTERPRETED_REGEXP
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00008567 __ TailCallRuntime(Runtime::kRegExpExec, 4, 1);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00008568#else // V8_INTERPRETED_REGEXP
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00008569 if (!FLAG_regexp_entry_native) {
8570 __ TailCallRuntime(Runtime::kRegExpExec, 4, 1);
8571 return;
8572 }
8573
8574 // Stack frame on entry.
8575 // sp[0]: last_match_info (expected JSArray)
8576 // sp[4]: previous index
8577 // sp[8]: subject string
8578 // sp[12]: JSRegExp object
8579
8580 static const int kLastMatchInfoOffset = 0 * kPointerSize;
8581 static const int kPreviousIndexOffset = 1 * kPointerSize;
8582 static const int kSubjectOffset = 2 * kPointerSize;
8583 static const int kJSRegExpOffset = 3 * kPointerSize;
8584
8585 Label runtime, invoke_regexp;
8586
8587 // Allocation of registers for this function. These are in callee save
8588 // registers and will be preserved by the call to the native RegExp code, as
8589 // this code is called using the normal C calling convention. When calling
8590 // directly from generated code the native RegExp code will not do a GC and
8591 // therefore the content of these registers are safe to use after the call.
8592 Register subject = r4;
8593 Register regexp_data = r5;
8594 Register last_match_info_elements = r6;
8595
8596 // Ensure that a RegExp stack is allocated.
8597 ExternalReference address_of_regexp_stack_memory_address =
8598 ExternalReference::address_of_regexp_stack_memory_address();
8599 ExternalReference address_of_regexp_stack_memory_size =
8600 ExternalReference::address_of_regexp_stack_memory_size();
8601 __ mov(r0, Operand(address_of_regexp_stack_memory_size));
8602 __ ldr(r0, MemOperand(r0, 0));
8603 __ tst(r0, Operand(r0));
8604 __ b(eq, &runtime);
8605
8606 // Check that the first argument is a JSRegExp object.
8607 __ ldr(r0, MemOperand(sp, kJSRegExpOffset));
8608 ASSERT_EQ(0, kSmiTag);
8609 __ tst(r0, Operand(kSmiTagMask));
8610 __ b(eq, &runtime);
8611 __ CompareObjectType(r0, r1, r1, JS_REGEXP_TYPE);
8612 __ b(ne, &runtime);
8613
8614 // Check that the RegExp has been compiled (data contains a fixed array).
8615 __ ldr(regexp_data, FieldMemOperand(r0, JSRegExp::kDataOffset));
8616 if (FLAG_debug_code) {
8617 __ tst(regexp_data, Operand(kSmiTagMask));
8618 __ Check(nz, "Unexpected type for RegExp data, FixedArray expected");
8619 __ CompareObjectType(regexp_data, r0, r0, FIXED_ARRAY_TYPE);
8620 __ Check(eq, "Unexpected type for RegExp data, FixedArray expected");
8621 }
8622
8623 // regexp_data: RegExp data (FixedArray)
8624 // Check the type of the RegExp. Only continue if type is JSRegExp::IRREGEXP.
8625 __ ldr(r0, FieldMemOperand(regexp_data, JSRegExp::kDataTagOffset));
8626 __ cmp(r0, Operand(Smi::FromInt(JSRegExp::IRREGEXP)));
8627 __ b(ne, &runtime);
8628
8629 // regexp_data: RegExp data (FixedArray)
8630 // Check that the number of captures fit in the static offsets vector buffer.
8631 __ ldr(r2,
8632 FieldMemOperand(regexp_data, JSRegExp::kIrregexpCaptureCountOffset));
8633 // Calculate number of capture registers (number_of_captures + 1) * 2. This
8634 // uses the asumption that smis are 2 * their untagged value.
8635 ASSERT_EQ(0, kSmiTag);
8636 ASSERT_EQ(1, kSmiTagSize + kSmiShiftSize);
8637 __ add(r2, r2, Operand(2)); // r2 was a smi.
8638 // Check that the static offsets vector buffer is large enough.
8639 __ cmp(r2, Operand(OffsetsVector::kStaticOffsetsVectorSize));
8640 __ b(hi, &runtime);
8641
8642 // r2: Number of capture registers
8643 // regexp_data: RegExp data (FixedArray)
8644 // Check that the second argument is a string.
8645 __ ldr(subject, MemOperand(sp, kSubjectOffset));
8646 __ tst(subject, Operand(kSmiTagMask));
8647 __ b(eq, &runtime);
8648 Condition is_string = masm->IsObjectStringType(subject, r0);
8649 __ b(NegateCondition(is_string), &runtime);
8650 // Get the length of the string to r3.
8651 __ ldr(r3, FieldMemOperand(subject, String::kLengthOffset));
8652
8653 // r2: Number of capture registers
ager@chromium.orgac091b72010-05-05 07:34:42 +00008654 // r3: Length of subject string as a smi
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00008655 // subject: Subject string
8656 // regexp_data: RegExp data (FixedArray)
8657 // Check that the third argument is a positive smi less than the subject
8658 // string length. A negative value will be greater (unsigned comparison).
8659 __ ldr(r0, MemOperand(sp, kPreviousIndexOffset));
ager@chromium.orgac091b72010-05-05 07:34:42 +00008660 __ tst(r0, Operand(kSmiTagMask));
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00008661 __ b(ne, &runtime);
ager@chromium.orgac091b72010-05-05 07:34:42 +00008662 __ cmp(r3, Operand(r0));
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00008663 __ b(ls, &runtime);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00008664
8665 // r2: Number of capture registers
8666 // subject: Subject string
8667 // regexp_data: RegExp data (FixedArray)
8668 // Check that the fourth object is a JSArray object.
8669 __ ldr(r0, MemOperand(sp, kLastMatchInfoOffset));
8670 __ tst(r0, Operand(kSmiTagMask));
8671 __ b(eq, &runtime);
8672 __ CompareObjectType(r0, r1, r1, JS_ARRAY_TYPE);
8673 __ b(ne, &runtime);
8674 // Check that the JSArray is in fast case.
8675 __ ldr(last_match_info_elements,
8676 FieldMemOperand(r0, JSArray::kElementsOffset));
8677 __ ldr(r0, FieldMemOperand(last_match_info_elements, HeapObject::kMapOffset));
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00008678 __ LoadRoot(ip, Heap::kFixedArrayMapRootIndex);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00008679 __ cmp(r0, ip);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00008680 __ b(ne, &runtime);
8681 // Check that the last match info has space for the capture registers and the
8682 // additional information.
8683 __ ldr(r0,
8684 FieldMemOperand(last_match_info_elements, FixedArray::kLengthOffset));
8685 __ add(r2, r2, Operand(RegExpImpl::kLastMatchOverhead));
8686 __ cmp(r2, r0);
8687 __ b(gt, &runtime);
8688
8689 // subject: Subject string
8690 // regexp_data: RegExp data (FixedArray)
8691 // Check the representation and encoding of the subject string.
8692 Label seq_string;
8693 const int kStringRepresentationEncodingMask =
8694 kIsNotStringMask | kStringRepresentationMask | kStringEncodingMask;
8695 __ ldr(r0, FieldMemOperand(subject, HeapObject::kMapOffset));
8696 __ ldrb(r0, FieldMemOperand(r0, Map::kInstanceTypeOffset));
8697 __ and_(r1, r0, Operand(kStringRepresentationEncodingMask));
8698 // First check for sequential string.
8699 ASSERT_EQ(0, kStringTag);
8700 ASSERT_EQ(0, kSeqStringTag);
8701 __ tst(r1, Operand(kIsNotStringMask | kStringRepresentationMask));
8702 __ b(eq, &seq_string);
8703
8704 // subject: Subject string
8705 // regexp_data: RegExp data (FixedArray)
8706 // Check for flat cons string.
8707 // A flat cons string is a cons string where the second part is the empty
8708 // string. In that case the subject string is just the first part of the cons
8709 // string. Also in this case the first part of the cons string is known to be
8710 // a sequential string or an external string.
8711 __ and_(r0, r0, Operand(kStringRepresentationMask));
8712 __ cmp(r0, Operand(kConsStringTag));
8713 __ b(ne, &runtime);
8714 __ ldr(r0, FieldMemOperand(subject, ConsString::kSecondOffset));
8715 __ LoadRoot(r1, Heap::kEmptyStringRootIndex);
8716 __ cmp(r0, r1);
8717 __ b(ne, &runtime);
8718 __ ldr(subject, FieldMemOperand(subject, ConsString::kFirstOffset));
8719 __ ldr(r0, FieldMemOperand(subject, HeapObject::kMapOffset));
8720 __ ldrb(r0, FieldMemOperand(r0, Map::kInstanceTypeOffset));
8721 ASSERT_EQ(0, kSeqStringTag);
8722 __ tst(r0, Operand(kStringRepresentationMask));
8723 __ b(nz, &runtime);
8724 __ and_(r1, r0, Operand(kStringRepresentationEncodingMask));
8725
8726 __ bind(&seq_string);
8727 // r1: suject string type & kStringRepresentationEncodingMask
8728 // subject: Subject string
8729 // regexp_data: RegExp data (FixedArray)
8730 // Check that the irregexp code has been generated for an ascii string. If
8731 // it has, the field contains a code object otherwise it contains the hole.
8732#ifdef DEBUG
8733 const int kSeqAsciiString = kStringTag | kSeqStringTag | kAsciiStringTag;
8734 const int kSeqTwoByteString = kStringTag | kSeqStringTag | kTwoByteStringTag;
8735 CHECK_EQ(4, kSeqAsciiString);
8736 CHECK_EQ(0, kSeqTwoByteString);
8737#endif
8738 // Find the code object based on the assumptions above.
8739 __ mov(r3, Operand(r1, ASR, 2), SetCC);
8740 __ ldr(r7, FieldMemOperand(regexp_data, JSRegExp::kDataAsciiCodeOffset), ne);
8741 __ ldr(r7, FieldMemOperand(regexp_data, JSRegExp::kDataUC16CodeOffset), eq);
8742
8743 // Check that the irregexp code has been generated for the actual string
8744 // encoding. If it has, the field contains a code object otherwise it contains
8745 // the hole.
8746 __ CompareObjectType(r7, r0, r0, CODE_TYPE);
8747 __ b(ne, &runtime);
8748
8749 // r3: encoding of subject string (1 if ascii, 0 if two_byte);
8750 // r7: code
8751 // subject: Subject string
8752 // regexp_data: RegExp data (FixedArray)
8753 // Load used arguments before starting to push arguments for call to native
8754 // RegExp code to avoid handling changing stack height.
8755 __ ldr(r1, MemOperand(sp, kPreviousIndexOffset));
8756 __ mov(r1, Operand(r1, ASR, kSmiTagSize));
8757
8758 // r1: previous index
8759 // r3: encoding of subject string (1 if ascii, 0 if two_byte);
8760 // r7: code
8761 // subject: Subject string
8762 // regexp_data: RegExp data (FixedArray)
8763 // All checks done. Now push arguments for native regexp code.
8764 __ IncrementCounter(&Counters::regexp_entry_native, 1, r0, r2);
8765
8766 static const int kRegExpExecuteArguments = 7;
8767 __ push(lr);
8768 __ PrepareCallCFunction(kRegExpExecuteArguments, r0);
8769
8770 // Argument 7 (sp[8]): Indicate that this is a direct call from JavaScript.
8771 __ mov(r0, Operand(1));
8772 __ str(r0, MemOperand(sp, 2 * kPointerSize));
8773
8774 // Argument 6 (sp[4]): Start (high end) of backtracking stack memory area.
8775 __ mov(r0, Operand(address_of_regexp_stack_memory_address));
8776 __ ldr(r0, MemOperand(r0, 0));
8777 __ mov(r2, Operand(address_of_regexp_stack_memory_size));
8778 __ ldr(r2, MemOperand(r2, 0));
8779 __ add(r0, r0, Operand(r2));
8780 __ str(r0, MemOperand(sp, 1 * kPointerSize));
8781
8782 // Argument 5 (sp[0]): static offsets vector buffer.
8783 __ mov(r0, Operand(ExternalReference::address_of_static_offsets_vector()));
8784 __ str(r0, MemOperand(sp, 0 * kPointerSize));
8785
8786 // For arguments 4 and 3 get string length, calculate start of string data and
8787 // calculate the shift of the index (0 for ASCII and 1 for two byte).
8788 __ ldr(r0, FieldMemOperand(subject, String::kLengthOffset));
ager@chromium.orgac091b72010-05-05 07:34:42 +00008789 __ mov(r0, Operand(r0, ASR, kSmiTagSize));
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00008790 ASSERT_EQ(SeqAsciiString::kHeaderSize, SeqTwoByteString::kHeaderSize);
8791 __ add(r9, subject, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
8792 __ eor(r3, r3, Operand(1));
8793 // Argument 4 (r3): End of string data
8794 // Argument 3 (r2): Start of string data
8795 __ add(r2, r9, Operand(r1, LSL, r3));
8796 __ add(r3, r9, Operand(r0, LSL, r3));
8797
8798 // Argument 2 (r1): Previous index.
8799 // Already there
8800
8801 // Argument 1 (r0): Subject string.
8802 __ mov(r0, subject);
8803
8804 // Locate the code entry and call it.
8805 __ add(r7, r7, Operand(Code::kHeaderSize - kHeapObjectTag));
8806 __ CallCFunction(r7, kRegExpExecuteArguments);
8807 __ pop(lr);
8808
8809 // r0: result
8810 // subject: subject string (callee saved)
8811 // regexp_data: RegExp data (callee saved)
8812 // last_match_info_elements: Last match info elements (callee saved)
8813
8814 // Check the result.
8815 Label success;
8816 __ cmp(r0, Operand(NativeRegExpMacroAssembler::SUCCESS));
8817 __ b(eq, &success);
8818 Label failure;
8819 __ cmp(r0, Operand(NativeRegExpMacroAssembler::FAILURE));
8820 __ b(eq, &failure);
8821 __ cmp(r0, Operand(NativeRegExpMacroAssembler::EXCEPTION));
8822 // If not exception it can only be retry. Handle that in the runtime system.
8823 __ b(ne, &runtime);
8824 // Result must now be exception. If there is no pending exception already a
8825 // stack overflow (on the backtrack stack) was detected in RegExp code but
8826 // haven't created the exception yet. Handle that in the runtime system.
8827 // TODO(592): Rerunning the RegExp to get the stack overflow exception.
8828 __ mov(r0, Operand(ExternalReference::the_hole_value_location()));
8829 __ ldr(r0, MemOperand(r0, 0));
8830 __ mov(r1, Operand(ExternalReference(Top::k_pending_exception_address)));
8831 __ ldr(r1, MemOperand(r1, 0));
8832 __ cmp(r0, r1);
8833 __ b(eq, &runtime);
8834 __ bind(&failure);
8835 // For failure and exception return null.
8836 __ mov(r0, Operand(Factory::null_value()));
8837 __ add(sp, sp, Operand(4 * kPointerSize));
8838 __ Ret();
8839
8840 // Process the result from the native regexp code.
8841 __ bind(&success);
8842 __ ldr(r1,
8843 FieldMemOperand(regexp_data, JSRegExp::kIrregexpCaptureCountOffset));
8844 // Calculate number of capture registers (number_of_captures + 1) * 2.
8845 ASSERT_EQ(0, kSmiTag);
8846 ASSERT_EQ(1, kSmiTagSize + kSmiShiftSize);
8847 __ add(r1, r1, Operand(2)); // r1 was a smi.
8848
8849 // r1: number of capture registers
8850 // r4: subject string
8851 // Store the capture count.
8852 __ mov(r2, Operand(r1, LSL, kSmiTagSize + kSmiShiftSize)); // To smi.
8853 __ str(r2, FieldMemOperand(last_match_info_elements,
8854 RegExpImpl::kLastCaptureCountOffset));
8855 // Store last subject and last input.
8856 __ mov(r3, last_match_info_elements); // Moved up to reduce latency.
8857 __ mov(r2, Operand(RegExpImpl::kLastSubjectOffset)); // Ditto.
8858 __ str(subject,
8859 FieldMemOperand(last_match_info_elements,
8860 RegExpImpl::kLastSubjectOffset));
8861 __ RecordWrite(r3, r2, r7);
8862 __ str(subject,
8863 FieldMemOperand(last_match_info_elements,
8864 RegExpImpl::kLastInputOffset));
8865 __ mov(r3, last_match_info_elements);
8866 __ mov(r2, Operand(RegExpImpl::kLastInputOffset));
8867 __ RecordWrite(r3, r2, r7);
8868
8869 // Get the static offsets vector filled by the native regexp code.
8870 ExternalReference address_of_static_offsets_vector =
8871 ExternalReference::address_of_static_offsets_vector();
8872 __ mov(r2, Operand(address_of_static_offsets_vector));
8873
8874 // r1: number of capture registers
8875 // r2: offsets vector
8876 Label next_capture, done;
8877 // Capture register counter starts from number of capture registers and
8878 // counts down until wraping after zero.
8879 __ add(r0,
8880 last_match_info_elements,
8881 Operand(RegExpImpl::kFirstCaptureOffset - kHeapObjectTag));
8882 __ bind(&next_capture);
8883 __ sub(r1, r1, Operand(1), SetCC);
8884 __ b(mi, &done);
8885 // Read the value from the static offsets vector buffer.
8886 __ ldr(r3, MemOperand(r2, kPointerSize, PostIndex));
8887 // Store the smi value in the last match info.
8888 __ mov(r3, Operand(r3, LSL, kSmiTagSize));
8889 __ str(r3, MemOperand(r0, kPointerSize, PostIndex));
8890 __ jmp(&next_capture);
8891 __ bind(&done);
8892
8893 // Return last match info.
8894 __ ldr(r0, MemOperand(sp, kLastMatchInfoOffset));
8895 __ add(sp, sp, Operand(4 * kPointerSize));
8896 __ Ret();
8897
8898 // Do the runtime call to execute the regexp.
8899 __ bind(&runtime);
8900 __ TailCallRuntime(Runtime::kRegExpExec, 4, 1);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00008901#endif // V8_INTERPRETED_REGEXP
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00008902}
8903
8904
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008905void CallFunctionStub::Generate(MacroAssembler* masm) {
8906 Label slow;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008907
8908 // If the receiver might be a value (string, number or boolean) check for this
8909 // and box it if it is.
8910 if (ReceiverMightBeValue()) {
8911 // Get the receiver from the stack.
8912 // function, receiver [, arguments]
8913 Label receiver_is_value, receiver_is_js_object;
8914 __ ldr(r1, MemOperand(sp, argc_ * kPointerSize));
8915
8916 // Check if receiver is a smi (which is a number value).
8917 __ BranchOnSmi(r1, &receiver_is_value);
8918
8919 // Check if the receiver is a valid JS object.
8920 __ CompareObjectType(r1, r2, r2, FIRST_JS_OBJECT_TYPE);
8921 __ b(ge, &receiver_is_js_object);
8922
8923 // Call the runtime to box the value.
8924 __ bind(&receiver_is_value);
8925 __ EnterInternalFrame();
8926 __ push(r1);
8927 __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_JS);
8928 __ LeaveInternalFrame();
8929 __ str(r0, MemOperand(sp, argc_ * kPointerSize));
8930
8931 __ bind(&receiver_is_js_object);
8932 }
8933
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008934 // Get the function to call from the stack.
8935 // function, receiver [, arguments]
8936 __ ldr(r1, MemOperand(sp, (argc_ + 1) * kPointerSize));
8937
8938 // Check that the function is really a JavaScript function.
8939 // r1: pushed function (to be verified)
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008940 __ BranchOnSmi(r1, &slow);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008941 // Get the map of the function object.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008942 __ CompareObjectType(r1, r2, r2, JS_FUNCTION_TYPE);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008943 __ b(ne, &slow);
8944
8945 // Fast-case: Invoke the function now.
8946 // r1: pushed function
8947 ParameterCount actual(argc_);
8948 __ InvokeFunction(r1, actual, JUMP_FUNCTION);
8949
8950 // Slow-case: Non-function called.
8951 __ bind(&slow);
ager@chromium.org5c838252010-02-19 08:53:10 +00008952 // CALL_NON_FUNCTION expects the non-function callee as receiver (instead
8953 // of the original receiver from the call site).
8954 __ str(r1, MemOperand(sp, argc_ * kPointerSize));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008955 __ mov(r0, Operand(argc_)); // Setup the number of arguments.
ager@chromium.org3bf7b912008-11-17 09:09:45 +00008956 __ mov(r2, Operand(0));
8957 __ GetBuiltinEntry(r3, Builtins::CALL_NON_FUNCTION);
8958 __ Jump(Handle<Code>(Builtins::builtin(Builtins::ArgumentsAdaptorTrampoline)),
8959 RelocInfo::CODE_TARGET);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008960}
8961
8962
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00008963// Unfortunately you have to run without snapshots to see most of these
8964// names in the profile since most compare stubs end up in the snapshot.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00008965const char* CompareStub::GetName() {
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00008966 if (name_ != NULL) return name_;
8967 const int kMaxNameLength = 100;
8968 name_ = Bootstrapper::AllocateAutoDeletedArray(kMaxNameLength);
8969 if (name_ == NULL) return "OOM";
8970
8971 const char* cc_name;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00008972 switch (cc_) {
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00008973 case lt: cc_name = "LT"; break;
8974 case gt: cc_name = "GT"; break;
8975 case le: cc_name = "LE"; break;
8976 case ge: cc_name = "GE"; break;
8977 case eq: cc_name = "EQ"; break;
8978 case ne: cc_name = "NE"; break;
8979 default: cc_name = "UnknownCondition"; break;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00008980 }
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00008981
8982 const char* strict_name = "";
8983 if (strict_ && (cc_ == eq || cc_ == ne)) {
8984 strict_name = "_STRICT";
8985 }
8986
8987 const char* never_nan_nan_name = "";
8988 if (never_nan_nan_ && (cc_ == eq || cc_ == ne)) {
8989 never_nan_nan_name = "_NO_NAN";
8990 }
8991
8992 const char* include_number_compare_name = "";
8993 if (!include_number_compare_) {
8994 include_number_compare_name = "_NO_NUMBER";
8995 }
8996
8997 OS::SNPrintF(Vector<char>(name_, kMaxNameLength),
8998 "CompareStub_%s%s%s%s",
8999 cc_name,
9000 strict_name,
9001 never_nan_nan_name,
9002 include_number_compare_name);
9003 return name_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00009004}
9005
9006
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009007int CompareStub::MinorKey() {
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009008 // Encode the three parameters in a unique 16 bit value. To avoid duplicate
9009 // stubs the never NaN NaN condition is only taken into account if the
9010 // condition is equals.
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00009011 ASSERT((static_cast<unsigned>(cc_) >> 28) < (1 << 13));
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009012 return ConditionField::encode(static_cast<unsigned>(cc_) >> 28)
9013 | StrictField::encode(strict_)
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00009014 | NeverNanNanField::encode(cc_ == eq ? never_nan_nan_ : false)
9015 | IncludeNumberCompareField::encode(include_number_compare_);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009016}
9017
9018
ager@chromium.orgac091b72010-05-05 07:34:42 +00009019void StringHelper::GenerateFastCharCodeAt(MacroAssembler* masm,
9020 Register object,
9021 Register index,
9022 Register scratch,
9023 Register result,
9024 Label* receiver_not_string,
9025 Label* index_not_smi,
9026 Label* index_out_of_range,
9027 Label* slow_case) {
9028 Label not_a_flat_string;
9029 Label try_again_with_new_string;
9030 Label ascii_string;
9031 Label got_char_code;
9032
9033 // If the receiver is a smi trigger the non-string case.
9034 __ BranchOnSmi(object, receiver_not_string);
9035
9036 // Fetch the instance type of the receiver into result register.
9037 __ ldr(result, FieldMemOperand(object, HeapObject::kMapOffset));
9038 __ ldrb(result, FieldMemOperand(result, Map::kInstanceTypeOffset));
9039 // If the receiver is not a string trigger the non-string case.
9040 __ tst(result, Operand(kIsNotStringMask));
9041 __ b(ne, receiver_not_string);
9042
9043 // If the index is non-smi trigger the non-smi case.
9044 __ BranchOnNotSmi(index, index_not_smi);
9045
9046 // Check for index out of range.
9047 __ ldr(scratch, FieldMemOperand(object, String::kLengthOffset));
9048 // Now scratch has the length of the string. Compare with the index.
9049 __ cmp(scratch, Operand(index));
9050 __ b(ls, index_out_of_range);
9051
9052 __ bind(&try_again_with_new_string);
9053 // ----------- S t a t e -------------
9054 // -- object : string to access
9055 // -- result : instance type of the string
9056 // -- scratch : non-negative index < length
9057 // -----------------------------------
9058
9059 // We need special handling for non-flat strings.
9060 ASSERT_EQ(0, kSeqStringTag);
9061 __ tst(result, Operand(kStringRepresentationMask));
9062 __ b(ne, &not_a_flat_string);
9063
9064 // Check for 1-byte or 2-byte string.
9065 ASSERT_EQ(0, kTwoByteStringTag);
9066 __ tst(result, Operand(kStringEncodingMask));
9067 __ b(ne, &ascii_string);
9068
9069 // 2-byte string. We can add without shifting since the Smi tag size is the
9070 // log2 of the number of bytes in a two-byte character.
9071 ASSERT_EQ(1, kSmiTagSize);
9072 ASSERT_EQ(0, kSmiShiftSize);
9073 __ add(scratch, object, Operand(index));
9074 __ ldrh(result, FieldMemOperand(scratch, SeqTwoByteString::kHeaderSize));
9075 __ jmp(&got_char_code);
9076
9077 // Handle non-flat strings.
9078 __ bind(&not_a_flat_string);
9079 __ and_(result, result, Operand(kStringRepresentationMask));
9080 __ cmp(result, Operand(kConsStringTag));
9081 __ b(ne, slow_case);
9082
9083 // ConsString.
9084 // Check whether the right hand side is the empty string (i.e. if
9085 // this is really a flat string in a cons string). If that is not
9086 // the case we would rather go to the runtime system now to flatten
9087 // the string.
9088 __ ldr(result, FieldMemOperand(object, ConsString::kSecondOffset));
9089 __ LoadRoot(scratch, Heap::kEmptyStringRootIndex);
9090 __ cmp(result, Operand(scratch));
9091 __ b(ne, slow_case);
9092
9093 // Get the first of the two strings and load its instance type.
9094 __ ldr(object, FieldMemOperand(object, ConsString::kFirstOffset));
9095 __ ldr(result, FieldMemOperand(object, HeapObject::kMapOffset));
9096 __ ldrb(result, FieldMemOperand(result, Map::kInstanceTypeOffset));
9097 __ jmp(&try_again_with_new_string);
9098
9099 // ASCII string.
9100 __ bind(&ascii_string);
9101 __ add(scratch, object, Operand(index, LSR, kSmiTagSize));
9102 __ ldrb(result, FieldMemOperand(scratch, SeqAsciiString::kHeaderSize));
9103
9104 __ bind(&got_char_code);
9105 __ mov(result, Operand(result, LSL, kSmiTagSize));
9106}
9107
9108
9109void StringHelper::GenerateCharFromCode(MacroAssembler* masm,
9110 Register code,
9111 Register scratch,
9112 Register result,
9113 InvokeFlag flag) {
9114 ASSERT(!code.is(result));
9115
9116 Label slow_case;
9117 Label exit;
9118
9119 // Fast case of Heap::LookupSingleCharacterStringFromCode.
9120 ASSERT(kSmiTag == 0);
9121 ASSERT(kSmiShiftSize == 0);
9122 ASSERT(IsPowerOf2(String::kMaxAsciiCharCode + 1));
9123 __ tst(code, Operand(kSmiTagMask |
9124 ((~String::kMaxAsciiCharCode) << kSmiTagSize)));
9125 __ b(nz, &slow_case);
9126
9127 ASSERT(kSmiTag == 0);
9128 __ mov(result, Operand(Factory::single_character_string_cache()));
9129 __ add(result, result, Operand(code, LSL, kPointerSizeLog2 - kSmiTagSize));
9130 __ ldr(result, MemOperand(result, FixedArray::kHeaderSize - kHeapObjectTag));
9131 __ LoadRoot(scratch, Heap::kUndefinedValueRootIndex);
9132 __ cmp(result, scratch);
9133 __ b(eq, &slow_case);
9134 __ b(&exit);
9135
9136 __ bind(&slow_case);
9137 if (flag == CALL_FUNCTION) {
9138 __ push(code);
9139 __ CallRuntime(Runtime::kCharFromCode, 1);
9140 if (!result.is(r0)) {
9141 __ mov(result, r0);
9142 }
9143 } else {
9144 ASSERT(flag == JUMP_FUNCTION);
9145 ASSERT(result.is(r0));
9146 __ push(code);
9147 __ TailCallRuntime(Runtime::kCharFromCode, 1, 1);
9148 }
9149
9150 __ bind(&exit);
9151 if (flag == JUMP_FUNCTION) {
9152 ASSERT(result.is(r0));
9153 __ Ret();
9154 }
9155}
9156
9157
9158void StringHelper::GenerateCopyCharacters(MacroAssembler* masm,
9159 Register dest,
9160 Register src,
9161 Register count,
9162 Register scratch,
9163 bool ascii) {
ager@chromium.org5c838252010-02-19 08:53:10 +00009164 Label loop;
9165 Label done;
9166 // This loop just copies one character at a time, as it is only used for very
9167 // short strings.
9168 if (!ascii) {
9169 __ add(count, count, Operand(count), SetCC);
9170 } else {
9171 __ cmp(count, Operand(0));
9172 }
9173 __ b(eq, &done);
9174
9175 __ bind(&loop);
9176 __ ldrb(scratch, MemOperand(src, 1, PostIndex));
9177 // Perform sub between load and dependent store to get the load time to
9178 // complete.
9179 __ sub(count, count, Operand(1), SetCC);
9180 __ strb(scratch, MemOperand(dest, 1, PostIndex));
9181 // last iteration.
9182 __ b(gt, &loop);
9183
9184 __ bind(&done);
9185}
9186
9187
9188enum CopyCharactersFlags {
9189 COPY_ASCII = 1,
9190 DEST_ALWAYS_ALIGNED = 2
9191};
9192
9193
ager@chromium.orgac091b72010-05-05 07:34:42 +00009194void StringHelper::GenerateCopyCharactersLong(MacroAssembler* masm,
9195 Register dest,
9196 Register src,
9197 Register count,
9198 Register scratch1,
9199 Register scratch2,
9200 Register scratch3,
9201 Register scratch4,
9202 Register scratch5,
9203 int flags) {
ager@chromium.org5c838252010-02-19 08:53:10 +00009204 bool ascii = (flags & COPY_ASCII) != 0;
9205 bool dest_always_aligned = (flags & DEST_ALWAYS_ALIGNED) != 0;
9206
9207 if (dest_always_aligned && FLAG_debug_code) {
9208 // Check that destination is actually word aligned if the flag says
9209 // that it is.
9210 __ tst(dest, Operand(kPointerAlignmentMask));
9211 __ Check(eq, "Destination of copy not aligned.");
9212 }
9213
9214 const int kReadAlignment = 4;
9215 const int kReadAlignmentMask = kReadAlignment - 1;
9216 // Ensure that reading an entire aligned word containing the last character
9217 // of a string will not read outside the allocated area (because we pad up
9218 // to kObjectAlignment).
9219 ASSERT(kObjectAlignment >= kReadAlignment);
9220 // Assumes word reads and writes are little endian.
9221 // Nothing to do for zero characters.
9222 Label done;
9223 if (!ascii) {
9224 __ add(count, count, Operand(count), SetCC);
9225 } else {
9226 __ cmp(count, Operand(0));
9227 }
9228 __ b(eq, &done);
9229
9230 // Assume that you cannot read (or write) unaligned.
9231 Label byte_loop;
9232 // Must copy at least eight bytes, otherwise just do it one byte at a time.
9233 __ cmp(count, Operand(8));
9234 __ add(count, dest, Operand(count));
9235 Register limit = count; // Read until src equals this.
9236 __ b(lt, &byte_loop);
9237
9238 if (!dest_always_aligned) {
9239 // Align dest by byte copying. Copies between zero and three bytes.
9240 __ and_(scratch4, dest, Operand(kReadAlignmentMask), SetCC);
9241 Label dest_aligned;
9242 __ b(eq, &dest_aligned);
9243 __ cmp(scratch4, Operand(2));
9244 __ ldrb(scratch1, MemOperand(src, 1, PostIndex));
9245 __ ldrb(scratch2, MemOperand(src, 1, PostIndex), le);
9246 __ ldrb(scratch3, MemOperand(src, 1, PostIndex), lt);
9247 __ strb(scratch1, MemOperand(dest, 1, PostIndex));
9248 __ strb(scratch2, MemOperand(dest, 1, PostIndex), le);
9249 __ strb(scratch3, MemOperand(dest, 1, PostIndex), lt);
9250 __ bind(&dest_aligned);
9251 }
9252
9253 Label simple_loop;
9254
9255 __ sub(scratch4, dest, Operand(src));
9256 __ and_(scratch4, scratch4, Operand(0x03), SetCC);
9257 __ b(eq, &simple_loop);
9258 // Shift register is number of bits in a source word that
9259 // must be combined with bits in the next source word in order
9260 // to create a destination word.
9261
9262 // Complex loop for src/dst that are not aligned the same way.
9263 {
9264 Label loop;
9265 __ mov(scratch4, Operand(scratch4, LSL, 3));
9266 Register left_shift = scratch4;
9267 __ and_(src, src, Operand(~3)); // Round down to load previous word.
9268 __ ldr(scratch1, MemOperand(src, 4, PostIndex));
9269 // Store the "shift" most significant bits of scratch in the least
9270 // signficant bits (i.e., shift down by (32-shift)).
9271 __ rsb(scratch2, left_shift, Operand(32));
9272 Register right_shift = scratch2;
9273 __ mov(scratch1, Operand(scratch1, LSR, right_shift));
9274
9275 __ bind(&loop);
9276 __ ldr(scratch3, MemOperand(src, 4, PostIndex));
9277 __ sub(scratch5, limit, Operand(dest));
9278 __ orr(scratch1, scratch1, Operand(scratch3, LSL, left_shift));
9279 __ str(scratch1, MemOperand(dest, 4, PostIndex));
9280 __ mov(scratch1, Operand(scratch3, LSR, right_shift));
9281 // Loop if four or more bytes left to copy.
9282 // Compare to eight, because we did the subtract before increasing dst.
9283 __ sub(scratch5, scratch5, Operand(8), SetCC);
9284 __ b(ge, &loop);
9285 }
9286 // There is now between zero and three bytes left to copy (negative that
9287 // number is in scratch5), and between one and three bytes already read into
9288 // scratch1 (eight times that number in scratch4). We may have read past
9289 // the end of the string, but because objects are aligned, we have not read
9290 // past the end of the object.
9291 // Find the minimum of remaining characters to move and preloaded characters
9292 // and write those as bytes.
9293 __ add(scratch5, scratch5, Operand(4), SetCC);
9294 __ b(eq, &done);
9295 __ cmp(scratch4, Operand(scratch5, LSL, 3), ne);
9296 // Move minimum of bytes read and bytes left to copy to scratch4.
9297 __ mov(scratch5, Operand(scratch4, LSR, 3), LeaveCC, lt);
9298 // Between one and three (value in scratch5) characters already read into
9299 // scratch ready to write.
9300 __ cmp(scratch5, Operand(2));
9301 __ strb(scratch1, MemOperand(dest, 1, PostIndex));
9302 __ mov(scratch1, Operand(scratch1, LSR, 8), LeaveCC, ge);
9303 __ strb(scratch1, MemOperand(dest, 1, PostIndex), ge);
9304 __ mov(scratch1, Operand(scratch1, LSR, 8), LeaveCC, gt);
9305 __ strb(scratch1, MemOperand(dest, 1, PostIndex), gt);
9306 // Copy any remaining bytes.
9307 __ b(&byte_loop);
9308
9309 // Simple loop.
9310 // Copy words from src to dst, until less than four bytes left.
9311 // Both src and dest are word aligned.
9312 __ bind(&simple_loop);
9313 {
9314 Label loop;
9315 __ bind(&loop);
9316 __ ldr(scratch1, MemOperand(src, 4, PostIndex));
9317 __ sub(scratch3, limit, Operand(dest));
9318 __ str(scratch1, MemOperand(dest, 4, PostIndex));
9319 // Compare to 8, not 4, because we do the substraction before increasing
9320 // dest.
9321 __ cmp(scratch3, Operand(8));
9322 __ b(ge, &loop);
9323 }
9324
9325 // Copy bytes from src to dst until dst hits limit.
9326 __ bind(&byte_loop);
9327 __ cmp(dest, Operand(limit));
9328 __ ldrb(scratch1, MemOperand(src, 1, PostIndex), lt);
9329 __ b(ge, &done);
9330 __ strb(scratch1, MemOperand(dest, 1, PostIndex));
9331 __ b(&byte_loop);
9332
9333 __ bind(&done);
9334}
9335
9336
ager@chromium.orgac091b72010-05-05 07:34:42 +00009337void StringHelper::GenerateTwoCharacterSymbolTableProbe(MacroAssembler* masm,
9338 Register c1,
9339 Register c2,
9340 Register scratch1,
9341 Register scratch2,
9342 Register scratch3,
9343 Register scratch4,
9344 Register scratch5,
9345 Label* not_found) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009346 // Register scratch3 is the general scratch register in this function.
9347 Register scratch = scratch3;
9348
9349 // Make sure that both characters are not digits as such strings has a
9350 // different hash algorithm. Don't try to look for these in the symbol table.
9351 Label not_array_index;
9352 __ sub(scratch, c1, Operand(static_cast<int>('0')));
9353 __ cmp(scratch, Operand(static_cast<int>('9' - '0')));
9354 __ b(hi, &not_array_index);
9355 __ sub(scratch, c2, Operand(static_cast<int>('0')));
9356 __ cmp(scratch, Operand(static_cast<int>('9' - '0')));
9357
9358 // If check failed combine both characters into single halfword.
9359 // This is required by the contract of the method: code at the
9360 // not_found branch expects this combination in c1 register
9361 __ orr(c1, c1, Operand(c2, LSL, kBitsPerByte), LeaveCC, ls);
9362 __ b(ls, not_found);
9363
9364 __ bind(&not_array_index);
9365 // Calculate the two character string hash.
9366 Register hash = scratch1;
ager@chromium.orgac091b72010-05-05 07:34:42 +00009367 StringHelper::GenerateHashInit(masm, hash, c1);
9368 StringHelper::GenerateHashAddCharacter(masm, hash, c2);
9369 StringHelper::GenerateHashGetHash(masm, hash);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009370
9371 // Collect the two characters in a register.
9372 Register chars = c1;
9373 __ orr(chars, chars, Operand(c2, LSL, kBitsPerByte));
9374
9375 // chars: two character string, char 1 in byte 0 and char 2 in byte 1.
9376 // hash: hash of two character string.
9377
9378 // Load symbol table
9379 // Load address of first element of the symbol table.
9380 Register symbol_table = c2;
9381 __ LoadRoot(symbol_table, Heap::kSymbolTableRootIndex);
9382
9383 // Load undefined value
9384 Register undefined = scratch4;
9385 __ LoadRoot(undefined, Heap::kUndefinedValueRootIndex);
9386
9387 // Calculate capacity mask from the symbol table capacity.
9388 Register mask = scratch2;
9389 __ ldr(mask, FieldMemOperand(symbol_table, SymbolTable::kCapacityOffset));
9390 __ mov(mask, Operand(mask, ASR, 1));
9391 __ sub(mask, mask, Operand(1));
9392
9393 // Calculate untagged address of the first element of the symbol table.
9394 Register first_symbol_table_element = symbol_table;
9395 __ add(first_symbol_table_element, symbol_table,
9396 Operand(SymbolTable::kElementsStartOffset - kHeapObjectTag));
9397
9398 // Registers
9399 // chars: two character string, char 1 in byte 0 and char 2 in byte 1.
9400 // hash: hash of two character string
9401 // mask: capacity mask
9402 // first_symbol_table_element: address of the first element of
9403 // the symbol table
9404 // scratch: -
9405
9406 // Perform a number of probes in the symbol table.
9407 static const int kProbes = 4;
9408 Label found_in_symbol_table;
9409 Label next_probe[kProbes];
9410 for (int i = 0; i < kProbes; i++) {
9411 Register candidate = scratch5; // Scratch register contains candidate.
9412
9413 // Calculate entry in symbol table.
9414 if (i > 0) {
9415 __ add(candidate, hash, Operand(SymbolTable::GetProbeOffset(i)));
9416 } else {
9417 __ mov(candidate, hash);
9418 }
9419
9420 __ and_(candidate, candidate, Operand(mask));
9421
9422 // Load the entry from the symble table.
9423 ASSERT_EQ(1, SymbolTable::kEntrySize);
9424 __ ldr(candidate,
9425 MemOperand(first_symbol_table_element,
9426 candidate,
9427 LSL,
9428 kPointerSizeLog2));
9429
9430 // If entry is undefined no string with this hash can be found.
9431 __ cmp(candidate, undefined);
9432 __ b(eq, not_found);
9433
9434 // If length is not 2 the string is not a candidate.
9435 __ ldr(scratch, FieldMemOperand(candidate, String::kLengthOffset));
ager@chromium.orgac091b72010-05-05 07:34:42 +00009436 __ cmp(scratch, Operand(Smi::FromInt(2)));
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009437 __ b(ne, &next_probe[i]);
9438
9439 // Check that the candidate is a non-external ascii string.
9440 __ ldr(scratch, FieldMemOperand(candidate, HeapObject::kMapOffset));
9441 __ ldrb(scratch, FieldMemOperand(scratch, Map::kInstanceTypeOffset));
9442 __ JumpIfInstanceTypeIsNotSequentialAscii(scratch, scratch,
9443 &next_probe[i]);
9444
9445 // Check if the two characters match.
9446 // Assumes that word load is little endian.
9447 __ ldrh(scratch, FieldMemOperand(candidate, SeqAsciiString::kHeaderSize));
9448 __ cmp(chars, scratch);
9449 __ b(eq, &found_in_symbol_table);
9450 __ bind(&next_probe[i]);
9451 }
9452
9453 // No matching 2 character string found by probing.
9454 __ jmp(not_found);
9455
9456 // Scratch register contains result when we fall through to here.
9457 Register result = scratch;
9458 __ bind(&found_in_symbol_table);
ager@chromium.org357bf652010-04-12 11:30:10 +00009459 __ Move(r0, result);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009460}
9461
9462
ager@chromium.orgac091b72010-05-05 07:34:42 +00009463void StringHelper::GenerateHashInit(MacroAssembler* masm,
9464 Register hash,
9465 Register character) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009466 // hash = character + (character << 10);
9467 __ add(hash, character, Operand(character, LSL, 10));
9468 // hash ^= hash >> 6;
9469 __ eor(hash, hash, Operand(hash, ASR, 6));
9470}
9471
9472
ager@chromium.orgac091b72010-05-05 07:34:42 +00009473void StringHelper::GenerateHashAddCharacter(MacroAssembler* masm,
9474 Register hash,
9475 Register character) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009476 // hash += character;
9477 __ add(hash, hash, Operand(character));
9478 // hash += hash << 10;
9479 __ add(hash, hash, Operand(hash, LSL, 10));
9480 // hash ^= hash >> 6;
9481 __ eor(hash, hash, Operand(hash, ASR, 6));
9482}
9483
9484
ager@chromium.orgac091b72010-05-05 07:34:42 +00009485void StringHelper::GenerateHashGetHash(MacroAssembler* masm,
9486 Register hash) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009487 // hash += hash << 3;
9488 __ add(hash, hash, Operand(hash, LSL, 3));
9489 // hash ^= hash >> 11;
9490 __ eor(hash, hash, Operand(hash, ASR, 11));
9491 // hash += hash << 15;
9492 __ add(hash, hash, Operand(hash, LSL, 15), SetCC);
9493
9494 // if (hash == 0) hash = 27;
9495 __ mov(hash, Operand(27), LeaveCC, nz);
9496}
9497
9498
ager@chromium.org5c838252010-02-19 08:53:10 +00009499void SubStringStub::Generate(MacroAssembler* masm) {
9500 Label runtime;
9501
9502 // Stack frame on entry.
9503 // lr: return address
9504 // sp[0]: to
9505 // sp[4]: from
9506 // sp[8]: string
9507
9508 // This stub is called from the native-call %_SubString(...), so
9509 // nothing can be assumed about the arguments. It is tested that:
9510 // "string" is a sequential string,
9511 // both "from" and "to" are smis, and
9512 // 0 <= from <= to <= string.length.
9513 // If any of these assumptions fail, we call the runtime system.
9514
9515 static const int kToOffset = 0 * kPointerSize;
9516 static const int kFromOffset = 1 * kPointerSize;
9517 static const int kStringOffset = 2 * kPointerSize;
9518
9519
9520 // Check bounds and smi-ness.
9521 __ ldr(r7, MemOperand(sp, kToOffset));
9522 __ ldr(r6, MemOperand(sp, kFromOffset));
9523 ASSERT_EQ(0, kSmiTag);
9524 ASSERT_EQ(1, kSmiTagSize + kSmiShiftSize);
9525 // I.e., arithmetic shift right by one un-smi-tags.
9526 __ mov(r2, Operand(r7, ASR, 1), SetCC);
9527 __ mov(r3, Operand(r6, ASR, 1), SetCC, cc);
9528 // If either r2 or r6 had the smi tag bit set, then carry is set now.
9529 __ b(cs, &runtime); // Either "from" or "to" is not a smi.
9530 __ b(mi, &runtime); // From is negative.
9531
9532 __ sub(r2, r2, Operand(r3), SetCC);
9533 __ b(mi, &runtime); // Fail if from > to.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009534 // Special handling of sub-strings of length 1 and 2. One character strings
9535 // are handled in the runtime system (looked up in the single character
9536 // cache). Two character strings are looked for in the symbol cache.
ager@chromium.org5c838252010-02-19 08:53:10 +00009537 __ cmp(r2, Operand(2));
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009538 __ b(lt, &runtime);
ager@chromium.org5c838252010-02-19 08:53:10 +00009539
9540 // r2: length
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009541 // r3: from index (untaged smi)
ager@chromium.org5c838252010-02-19 08:53:10 +00009542 // r6: from (smi)
9543 // r7: to (smi)
9544
9545 // Make sure first argument is a sequential (or flat) string.
9546 __ ldr(r5, MemOperand(sp, kStringOffset));
9547 ASSERT_EQ(0, kSmiTag);
9548 __ tst(r5, Operand(kSmiTagMask));
9549 __ b(eq, &runtime);
9550 Condition is_string = masm->IsObjectStringType(r5, r1);
9551 __ b(NegateCondition(is_string), &runtime);
9552
9553 // r1: instance type
9554 // r2: length
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009555 // r3: from index (untaged smi)
ager@chromium.org5c838252010-02-19 08:53:10 +00009556 // r5: string
9557 // r6: from (smi)
9558 // r7: to (smi)
9559 Label seq_string;
9560 __ and_(r4, r1, Operand(kStringRepresentationMask));
9561 ASSERT(kSeqStringTag < kConsStringTag);
9562 ASSERT(kExternalStringTag > kConsStringTag);
9563 __ cmp(r4, Operand(kConsStringTag));
9564 __ b(gt, &runtime); // External strings go to runtime.
9565 __ b(lt, &seq_string); // Sequential strings are handled directly.
9566
9567 // Cons string. Try to recurse (once) on the first substring.
9568 // (This adds a little more generality than necessary to handle flattened
9569 // cons strings, but not much).
9570 __ ldr(r5, FieldMemOperand(r5, ConsString::kFirstOffset));
9571 __ ldr(r4, FieldMemOperand(r5, HeapObject::kMapOffset));
9572 __ ldrb(r1, FieldMemOperand(r4, Map::kInstanceTypeOffset));
9573 __ tst(r1, Operand(kStringRepresentationMask));
9574 ASSERT_EQ(0, kSeqStringTag);
9575 __ b(ne, &runtime); // Cons and External strings go to runtime.
9576
9577 // Definitly a sequential string.
9578 __ bind(&seq_string);
9579
9580 // r1: instance type.
9581 // r2: length
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009582 // r3: from index (untaged smi)
ager@chromium.org5c838252010-02-19 08:53:10 +00009583 // r5: string
9584 // r6: from (smi)
9585 // r7: to (smi)
9586 __ ldr(r4, FieldMemOperand(r5, String::kLengthOffset));
ager@chromium.orgac091b72010-05-05 07:34:42 +00009587 __ cmp(r4, Operand(r7));
ager@chromium.org5c838252010-02-19 08:53:10 +00009588 __ b(lt, &runtime); // Fail if to > length.
9589
9590 // r1: instance type.
9591 // r2: result string length.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009592 // r3: from index (untaged smi)
ager@chromium.org5c838252010-02-19 08:53:10 +00009593 // r5: string.
9594 // r6: from offset (smi)
9595 // Check for flat ascii string.
9596 Label non_ascii_flat;
9597 __ tst(r1, Operand(kStringEncodingMask));
9598 ASSERT_EQ(0, kTwoByteStringTag);
9599 __ b(eq, &non_ascii_flat);
9600
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009601 Label result_longer_than_two;
9602 __ cmp(r2, Operand(2));
9603 __ b(gt, &result_longer_than_two);
9604
9605 // Sub string of length 2 requested.
9606 // Get the two characters forming the sub string.
9607 __ add(r5, r5, Operand(r3));
9608 __ ldrb(r3, FieldMemOperand(r5, SeqAsciiString::kHeaderSize));
9609 __ ldrb(r4, FieldMemOperand(r5, SeqAsciiString::kHeaderSize + 1));
9610
9611 // Try to lookup two character string in symbol table.
9612 Label make_two_character_string;
ager@chromium.orgac091b72010-05-05 07:34:42 +00009613 StringHelper::GenerateTwoCharacterSymbolTableProbe(
9614 masm, r3, r4, r1, r5, r6, r7, r9, &make_two_character_string);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009615 __ IncrementCounter(&Counters::sub_string_native, 1, r3, r4);
9616 __ add(sp, sp, Operand(3 * kPointerSize));
9617 __ Ret();
9618
9619 // r2: result string length.
9620 // r3: two characters combined into halfword in little endian byte order.
9621 __ bind(&make_two_character_string);
9622 __ AllocateAsciiString(r0, r2, r4, r5, r9, &runtime);
9623 __ strh(r3, FieldMemOperand(r0, SeqAsciiString::kHeaderSize));
9624 __ IncrementCounter(&Counters::sub_string_native, 1, r3, r4);
9625 __ add(sp, sp, Operand(3 * kPointerSize));
9626 __ Ret();
9627
9628 __ bind(&result_longer_than_two);
9629
ager@chromium.org5c838252010-02-19 08:53:10 +00009630 // Allocate the result.
9631 __ AllocateAsciiString(r0, r2, r3, r4, r1, &runtime);
9632
9633 // r0: result string.
9634 // r2: result string length.
9635 // r5: string.
9636 // r6: from offset (smi)
9637 // Locate first character of result.
9638 __ add(r1, r0, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
9639 // Locate 'from' character of string.
9640 __ add(r5, r5, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
9641 __ add(r5, r5, Operand(r6, ASR, 1));
9642
9643 // r0: result string.
9644 // r1: first character of result string.
9645 // r2: result string length.
9646 // r5: first character of sub string to copy.
9647 ASSERT_EQ(0, SeqAsciiString::kHeaderSize & kObjectAlignmentMask);
ager@chromium.orgac091b72010-05-05 07:34:42 +00009648 StringHelper::GenerateCopyCharactersLong(masm, r1, r5, r2, r3, r4, r6, r7, r9,
9649 COPY_ASCII | DEST_ALWAYS_ALIGNED);
ager@chromium.org5c838252010-02-19 08:53:10 +00009650 __ IncrementCounter(&Counters::sub_string_native, 1, r3, r4);
9651 __ add(sp, sp, Operand(3 * kPointerSize));
9652 __ Ret();
9653
9654 __ bind(&non_ascii_flat);
9655 // r2: result string length.
9656 // r5: string.
9657 // r6: from offset (smi)
9658 // Check for flat two byte string.
9659
9660 // Allocate the result.
9661 __ AllocateTwoByteString(r0, r2, r1, r3, r4, &runtime);
9662
9663 // r0: result string.
9664 // r2: result string length.
9665 // r5: string.
9666 // Locate first character of result.
9667 __ add(r1, r0, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
9668 // Locate 'from' character of string.
9669 __ add(r5, r5, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
9670 // As "from" is a smi it is 2 times the value which matches the size of a two
9671 // byte character.
9672 __ add(r5, r5, Operand(r6));
9673
9674 // r0: result string.
9675 // r1: first character of result.
9676 // r2: result length.
9677 // r5: first character of string to copy.
9678 ASSERT_EQ(0, SeqTwoByteString::kHeaderSize & kObjectAlignmentMask);
ager@chromium.orgac091b72010-05-05 07:34:42 +00009679 StringHelper::GenerateCopyCharactersLong(masm, r1, r5, r2, r3, r4, r6, r7, r9,
9680 DEST_ALWAYS_ALIGNED);
ager@chromium.org5c838252010-02-19 08:53:10 +00009681 __ IncrementCounter(&Counters::sub_string_native, 1, r3, r4);
9682 __ add(sp, sp, Operand(3 * kPointerSize));
9683 __ Ret();
9684
9685 // Just jump to runtime to create the sub string.
9686 __ bind(&runtime);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009687 __ TailCallRuntime(Runtime::kSubString, 3, 1);
ager@chromium.org5c838252010-02-19 08:53:10 +00009688}
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009689
9690
9691void StringCompareStub::GenerateCompareFlatAsciiStrings(MacroAssembler* masm,
9692 Register left,
9693 Register right,
9694 Register scratch1,
9695 Register scratch2,
9696 Register scratch3,
9697 Register scratch4) {
9698 Label compare_lengths;
9699 // Find minimum length and length difference.
9700 __ ldr(scratch1, FieldMemOperand(left, String::kLengthOffset));
9701 __ ldr(scratch2, FieldMemOperand(right, String::kLengthOffset));
9702 __ sub(scratch3, scratch1, Operand(scratch2), SetCC);
9703 Register length_delta = scratch3;
9704 __ mov(scratch1, scratch2, LeaveCC, gt);
9705 Register min_length = scratch1;
ager@chromium.orgac091b72010-05-05 07:34:42 +00009706 ASSERT(kSmiTag == 0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009707 __ tst(min_length, Operand(min_length));
9708 __ b(eq, &compare_lengths);
9709
ager@chromium.orgac091b72010-05-05 07:34:42 +00009710 // Untag smi.
9711 __ mov(min_length, Operand(min_length, ASR, kSmiTagSize));
9712
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009713 // Setup registers so that we only need to increment one register
9714 // in the loop.
9715 __ add(scratch2, min_length,
9716 Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
9717 __ add(left, left, Operand(scratch2));
9718 __ add(right, right, Operand(scratch2));
9719 // Registers left and right points to the min_length character of strings.
9720 __ rsb(min_length, min_length, Operand(-1));
9721 Register index = min_length;
9722 // Index starts at -min_length.
9723
9724 {
9725 // Compare loop.
9726 Label loop;
9727 __ bind(&loop);
9728 // Compare characters.
9729 __ add(index, index, Operand(1), SetCC);
9730 __ ldrb(scratch2, MemOperand(left, index), ne);
9731 __ ldrb(scratch4, MemOperand(right, index), ne);
9732 // Skip to compare lengths with eq condition true.
9733 __ b(eq, &compare_lengths);
9734 __ cmp(scratch2, scratch4);
9735 __ b(eq, &loop);
9736 // Fallthrough with eq condition false.
9737 }
9738 // Compare lengths - strings up to min-length are equal.
9739 __ bind(&compare_lengths);
9740 ASSERT(Smi::FromInt(EQUAL) == static_cast<Smi*>(0));
9741 // Use zero length_delta as result.
9742 __ mov(r0, Operand(length_delta), SetCC, eq);
9743 // Fall through to here if characters compare not-equal.
9744 __ mov(r0, Operand(Smi::FromInt(GREATER)), LeaveCC, gt);
9745 __ mov(r0, Operand(Smi::FromInt(LESS)), LeaveCC, lt);
9746 __ Ret();
9747}
9748
9749
9750void StringCompareStub::Generate(MacroAssembler* masm) {
9751 Label runtime;
9752
9753 // Stack frame on entry.
ager@chromium.org5c838252010-02-19 08:53:10 +00009754 // sp[0]: right string
9755 // sp[4]: left string
9756 __ ldr(r0, MemOperand(sp, 1 * kPointerSize)); // left
9757 __ ldr(r1, MemOperand(sp, 0 * kPointerSize)); // right
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009758
9759 Label not_same;
9760 __ cmp(r0, r1);
9761 __ b(ne, &not_same);
9762 ASSERT_EQ(0, EQUAL);
9763 ASSERT_EQ(0, kSmiTag);
9764 __ mov(r0, Operand(Smi::FromInt(EQUAL)));
9765 __ IncrementCounter(&Counters::string_compare_native, 1, r1, r2);
9766 __ add(sp, sp, Operand(2 * kPointerSize));
9767 __ Ret();
9768
9769 __ bind(&not_same);
9770
9771 // Check that both objects are sequential ascii strings.
9772 __ JumpIfNotBothSequentialAsciiStrings(r0, r1, r2, r3, &runtime);
9773
9774 // Compare flat ascii strings natively. Remove arguments from stack first.
9775 __ IncrementCounter(&Counters::string_compare_native, 1, r2, r3);
9776 __ add(sp, sp, Operand(2 * kPointerSize));
9777 GenerateCompareFlatAsciiStrings(masm, r0, r1, r2, r3, r4, r5);
9778
9779 // Call the runtime; it returns -1 (less), 0 (equal), or 1 (greater)
9780 // tagged as a small integer.
9781 __ bind(&runtime);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009782 __ TailCallRuntime(Runtime::kStringCompare, 2, 1);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009783}
9784
9785
ager@chromium.org5c838252010-02-19 08:53:10 +00009786void StringAddStub::Generate(MacroAssembler* masm) {
9787 Label string_add_runtime;
9788 // Stack on entry:
9789 // sp[0]: second argument.
9790 // sp[4]: first argument.
9791
9792 // Load the two arguments.
9793 __ ldr(r0, MemOperand(sp, 1 * kPointerSize)); // First argument.
9794 __ ldr(r1, MemOperand(sp, 0 * kPointerSize)); // Second argument.
9795
9796 // Make sure that both arguments are strings if not known in advance.
9797 if (string_check_) {
9798 ASSERT_EQ(0, kSmiTag);
9799 __ JumpIfEitherSmi(r0, r1, &string_add_runtime);
9800 // Load instance types.
9801 __ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset));
9802 __ ldr(r5, FieldMemOperand(r1, HeapObject::kMapOffset));
9803 __ ldrb(r4, FieldMemOperand(r4, Map::kInstanceTypeOffset));
9804 __ ldrb(r5, FieldMemOperand(r5, Map::kInstanceTypeOffset));
9805 ASSERT_EQ(0, kStringTag);
9806 // If either is not a string, go to runtime.
9807 __ tst(r4, Operand(kIsNotStringMask));
9808 __ tst(r5, Operand(kIsNotStringMask), eq);
9809 __ b(ne, &string_add_runtime);
9810 }
9811
9812 // Both arguments are strings.
9813 // r0: first string
9814 // r1: second string
9815 // r4: first string instance type (if string_check_)
9816 // r5: second string instance type (if string_check_)
9817 {
9818 Label strings_not_empty;
9819 // Check if either of the strings are empty. In that case return the other.
9820 __ ldr(r2, FieldMemOperand(r0, String::kLengthOffset));
9821 __ ldr(r3, FieldMemOperand(r1, String::kLengthOffset));
ager@chromium.orgac091b72010-05-05 07:34:42 +00009822 ASSERT(kSmiTag == 0);
9823 __ cmp(r2, Operand(Smi::FromInt(0))); // Test if first string is empty.
ager@chromium.org5c838252010-02-19 08:53:10 +00009824 __ mov(r0, Operand(r1), LeaveCC, eq); // If first is empty, return second.
ager@chromium.orgac091b72010-05-05 07:34:42 +00009825 ASSERT(kSmiTag == 0);
9826 // Else test if second string is empty.
9827 __ cmp(r3, Operand(Smi::FromInt(0)), ne);
ager@chromium.org5c838252010-02-19 08:53:10 +00009828 __ b(ne, &strings_not_empty); // If either string was empty, return r0.
9829
9830 __ IncrementCounter(&Counters::string_add_native, 1, r2, r3);
9831 __ add(sp, sp, Operand(2 * kPointerSize));
9832 __ Ret();
9833
9834 __ bind(&strings_not_empty);
9835 }
9836
ager@chromium.orgac091b72010-05-05 07:34:42 +00009837 __ mov(r2, Operand(r2, ASR, kSmiTagSize));
9838 __ mov(r3, Operand(r3, ASR, kSmiTagSize));
ager@chromium.org5c838252010-02-19 08:53:10 +00009839 // Both strings are non-empty.
9840 // r0: first string
9841 // r1: second string
9842 // r2: length of first string
9843 // r3: length of second string
9844 // r4: first string instance type (if string_check_)
9845 // r5: second string instance type (if string_check_)
9846 // Look at the length of the result of adding the two strings.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009847 Label string_add_flat_result, longer_than_two;
ager@chromium.org5c838252010-02-19 08:53:10 +00009848 // Adding two lengths can't overflow.
9849 ASSERT(String::kMaxLength * 2 > String::kMaxLength);
9850 __ add(r6, r2, Operand(r3));
9851 // Use the runtime system when adding two one character strings, as it
9852 // contains optimizations for this specific case using the symbol table.
9853 __ cmp(r6, Operand(2));
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009854 __ b(ne, &longer_than_two);
9855
9856 // Check that both strings are non-external ascii strings.
9857 if (!string_check_) {
9858 __ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset));
9859 __ ldr(r5, FieldMemOperand(r1, HeapObject::kMapOffset));
9860 __ ldrb(r4, FieldMemOperand(r4, Map::kInstanceTypeOffset));
9861 __ ldrb(r5, FieldMemOperand(r5, Map::kInstanceTypeOffset));
9862 }
9863 __ JumpIfBothInstanceTypesAreNotSequentialAscii(r4, r5, r6, r7,
9864 &string_add_runtime);
9865
9866 // Get the two characters forming the sub string.
9867 __ ldrb(r2, FieldMemOperand(r0, SeqAsciiString::kHeaderSize));
9868 __ ldrb(r3, FieldMemOperand(r1, SeqAsciiString::kHeaderSize));
9869
9870 // Try to lookup two character string in symbol table. If it is not found
9871 // just allocate a new one.
9872 Label make_two_character_string;
ager@chromium.orgac091b72010-05-05 07:34:42 +00009873 StringHelper::GenerateTwoCharacterSymbolTableProbe(
9874 masm, r2, r3, r6, r7, r4, r5, r9, &make_two_character_string);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009875 __ IncrementCounter(&Counters::string_add_native, 1, r2, r3);
9876 __ add(sp, sp, Operand(2 * kPointerSize));
9877 __ Ret();
9878
9879 __ bind(&make_two_character_string);
9880 // Resulting string has length 2 and first chars of two strings
9881 // are combined into single halfword in r2 register.
9882 // So we can fill resulting string without two loops by a single
9883 // halfword store instruction (which assumes that processor is
9884 // in a little endian mode)
9885 __ mov(r6, Operand(2));
9886 __ AllocateAsciiString(r0, r6, r4, r5, r9, &string_add_runtime);
9887 __ strh(r2, FieldMemOperand(r0, SeqAsciiString::kHeaderSize));
9888 __ IncrementCounter(&Counters::string_add_native, 1, r2, r3);
9889 __ add(sp, sp, Operand(2 * kPointerSize));
9890 __ Ret();
9891
9892 __ bind(&longer_than_two);
ager@chromium.org5c838252010-02-19 08:53:10 +00009893 // Check if resulting string will be flat.
9894 __ cmp(r6, Operand(String::kMinNonFlatLength));
9895 __ b(lt, &string_add_flat_result);
9896 // Handle exceptionally long strings in the runtime system.
9897 ASSERT((String::kMaxLength & 0x80000000) == 0);
9898 ASSERT(IsPowerOf2(String::kMaxLength + 1));
9899 // kMaxLength + 1 is representable as shifted literal, kMaxLength is not.
9900 __ cmp(r6, Operand(String::kMaxLength + 1));
9901 __ b(hs, &string_add_runtime);
9902
9903 // If result is not supposed to be flat, allocate a cons string object.
9904 // If both strings are ascii the result is an ascii cons string.
9905 if (!string_check_) {
9906 __ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset));
9907 __ ldr(r5, FieldMemOperand(r1, HeapObject::kMapOffset));
9908 __ ldrb(r4, FieldMemOperand(r4, Map::kInstanceTypeOffset));
9909 __ ldrb(r5, FieldMemOperand(r5, Map::kInstanceTypeOffset));
9910 }
9911 Label non_ascii, allocated;
9912 ASSERT_EQ(0, kTwoByteStringTag);
9913 __ tst(r4, Operand(kStringEncodingMask));
9914 __ tst(r5, Operand(kStringEncodingMask), ne);
9915 __ b(eq, &non_ascii);
9916
9917 // Allocate an ASCII cons string.
9918 __ AllocateAsciiConsString(r7, r6, r4, r5, &string_add_runtime);
9919 __ bind(&allocated);
9920 // Fill the fields of the cons string.
9921 __ str(r0, FieldMemOperand(r7, ConsString::kFirstOffset));
9922 __ str(r1, FieldMemOperand(r7, ConsString::kSecondOffset));
9923 __ mov(r0, Operand(r7));
9924 __ IncrementCounter(&Counters::string_add_native, 1, r2, r3);
9925 __ add(sp, sp, Operand(2 * kPointerSize));
9926 __ Ret();
9927
9928 __ bind(&non_ascii);
9929 // Allocate a two byte cons string.
9930 __ AllocateTwoByteConsString(r7, r6, r4, r5, &string_add_runtime);
9931 __ jmp(&allocated);
9932
9933 // Handle creating a flat result. First check that both strings are
9934 // sequential and that they have the same encoding.
9935 // r0: first string
9936 // r1: second string
9937 // r2: length of first string
9938 // r3: length of second string
9939 // r4: first string instance type (if string_check_)
9940 // r5: second string instance type (if string_check_)
9941 // r6: sum of lengths.
9942 __ bind(&string_add_flat_result);
9943 if (!string_check_) {
9944 __ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset));
9945 __ ldr(r5, FieldMemOperand(r1, HeapObject::kMapOffset));
9946 __ ldrb(r4, FieldMemOperand(r4, Map::kInstanceTypeOffset));
9947 __ ldrb(r5, FieldMemOperand(r5, Map::kInstanceTypeOffset));
9948 }
9949 // Check that both strings are sequential.
9950 ASSERT_EQ(0, kSeqStringTag);
9951 __ tst(r4, Operand(kStringRepresentationMask));
9952 __ tst(r5, Operand(kStringRepresentationMask), eq);
9953 __ b(ne, &string_add_runtime);
9954 // Now check if both strings have the same encoding (ASCII/Two-byte).
9955 // r0: first string.
9956 // r1: second string.
9957 // r2: length of first string.
9958 // r3: length of second string.
9959 // r6: sum of lengths..
9960 Label non_ascii_string_add_flat_result;
9961 ASSERT(IsPowerOf2(kStringEncodingMask)); // Just one bit to test.
9962 __ eor(r7, r4, Operand(r5));
9963 __ tst(r7, Operand(kStringEncodingMask));
9964 __ b(ne, &string_add_runtime);
9965 // And see if it's ASCII or two-byte.
9966 __ tst(r4, Operand(kStringEncodingMask));
9967 __ b(eq, &non_ascii_string_add_flat_result);
9968
9969 // Both strings are sequential ASCII strings. We also know that they are
9970 // short (since the sum of the lengths is less than kMinNonFlatLength).
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009971 // r6: length of resulting flat string
ager@chromium.org5c838252010-02-19 08:53:10 +00009972 __ AllocateAsciiString(r7, r6, r4, r5, r9, &string_add_runtime);
9973 // Locate first character of result.
9974 __ add(r6, r7, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
9975 // Locate first character of first argument.
9976 __ add(r0, r0, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
9977 // r0: first character of first string.
9978 // r1: second string.
9979 // r2: length of first string.
9980 // r3: length of second string.
9981 // r6: first character of result.
9982 // r7: result string.
ager@chromium.orgac091b72010-05-05 07:34:42 +00009983 StringHelper::GenerateCopyCharacters(masm, r6, r0, r2, r4, true);
ager@chromium.org5c838252010-02-19 08:53:10 +00009984
9985 // Load second argument and locate first character.
9986 __ add(r1, r1, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
9987 // r1: first character of second string.
9988 // r3: length of second string.
9989 // r6: next character of result.
9990 // r7: result string.
ager@chromium.orgac091b72010-05-05 07:34:42 +00009991 StringHelper::GenerateCopyCharacters(masm, r6, r1, r3, r4, true);
ager@chromium.org5c838252010-02-19 08:53:10 +00009992 __ mov(r0, Operand(r7));
9993 __ IncrementCounter(&Counters::string_add_native, 1, r2, r3);
9994 __ add(sp, sp, Operand(2 * kPointerSize));
9995 __ Ret();
9996
9997 __ bind(&non_ascii_string_add_flat_result);
9998 // Both strings are sequential two byte strings.
9999 // r0: first string.
10000 // r1: second string.
10001 // r2: length of first string.
10002 // r3: length of second string.
10003 // r6: sum of length of strings.
10004 __ AllocateTwoByteString(r7, r6, r4, r5, r9, &string_add_runtime);
10005 // r0: first string.
10006 // r1: second string.
10007 // r2: length of first string.
10008 // r3: length of second string.
10009 // r7: result string.
10010
10011 // Locate first character of result.
10012 __ add(r6, r7, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
10013 // Locate first character of first argument.
10014 __ add(r0, r0, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
10015
10016 // r0: first character of first string.
10017 // r1: second string.
10018 // r2: length of first string.
10019 // r3: length of second string.
10020 // r6: first character of result.
10021 // r7: result string.
ager@chromium.orgac091b72010-05-05 07:34:42 +000010022 StringHelper::GenerateCopyCharacters(masm, r6, r0, r2, r4, false);
ager@chromium.org5c838252010-02-19 08:53:10 +000010023
10024 // Locate first character of second argument.
10025 __ add(r1, r1, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
10026
10027 // r1: first character of second string.
10028 // r3: length of second string.
10029 // r6: next character of result (after copy of first string).
10030 // r7: result string.
ager@chromium.orgac091b72010-05-05 07:34:42 +000010031 StringHelper::GenerateCopyCharacters(masm, r6, r1, r3, r4, false);
ager@chromium.org5c838252010-02-19 08:53:10 +000010032
10033 __ mov(r0, Operand(r7));
10034 __ IncrementCounter(&Counters::string_add_native, 1, r2, r3);
10035 __ add(sp, sp, Operand(2 * kPointerSize));
10036 __ Ret();
10037
10038 // Just jump to runtime to add the two strings.
10039 __ bind(&string_add_runtime);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010040 __ TailCallRuntime(Runtime::kStringAdd, 2, 1);
ager@chromium.org5c838252010-02-19 08:53:10 +000010041}
10042
10043
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000010044#undef __
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010045
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010046} } // namespace v8::internal
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +000010047
10048#endif // V8_TARGET_ARCH_ARM