blob: b6639ae670fdae10385da288218b21faab00342e [file] [log] [blame]
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001// Copyright 2010 the V8 project authors. All rights reserved.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28#include "v8.h"
29
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +000030#if defined(V8_TARGET_ARCH_ARM)
31
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000032#include "bootstrapper.h"
33#include "codegen-inl.h"
ager@chromium.orgc4c92722009-11-18 14:12:51 +000034#include "compiler.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000035#include "debug.h"
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000036#include "ic-inl.h"
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000037#include "jsregexp.h"
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +000038#include "jump-target-light-inl.h"
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000039#include "parser.h"
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000040#include "regexp-macro-assembler.h"
41#include "regexp-stack.h"
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000042#include "register-allocator-inl.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000043#include "runtime.h"
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000044#include "scopes.h"
sgjesse@chromium.org833cdd72010-02-26 10:06:16 +000045#include "virtual-frame-inl.h"
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +000046#include "virtual-frame-arm-inl.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000047
kasperl@chromium.org71affb52009-05-26 05:44:31 +000048namespace v8 {
49namespace internal {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000050
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +000051
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000052static void EmitIdenticalObjectComparison(MacroAssembler* masm,
53 Label* slow,
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000054 Condition cc,
55 bool never_nan_nan);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000056static void EmitSmiNonsmiComparison(MacroAssembler* masm,
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000057 Label* lhs_not_nan,
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000058 Label* slow,
59 bool strict);
60static void EmitTwoNonNanDoubleComparison(MacroAssembler* masm, Condition cc);
61static void EmitStrictTwoHeapObjectCompare(MacroAssembler* masm);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000062static void MultiplyByKnownInt(MacroAssembler* masm,
63 Register source,
64 Register destination,
65 int known_int);
66static bool IsEasyToMultiplyBy(int x);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000067
68
ricow@chromium.org30ce4112010-05-31 10:38:25 +000069#define __ ACCESS_MASM(masm_)
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000070
ager@chromium.orge2902be2009-06-08 12:21:35 +000071// -------------------------------------------------------------------------
72// Platform-specific DeferredCode functions.
73
74void DeferredCode::SaveRegisters() {
ricow@chromium.org30ce4112010-05-31 10:38:25 +000075 // On ARM you either have a completely spilled frame or you
76 // handle it yourself, but at the moment there's no automation
77 // of registers and deferred code.
ager@chromium.orge2902be2009-06-08 12:21:35 +000078}
79
80
81void DeferredCode::RestoreRegisters() {
ricow@chromium.org30ce4112010-05-31 10:38:25 +000082}
83
84
85// -------------------------------------------------------------------------
86// Platform-specific RuntimeCallHelper functions.
87
88void VirtualFrameRuntimeCallHelper::BeforeCall(MacroAssembler* masm) const {
89 frame_state_->frame()->AssertIsSpilled();
90}
91
92
93void VirtualFrameRuntimeCallHelper::AfterCall(MacroAssembler* masm) const {
94}
95
96
97void ICRuntimeCallHelper::BeforeCall(MacroAssembler* masm) const {
98 masm->EnterInternalFrame();
99}
100
101
102void ICRuntimeCallHelper::AfterCall(MacroAssembler* masm) const {
103 masm->LeaveInternalFrame();
ager@chromium.orge2902be2009-06-08 12:21:35 +0000104}
105
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000106
107// -------------------------------------------------------------------------
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000108// CodeGenState implementation.
109
ager@chromium.org7c537e22008-10-16 08:43:32 +0000110CodeGenState::CodeGenState(CodeGenerator* owner)
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000111 : owner_(owner),
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +0000112 previous_(owner->state()) {
113 owner->set_state(this);
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000114}
115
116
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +0000117ConditionCodeGenState::ConditionCodeGenState(CodeGenerator* owner,
118 JumpTarget* true_target,
119 JumpTarget* false_target)
120 : CodeGenState(owner),
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000121 true_target_(true_target),
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +0000122 false_target_(false_target) {
123 owner->set_state(this);
124}
125
126
127TypeInfoCodeGenState::TypeInfoCodeGenState(CodeGenerator* owner,
128 Slot* slot,
129 TypeInfo type_info)
130 : CodeGenState(owner),
131 slot_(slot) {
132 owner->set_state(this);
133 old_type_info_ = owner->set_type_info(slot, type_info);
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000134}
135
136
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000137CodeGenState::~CodeGenState() {
138 ASSERT(owner_->state() == this);
139 owner_->set_state(previous_);
140}
141
142
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +0000143TypeInfoCodeGenState::~TypeInfoCodeGenState() {
144 owner()->set_type_info(slot_, old_type_info_);
145}
146
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000147// -------------------------------------------------------------------------
ager@chromium.org7c537e22008-10-16 08:43:32 +0000148// CodeGenerator implementation
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000149
ager@chromium.org5c838252010-02-19 08:53:10 +0000150CodeGenerator::CodeGenerator(MacroAssembler* masm)
151 : deferred_(8),
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000152 masm_(masm),
ager@chromium.org5c838252010-02-19 08:53:10 +0000153 info_(NULL),
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000154 frame_(NULL),
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000155 allocator_(NULL),
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000156 cc_reg_(al),
157 state_(NULL),
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000158 loop_nesting_(0),
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +0000159 type_info_(NULL),
fschneider@chromium.org40b9da32010-06-28 11:29:21 +0000160 function_return_(JumpTarget::BIDIRECTIONAL),
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +0000161 function_return_is_shadowed_(false) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000162}
163
164
165// Calling conventions:
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000166// fp: caller's frame pointer
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000167// sp: stack pointer
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000168// r1: called JS function
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000169// cp: callee's context
170
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000171void CodeGenerator::Generate(CompilationInfo* info) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000172 // Record the position for debugging purposes.
ager@chromium.org5c838252010-02-19 08:53:10 +0000173 CodeForFunctionPosition(info->function());
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000174 Comment cmnt(masm_, "[ function compiled by virtual frame code generator");
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000175
176 // Initialize state.
ager@chromium.org5c838252010-02-19 08:53:10 +0000177 info_ = info;
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +0000178
179 int slots = scope()->num_parameters() + scope()->num_stack_slots();
180 ScopedVector<TypeInfo> type_info_array(slots);
181 type_info_ = &type_info_array;
182
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000183 ASSERT(allocator_ == NULL);
184 RegisterAllocator register_allocator(this);
185 allocator_ = &register_allocator;
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000186 ASSERT(frame_ == NULL);
kasperl@chromium.org71affb52009-05-26 05:44:31 +0000187 frame_ = new VirtualFrame();
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000188 cc_reg_ = al;
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000189
190 // Adjust for function-level loop nesting.
191 ASSERT_EQ(0, loop_nesting_);
192 loop_nesting_ = info->loop_nesting();
193
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000194 {
195 CodeGenState state(this);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000196
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000197 // Entry:
198 // Stack: receiver, arguments
199 // lr: return address
200 // fp: caller's frame pointer
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000201 // sp: stack pointer
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000202 // r1: called JS function
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000203 // cp: callee's context
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000204 allocator_->Initialize();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000205
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000206#ifdef DEBUG
207 if (strlen(FLAG_stop_at) > 0 &&
ager@chromium.org5c838252010-02-19 08:53:10 +0000208 info->function()->name()->IsEqualTo(CStrVector(FLAG_stop_at))) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000209 frame_->SpillAll();
kasper.lund7276f142008-07-30 08:49:36 +0000210 __ stop("stop-at");
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000211 }
212#endif
213
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000214 if (info->mode() == CompilationInfo::PRIMARY) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000215 frame_->Enter();
216 // tos: code slot
217
218 // Allocate space for locals and initialize them. This also checks
219 // for stack overflow.
220 frame_->AllocateStackSlots();
221
fschneider@chromium.org40b9da32010-06-28 11:29:21 +0000222 frame_->AssertIsSpilled();
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +0000223 int heap_slots = scope()->num_heap_slots() - Context::MIN_CONTEXT_SLOTS;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000224 if (heap_slots > 0) {
225 // Allocate local context.
226 // Get outer context and create a new context based on it.
227 __ ldr(r0, frame_->Function());
228 frame_->EmitPush(r0);
229 if (heap_slots <= FastNewContextStub::kMaximumSlots) {
230 FastNewContextStub stub(heap_slots);
231 frame_->CallStub(&stub, 1);
232 } else {
233 frame_->CallRuntime(Runtime::kNewContext, 1);
234 }
235
236#ifdef DEBUG
237 JumpTarget verified_true;
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000238 __ cmp(r0, cp);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000239 verified_true.Branch(eq);
240 __ stop("NewContext: r0 is expected to be the same as cp");
241 verified_true.Bind();
242#endif
243 // Update context local.
244 __ str(cp, frame_->Context());
245 }
246
247 // TODO(1241774): Improve this code:
248 // 1) only needed if we have a context
249 // 2) no need to recompute context ptr every single time
250 // 3) don't copy parameter operand code from SlotOperand!
251 {
252 Comment cmnt2(masm_, "[ copy context parameters into .context");
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000253 // Note that iteration order is relevant here! If we have the same
254 // parameter twice (e.g., function (x, y, x)), and that parameter
255 // needs to be copied into the context, it must be the last argument
256 // passed to the parameter that needs to be copied. This is a rare
257 // case so we don't check for it, instead we rely on the copying
258 // order: such a parameter is copied repeatedly into the same
259 // context location and thus the last value is what is seen inside
260 // the function.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +0000261 frame_->AssertIsSpilled();
ager@chromium.org5c838252010-02-19 08:53:10 +0000262 for (int i = 0; i < scope()->num_parameters(); i++) {
263 Variable* par = scope()->parameter(i);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000264 Slot* slot = par->slot();
265 if (slot != NULL && slot->type() == Slot::CONTEXT) {
ager@chromium.org5c838252010-02-19 08:53:10 +0000266 ASSERT(!scope()->is_global_scope()); // No params in global scope.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000267 __ ldr(r1, frame_->ParameterAt(i));
268 // Loads r2 with context; used below in RecordWrite.
269 __ str(r1, SlotOperand(slot, r2));
270 // Load the offset into r3.
271 int slot_offset =
272 FixedArray::kHeaderSize + slot->index() * kPointerSize;
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +0000273 __ RecordWrite(r2, Operand(slot_offset), r3, r1);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000274 }
275 }
276 }
277
278 // Store the arguments object. This must happen after context
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000279 // initialization because the arguments object may be stored in
280 // the context.
281 if (ArgumentsMode() != NO_ARGUMENTS_ALLOCATION) {
282 StoreArgumentsObject(true);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000283 }
284
285 // Initialize ThisFunction reference if present.
ager@chromium.org5c838252010-02-19 08:53:10 +0000286 if (scope()->is_function_scope() && scope()->function() != NULL) {
fschneider@chromium.org40b9da32010-06-28 11:29:21 +0000287 frame_->EmitPushRoot(Heap::kTheHoleValueRootIndex);
ager@chromium.org5c838252010-02-19 08:53:10 +0000288 StoreToSlot(scope()->function()->slot(), NOT_CONST_INIT);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000289 }
290 } else {
291 // When used as the secondary compiler for splitting, r1, cp,
292 // fp, and lr have been pushed on the stack. Adjust the virtual
293 // frame to match this state.
294 frame_->Adjust(4);
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000295
296 // Bind all the bailout labels to the beginning of the function.
297 List<CompilationInfo::Bailout*>* bailouts = info->bailouts();
298 for (int i = 0; i < bailouts->length(); i++) {
299 __ bind(bailouts->at(i)->label());
300 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000301 }
302
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000303 // Initialize the function return target after the locals are set
304 // up, because it needs the expected frame height from the frame.
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000305 function_return_.SetExpectedHeight();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000306 function_return_is_shadowed_ = false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000307
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000308 // Generate code to 'execute' declarations and initialize functions
309 // (source elements). In case of an illegal redeclaration we need to
310 // handle that instead of processing the declarations.
ager@chromium.org5c838252010-02-19 08:53:10 +0000311 if (scope()->HasIllegalRedeclaration()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000312 Comment cmnt(masm_, "[ illegal redeclarations");
ager@chromium.org5c838252010-02-19 08:53:10 +0000313 scope()->VisitIllegalRedeclaration(this);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000314 } else {
315 Comment cmnt(masm_, "[ declarations");
ager@chromium.org5c838252010-02-19 08:53:10 +0000316 ProcessDeclarations(scope()->declarations());
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000317 // Bail out if a stack-overflow exception occurred when processing
318 // declarations.
kasper.lund212ac232008-07-16 07:07:30 +0000319 if (HasStackOverflow()) return;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000320 }
321
mads.s.ager31e71382008-08-13 09:32:07 +0000322 if (FLAG_trace) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000323 frame_->CallRuntime(Runtime::kTraceEnter, 0);
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000324 // Ignore the return value.
mads.s.ager31e71382008-08-13 09:32:07 +0000325 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000326
327 // Compile the body of the function in a vanilla state. Don't
328 // bother compiling all the code if the scope has an illegal
329 // redeclaration.
ager@chromium.org5c838252010-02-19 08:53:10 +0000330 if (!scope()->HasIllegalRedeclaration()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000331 Comment cmnt(masm_, "[ function body");
332#ifdef DEBUG
333 bool is_builtin = Bootstrapper::IsActive();
334 bool should_trace =
335 is_builtin ? FLAG_trace_builtin_calls : FLAG_trace_calls;
mads.s.ager31e71382008-08-13 09:32:07 +0000336 if (should_trace) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000337 frame_->CallRuntime(Runtime::kDebugTrace, 0);
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000338 // Ignore the return value.
mads.s.ager31e71382008-08-13 09:32:07 +0000339 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000340#endif
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000341 VisitStatements(info->function()->body());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000342 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000343 }
344
whesse@chromium.org2c186ca2010-06-16 11:32:39 +0000345 // Handle the return from the function.
346 if (has_valid_frame()) {
347 // If there is a valid frame, control flow can fall off the end of
348 // the body. In that case there is an implicit return statement.
349 ASSERT(!function_return_is_shadowed_);
350 frame_->PrepareForReturn();
ager@chromium.orgab99eea2009-08-25 07:05:41 +0000351 __ LoadRoot(r0, Heap::kUndefinedValueRootIndex);
whesse@chromium.org2c186ca2010-06-16 11:32:39 +0000352 if (function_return_.is_bound()) {
353 function_return_.Jump();
354 } else {
355 function_return_.Bind();
356 GenerateReturnSequence();
357 }
358 } else if (function_return_.is_linked()) {
359 // If the return target has dangling jumps to it, then we have not
360 // yet generated the return sequence. This can happen when (a)
361 // control does not flow off the end of the body so we did not
362 // compile an artificial return statement just above, and (b) there
363 // are return statements in the body but (c) they are all shadowed.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000364 function_return_.Bind();
whesse@chromium.org2c186ca2010-06-16 11:32:39 +0000365 GenerateReturnSequence();
mads.s.ager31e71382008-08-13 09:32:07 +0000366 }
367
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000368 // Adjust for function-level loop nesting.
369 ASSERT(loop_nesting_ == info->loop_nesting());
370 loop_nesting_ = 0;
371
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000372 // Code generation state must be reset.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000373 ASSERT(!has_cc());
374 ASSERT(state_ == NULL);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000375 ASSERT(loop_nesting() == 0);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000376 ASSERT(!function_return_is_shadowed_);
377 function_return_.Unuse();
378 DeleteFrame();
379
380 // Process any deferred code using the register allocator.
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +0000381 if (!HasStackOverflow()) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000382 ProcessDeferred();
383 }
384
385 allocator_ = NULL;
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +0000386 type_info_ = NULL;
387}
388
389
390int CodeGenerator::NumberOfSlot(Slot* slot) {
391 if (slot == NULL) return kInvalidSlotNumber;
392 switch (slot->type()) {
393 case Slot::PARAMETER:
394 return slot->index();
395 case Slot::LOCAL:
396 return slot->index() + scope()->num_parameters();
397 default:
398 break;
399 }
400 return kInvalidSlotNumber;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000401}
402
403
ager@chromium.org7c537e22008-10-16 08:43:32 +0000404MemOperand CodeGenerator::SlotOperand(Slot* slot, Register tmp) {
405 // Currently, this assertion will fail if we try to assign to
406 // a constant variable that is constant because it is read-only
407 // (such as the variable referring to a named function expression).
408 // We need to implement assignments to read-only variables.
409 // Ideally, we should do this during AST generation (by converting
410 // such assignments into expression statements); however, in general
411 // we may not be able to make the decision until past AST generation,
412 // that is when the entire program is known.
413 ASSERT(slot != NULL);
414 int index = slot->index();
415 switch (slot->type()) {
416 case Slot::PARAMETER:
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000417 return frame_->ParameterAt(index);
ager@chromium.org7c537e22008-10-16 08:43:32 +0000418
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000419 case Slot::LOCAL:
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000420 return frame_->LocalAt(index);
ager@chromium.org7c537e22008-10-16 08:43:32 +0000421
422 case Slot::CONTEXT: {
423 // Follow the context chain if necessary.
424 ASSERT(!tmp.is(cp)); // do not overwrite context register
425 Register context = cp;
426 int chain_length = scope()->ContextChainLength(slot->var()->scope());
ager@chromium.org381abbb2009-02-25 13:23:22 +0000427 for (int i = 0; i < chain_length; i++) {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000428 // Load the closure.
429 // (All contexts, even 'with' contexts, have a closure,
430 // and it is the same for all contexts inside a function.
431 // There is no need to go to the function context first.)
432 __ ldr(tmp, ContextOperand(context, Context::CLOSURE_INDEX));
433 // Load the function context (which is the incoming, outer context).
434 __ ldr(tmp, FieldMemOperand(tmp, JSFunction::kContextOffset));
435 context = tmp;
436 }
437 // We may have a 'with' context now. Get the function context.
438 // (In fact this mov may never be the needed, since the scope analysis
439 // may not permit a direct context access in this case and thus we are
440 // always at a function context. However it is safe to dereference be-
441 // cause the function context of a function context is itself. Before
442 // deleting this mov we should try to create a counter-example first,
443 // though...)
444 __ ldr(tmp, ContextOperand(context, Context::FCONTEXT_INDEX));
445 return ContextOperand(tmp, index);
446 }
447
448 default:
449 UNREACHABLE();
450 return MemOperand(r0, 0);
451 }
452}
453
454
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000455MemOperand CodeGenerator::ContextSlotOperandCheckExtensions(
456 Slot* slot,
457 Register tmp,
458 Register tmp2,
459 JumpTarget* slow) {
ager@chromium.org381abbb2009-02-25 13:23:22 +0000460 ASSERT(slot->type() == Slot::CONTEXT);
ager@chromium.org381abbb2009-02-25 13:23:22 +0000461 Register context = cp;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000462
ager@chromium.org381abbb2009-02-25 13:23:22 +0000463 for (Scope* s = scope(); s != slot->var()->scope(); s = s->outer_scope()) {
464 if (s->num_heap_slots() > 0) {
465 if (s->calls_eval()) {
466 // Check that extension is NULL.
467 __ ldr(tmp2, ContextOperand(context, Context::EXTENSION_INDEX));
468 __ tst(tmp2, tmp2);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000469 slow->Branch(ne);
ager@chromium.org381abbb2009-02-25 13:23:22 +0000470 }
471 __ ldr(tmp, ContextOperand(context, Context::CLOSURE_INDEX));
472 __ ldr(tmp, FieldMemOperand(tmp, JSFunction::kContextOffset));
473 context = tmp;
474 }
475 }
476 // Check that last extension is NULL.
477 __ ldr(tmp2, ContextOperand(context, Context::EXTENSION_INDEX));
478 __ tst(tmp2, tmp2);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000479 slow->Branch(ne);
ager@chromium.org381abbb2009-02-25 13:23:22 +0000480 __ ldr(tmp, ContextOperand(context, Context::FCONTEXT_INDEX));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000481 return ContextOperand(tmp, slot->index());
ager@chromium.org381abbb2009-02-25 13:23:22 +0000482}
483
484
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000485// Loads a value on TOS. If it is a boolean value, the result may have been
486// (partially) translated into branches, or it may have set the condition
487// code register. If force_cc is set, the value is forced to set the
488// condition code register and no value is pushed. If the condition code
489// register was set, has_cc() is true and cc_reg_ contains the condition to
490// test for 'true'.
ager@chromium.org7c537e22008-10-16 08:43:32 +0000491void CodeGenerator::LoadCondition(Expression* x,
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000492 JumpTarget* true_target,
493 JumpTarget* false_target,
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000494 bool force_cc) {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000495 ASSERT(!has_cc());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000496 int original_height = frame_->height();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000497
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +0000498 { ConditionCodeGenState new_state(this, true_target, false_target);
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000499 Visit(x);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000500
501 // If we hit a stack overflow, we may not have actually visited
502 // the expression. In that case, we ensure that we have a
503 // valid-looking frame state because we will continue to generate
504 // code as we unwind the C++ stack.
505 //
506 // It's possible to have both a stack overflow and a valid frame
507 // state (eg, a subexpression overflowed, visiting it returned
508 // with a dummied frame state, and visiting this expression
509 // returned with a normal-looking state).
510 if (HasStackOverflow() &&
511 has_valid_frame() &&
512 !has_cc() &&
513 frame_->height() == original_height) {
514 true_target->Jump();
515 }
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000516 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000517 if (force_cc && frame_ != NULL && !has_cc()) {
mads.s.ager31e71382008-08-13 09:32:07 +0000518 // Convert the TOS value to a boolean in the condition code register.
519 ToBoolean(true_target, false_target);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000520 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000521 ASSERT(!force_cc || !has_valid_frame() || has_cc());
522 ASSERT(!has_valid_frame() ||
523 (has_cc() && frame_->height() == original_height) ||
524 (!has_cc() && frame_->height() == original_height + 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000525}
526
527
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000528void CodeGenerator::Load(Expression* expr) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000529#ifdef DEBUG
530 int original_height = frame_->height();
531#endif
kasperl@chromium.org71affb52009-05-26 05:44:31 +0000532 JumpTarget true_target;
533 JumpTarget false_target;
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000534 LoadCondition(expr, &true_target, &false_target, false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000535
536 if (has_cc()) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000537 // Convert cc_reg_ into a boolean value.
kasperl@chromium.org71affb52009-05-26 05:44:31 +0000538 JumpTarget loaded;
539 JumpTarget materialize_true;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000540 materialize_true.Branch(cc_reg_);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +0000541 frame_->EmitPushRoot(Heap::kFalseValueRootIndex);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000542 loaded.Jump();
543 materialize_true.Bind();
fschneider@chromium.org40b9da32010-06-28 11:29:21 +0000544 frame_->EmitPushRoot(Heap::kTrueValueRootIndex);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000545 loaded.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000546 cc_reg_ = al;
547 }
548
549 if (true_target.is_linked() || false_target.is_linked()) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000550 // We have at least one condition value that has been "translated"
551 // into a branch, thus it needs to be loaded explicitly.
kasperl@chromium.org71affb52009-05-26 05:44:31 +0000552 JumpTarget loaded;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000553 if (frame_ != NULL) {
554 loaded.Jump(); // Don't lose the current TOS.
555 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000556 bool both = true_target.is_linked() && false_target.is_linked();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000557 // Load "true" if necessary.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000558 if (true_target.is_linked()) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000559 true_target.Bind();
fschneider@chromium.org40b9da32010-06-28 11:29:21 +0000560 frame_->EmitPushRoot(Heap::kTrueValueRootIndex);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000561 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000562 // If both "true" and "false" need to be loaded jump across the code for
563 // "false".
564 if (both) {
565 loaded.Jump();
566 }
567 // Load "false" if necessary.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000568 if (false_target.is_linked()) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000569 false_target.Bind();
fschneider@chromium.org40b9da32010-06-28 11:29:21 +0000570 frame_->EmitPushRoot(Heap::kFalseValueRootIndex);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000571 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000572 // A value is loaded on all paths reaching this point.
573 loaded.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000574 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000575 ASSERT(has_valid_frame());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000576 ASSERT(!has_cc());
ager@chromium.orgac091b72010-05-05 07:34:42 +0000577 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000578}
579
580
ager@chromium.org7c537e22008-10-16 08:43:32 +0000581void CodeGenerator::LoadGlobal() {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +0000582 Register reg = frame_->GetTOSRegister();
583 __ ldr(reg, GlobalObject());
584 frame_->EmitPush(reg);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000585}
586
587
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000588void CodeGenerator::LoadGlobalReceiver(Register scratch) {
fschneider@chromium.org40b9da32010-06-28 11:29:21 +0000589 Register reg = frame_->GetTOSRegister();
590 __ ldr(reg, ContextOperand(cp, Context::GLOBAL_INDEX));
591 __ ldr(reg,
592 FieldMemOperand(reg, GlobalObject::kGlobalReceiverOffset));
593 frame_->EmitPush(reg);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000594}
595
596
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000597ArgumentsAllocationMode CodeGenerator::ArgumentsMode() {
598 if (scope()->arguments() == NULL) return NO_ARGUMENTS_ALLOCATION;
599 ASSERT(scope()->arguments_shadow() != NULL);
600 // We don't want to do lazy arguments allocation for functions that
601 // have heap-allocated contexts, because it interfers with the
602 // uninitialized const tracking in the context objects.
603 return (scope()->num_heap_slots() > 0)
604 ? EAGER_ARGUMENTS_ALLOCATION
605 : LAZY_ARGUMENTS_ALLOCATION;
606}
607
608
609void CodeGenerator::StoreArgumentsObject(bool initial) {
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000610 ArgumentsAllocationMode mode = ArgumentsMode();
611 ASSERT(mode != NO_ARGUMENTS_ALLOCATION);
612
613 Comment cmnt(masm_, "[ store arguments object");
614 if (mode == LAZY_ARGUMENTS_ALLOCATION && initial) {
615 // When using lazy arguments allocation, we store the hole value
616 // as a sentinel indicating that the arguments object hasn't been
617 // allocated yet.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +0000618 frame_->EmitPushRoot(Heap::kTheHoleValueRootIndex);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000619 } else {
fschneider@chromium.org40b9da32010-06-28 11:29:21 +0000620 frame_->SpillAll();
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000621 ArgumentsAccessStub stub(ArgumentsAccessStub::NEW_OBJECT);
622 __ ldr(r2, frame_->Function());
623 // The receiver is below the arguments, the return address, and the
624 // frame pointer on the stack.
625 const int kReceiverDisplacement = 2 + scope()->num_parameters();
626 __ add(r1, fp, Operand(kReceiverDisplacement * kPointerSize));
627 __ mov(r0, Operand(Smi::FromInt(scope()->num_parameters())));
628 frame_->Adjust(3);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +0000629 __ Push(r2, r1, r0);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000630 frame_->CallStub(&stub, 3);
631 frame_->EmitPush(r0);
632 }
633
634 Variable* arguments = scope()->arguments()->var();
635 Variable* shadow = scope()->arguments_shadow()->var();
636 ASSERT(arguments != NULL && arguments->slot() != NULL);
637 ASSERT(shadow != NULL && shadow->slot() != NULL);
638 JumpTarget done;
639 if (mode == LAZY_ARGUMENTS_ALLOCATION && !initial) {
640 // We have to skip storing into the arguments slot if it has
641 // already been written to. This can happen if the a function
642 // has a local variable named 'arguments'.
643 LoadFromSlot(scope()->arguments()->var()->slot(), NOT_INSIDE_TYPEOF);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +0000644 Register arguments = frame_->PopToRegister();
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000645 __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +0000646 __ cmp(arguments, ip);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000647 done.Branch(ne);
648 }
649 StoreToSlot(arguments->slot(), NOT_CONST_INIT);
650 if (mode == LAZY_ARGUMENTS_ALLOCATION) done.Bind();
651 StoreToSlot(shadow->slot(), NOT_CONST_INIT);
652}
653
654
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000655void CodeGenerator::LoadTypeofExpression(Expression* expr) {
656 // Special handling of identifiers as subexpressions of typeof.
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000657 Variable* variable = expr->AsVariableProxy()->AsVariable();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000658 if (variable != NULL && !variable->is_this() && variable->is_global()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000659 // For a global variable we build the property reference
660 // <global>.<variable> and perform a (regular non-contextual) property
661 // load to make sure we do not get reference errors.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000662 Slot global(variable, Slot::CONTEXT, Context::GLOBAL_INDEX);
663 Literal key(variable->name());
ager@chromium.org236ad962008-09-25 09:45:57 +0000664 Property property(&global, &key, RelocInfo::kNoPosition);
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000665 Reference ref(this, &property);
ager@chromium.org357bf652010-04-12 11:30:10 +0000666 ref.GetValue();
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000667 } else if (variable != NULL && variable->slot() != NULL) {
668 // For a variable that rewrites to a slot, we signal it is the immediate
669 // subexpression of a typeof.
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000670 LoadFromSlotCheckForArguments(variable->slot(), INSIDE_TYPEOF);
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.
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000673 Load(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.
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000722 Load(e);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000723 frame_->CallRuntime(Runtime::kThrowReferenceError, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000724 }
725}
726
727
ager@chromium.org7c537e22008-10-16 08:43:32 +0000728void CodeGenerator::UnloadReference(Reference* ref) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000729 int size = ref->size();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000730 ref->set_unloaded();
ager@chromium.org357bf652010-04-12 11:30:10 +0000731 if (size == 0) return;
732
733 // Pop a reference from the stack while preserving TOS.
734 VirtualFrame::RegisterAllocationScope scope(this);
735 Comment cmnt(masm_, "[ UnloadReference");
736 if (size > 0) {
737 Register tos = frame_->PopToRegister();
738 frame_->Drop(size);
739 frame_->EmitPush(tos);
740 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000741}
742
743
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000744// ECMA-262, section 9.2, page 30: ToBoolean(). Convert the given
745// register to a boolean in the condition code register. The code
746// may jump to 'false_target' in case the register converts to 'false'.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000747void CodeGenerator::ToBoolean(JumpTarget* true_target,
748 JumpTarget* false_target) {
mads.s.ager31e71382008-08-13 09:32:07 +0000749 // Note: The generated code snippet does not change stack variables.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000750 // Only the condition code should be set.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +0000751 Register tos = frame_->PopToRegister();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000752
753 // Fast case checks
754
mads.s.ager31e71382008-08-13 09:32:07 +0000755 // Check if the value is 'false'.
ager@chromium.orgab99eea2009-08-25 07:05:41 +0000756 __ LoadRoot(ip, Heap::kFalseValueRootIndex);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +0000757 __ cmp(tos, ip);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000758 false_target->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000759
mads.s.ager31e71382008-08-13 09:32:07 +0000760 // Check if the value is 'true'.
ager@chromium.orgab99eea2009-08-25 07:05:41 +0000761 __ LoadRoot(ip, Heap::kTrueValueRootIndex);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +0000762 __ cmp(tos, ip);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000763 true_target->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000764
mads.s.ager31e71382008-08-13 09:32:07 +0000765 // Check if the value is 'undefined'.
ager@chromium.orgab99eea2009-08-25 07:05:41 +0000766 __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +0000767 __ cmp(tos, ip);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000768 false_target->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000769
mads.s.ager31e71382008-08-13 09:32:07 +0000770 // Check if the value is a smi.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +0000771 __ cmp(tos, Operand(Smi::FromInt(0)));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000772 false_target->Branch(eq);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +0000773 __ tst(tos, Operand(kSmiTagMask));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000774 true_target->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000775
776 // Slow case: call the runtime.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +0000777 frame_->EmitPush(tos);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000778 frame_->CallRuntime(Runtime::kToBool, 1);
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000779 // Convert the result (r0) to a condition code.
ager@chromium.orgab99eea2009-08-25 07:05:41 +0000780 __ LoadRoot(ip, Heap::kFalseValueRootIndex);
781 __ cmp(r0, ip);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000782
783 cc_reg_ = ne;
784}
785
786
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000787void CodeGenerator::GenericBinaryOperation(Token::Value op,
kasperl@chromium.org86f77b72009-07-06 08:21:57 +0000788 OverwriteMode overwrite_mode,
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +0000789 GenerateInlineSmi inline_smi,
kasperl@chromium.org86f77b72009-07-06 08:21:57 +0000790 int constant_rhs) {
ager@chromium.org357bf652010-04-12 11:30:10 +0000791 // top of virtual frame: y
792 // 2nd elt. on virtual frame : x
793 // result : top of virtual frame
794
795 // Stub is entered with a call: 'return address' is in lr.
796 switch (op) {
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +0000797 case Token::ADD:
798 case Token::SUB:
799 if (inline_smi) {
800 JumpTarget done;
801 Register rhs = frame_->PopToRegister();
802 Register lhs = frame_->PopToRegister(rhs);
803 Register scratch = VirtualFrame::scratch0();
804 __ orr(scratch, rhs, Operand(lhs));
805 // Check they are both small and positive.
806 __ tst(scratch, Operand(kSmiTagMask | 0xc0000000));
807 ASSERT(rhs.is(r0) || lhs.is(r0)); // r0 is free now.
808 ASSERT_EQ(0, kSmiTag);
809 if (op == Token::ADD) {
810 __ add(r0, lhs, Operand(rhs), LeaveCC, eq);
811 } else {
812 __ sub(r0, lhs, Operand(rhs), LeaveCC, eq);
813 }
814 done.Branch(eq);
815 GenericBinaryOpStub stub(op, overwrite_mode, lhs, rhs, constant_rhs);
816 frame_->SpillAll();
817 frame_->CallStub(&stub, 0);
818 done.Bind();
819 frame_->EmitPush(r0);
820 break;
821 } else {
822 // Fall through!
823 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000824 case Token::BIT_OR:
825 case Token::BIT_AND:
826 case Token::BIT_XOR:
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +0000827 if (inline_smi) {
828 bool rhs_is_smi = frame_->KnownSmiAt(0);
829 bool lhs_is_smi = frame_->KnownSmiAt(1);
830 Register rhs = frame_->PopToRegister();
831 Register lhs = frame_->PopToRegister(rhs);
832 Register smi_test_reg;
833 Condition cond;
834 if (!rhs_is_smi || !lhs_is_smi) {
835 if (rhs_is_smi) {
836 smi_test_reg = lhs;
837 } else if (lhs_is_smi) {
838 smi_test_reg = rhs;
839 } else {
840 smi_test_reg = VirtualFrame::scratch0();
841 __ orr(smi_test_reg, rhs, Operand(lhs));
842 }
843 // Check they are both Smis.
844 __ tst(smi_test_reg, Operand(kSmiTagMask));
845 cond = eq;
846 } else {
847 cond = al;
848 }
849 ASSERT(rhs.is(r0) || lhs.is(r0)); // r0 is free now.
850 if (op == Token::BIT_OR) {
851 __ orr(r0, lhs, Operand(rhs), LeaveCC, cond);
852 } else if (op == Token::BIT_AND) {
853 __ and_(r0, lhs, Operand(rhs), LeaveCC, cond);
854 } else {
855 ASSERT(op == Token::BIT_XOR);
856 ASSERT_EQ(0, kSmiTag);
857 __ eor(r0, lhs, Operand(rhs), LeaveCC, cond);
858 }
859 if (cond != al) {
860 JumpTarget done;
861 done.Branch(cond);
862 GenericBinaryOpStub stub(op, overwrite_mode, lhs, rhs, constant_rhs);
863 frame_->SpillAll();
864 frame_->CallStub(&stub, 0);
865 done.Bind();
866 }
867 frame_->EmitPush(r0);
868 break;
869 } else {
870 // Fall through!
871 }
872 case Token::MUL:
873 case Token::DIV:
874 case Token::MOD:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000875 case Token::SHL:
876 case Token::SHR:
877 case Token::SAR: {
ager@chromium.org357bf652010-04-12 11:30:10 +0000878 Register rhs = frame_->PopToRegister();
879 Register lhs = frame_->PopToRegister(rhs); // Don't pop to rhs register.
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000880 GenericBinaryOpStub stub(op, overwrite_mode, lhs, rhs, constant_rhs);
881 frame_->SpillAll();
882 frame_->CallStub(&stub, 0);
ager@chromium.org357bf652010-04-12 11:30:10 +0000883 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000884 break;
885 }
886
ager@chromium.org357bf652010-04-12 11:30:10 +0000887 case Token::COMMA: {
888 Register scratch = frame_->PopToRegister();
889 // Simply discard left value.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000890 frame_->Drop();
ager@chromium.org357bf652010-04-12 11:30:10 +0000891 frame_->EmitPush(scratch);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000892 break;
ager@chromium.org357bf652010-04-12 11:30:10 +0000893 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000894
895 default:
896 // Other cases should have been handled before this point.
897 UNREACHABLE();
898 break;
899 }
900}
901
902
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000903class DeferredInlineSmiOperation: public DeferredCode {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000904 public:
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +0000905 DeferredInlineSmiOperation(Token::Value op,
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000906 int value,
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000907 bool reversed,
ager@chromium.org357bf652010-04-12 11:30:10 +0000908 OverwriteMode overwrite_mode,
909 Register tos)
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +0000910 : op_(op),
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000911 value_(value),
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000912 reversed_(reversed),
ager@chromium.org357bf652010-04-12 11:30:10 +0000913 overwrite_mode_(overwrite_mode),
914 tos_register_(tos) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000915 set_comment("[ DeferredInlinedSmiOperation");
916 }
917
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000918 virtual void Generate();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000919
920 private:
921 Token::Value op_;
922 int value_;
923 bool reversed_;
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000924 OverwriteMode overwrite_mode_;
ager@chromium.org357bf652010-04-12 11:30:10 +0000925 Register tos_register_;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000926};
927
928
fschneider@chromium.org40b9da32010-06-28 11:29:21 +0000929
930// On entry the non-constant side of the binary operation is in tos_register_
931// and the constant smi side is nowhere. The tos_register_ is not used by the
932// virtual frame. On exit the answer is in the tos_register_ and the virtual
933// frame is unchanged.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000934void DeferredInlineSmiOperation::Generate() {
fschneider@chromium.org40b9da32010-06-28 11:29:21 +0000935 VirtualFrame copied_frame(*frame_state()->frame());
936 copied_frame.SpillAll();
937
ager@chromium.org357bf652010-04-12 11:30:10 +0000938 Register lhs = r1;
939 Register rhs = r0;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000940 switch (op_) {
941 case Token::ADD: {
ager@chromium.orge2902be2009-06-08 12:21:35 +0000942 // Revert optimistic add.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000943 if (reversed_) {
ager@chromium.org357bf652010-04-12 11:30:10 +0000944 __ sub(r0, tos_register_, Operand(Smi::FromInt(value_)));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000945 __ mov(r1, Operand(Smi::FromInt(value_)));
946 } else {
ager@chromium.org357bf652010-04-12 11:30:10 +0000947 __ sub(r1, tos_register_, Operand(Smi::FromInt(value_)));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000948 __ mov(r0, Operand(Smi::FromInt(value_)));
949 }
950 break;
951 }
952
953 case Token::SUB: {
ager@chromium.orge2902be2009-06-08 12:21:35 +0000954 // Revert optimistic sub.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000955 if (reversed_) {
ager@chromium.org357bf652010-04-12 11:30:10 +0000956 __ rsb(r0, tos_register_, Operand(Smi::FromInt(value_)));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000957 __ mov(r1, Operand(Smi::FromInt(value_)));
958 } else {
ager@chromium.org357bf652010-04-12 11:30:10 +0000959 __ add(r1, tos_register_, Operand(Smi::FromInt(value_)));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000960 __ mov(r0, Operand(Smi::FromInt(value_)));
961 }
962 break;
963 }
964
kasperl@chromium.org86f77b72009-07-06 08:21:57 +0000965 // For these operations there is no optimistic operation that needs to be
966 // reverted.
967 case Token::MUL:
968 case Token::MOD:
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000969 case Token::BIT_OR:
970 case Token::BIT_XOR:
fschneider@chromium.org40b9da32010-06-28 11:29:21 +0000971 case Token::BIT_AND:
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000972 case Token::SHL:
973 case Token::SHR:
974 case Token::SAR: {
fschneider@chromium.org40b9da32010-06-28 11:29:21 +0000975 if (tos_register_.is(r1)) {
976 __ mov(r0, Operand(Smi::FromInt(value_)));
977 } else {
978 ASSERT(tos_register_.is(r0));
979 __ mov(r1, Operand(Smi::FromInt(value_)));
980 }
981 if (reversed_ == tos_register_.is(r1)) {
ager@chromium.org357bf652010-04-12 11:30:10 +0000982 lhs = r0;
983 rhs = r1;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000984 }
985 break;
986 }
987
988 default:
ager@chromium.orge2902be2009-06-08 12:21:35 +0000989 // Other cases should have been handled before this point.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000990 UNREACHABLE();
991 break;
992 }
993
ager@chromium.org357bf652010-04-12 11:30:10 +0000994 GenericBinaryOpStub stub(op_, overwrite_mode_, lhs, rhs, value_);
ager@chromium.orge2902be2009-06-08 12:21:35 +0000995 __ CallStub(&stub);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +0000996
ager@chromium.org357bf652010-04-12 11:30:10 +0000997 // The generic stub returns its value in r0, but that's not
998 // necessarily what we want. We want whatever the inlined code
999 // expected, which is that the answer is in the same register as
1000 // the operand was.
1001 __ Move(tos_register_, r0);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00001002
1003 // The tos register was not in use for the virtual frame that we
1004 // came into this function with, so we can merge back to that frame
1005 // without trashing it.
1006 copied_frame.MergeTo(frame_state()->frame());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001007}
1008
1009
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00001010static bool PopCountLessThanEqual2(unsigned int x) {
1011 x &= x - 1;
1012 return (x & (x - 1)) == 0;
1013}
1014
1015
1016// Returns the index of the lowest bit set.
1017static int BitPosition(unsigned x) {
1018 int bit_posn = 0;
1019 while ((x & 0xf) == 0) {
1020 bit_posn += 4;
1021 x >>= 4;
1022 }
1023 while ((x & 1) == 0) {
1024 bit_posn++;
1025 x >>= 1;
1026 }
1027 return bit_posn;
1028}
1029
1030
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00001031void CodeGenerator::SmiOperation(Token::Value op,
1032 Handle<Object> value,
1033 bool reversed,
1034 OverwriteMode mode) {
ager@chromium.org357bf652010-04-12 11:30:10 +00001035 int int_value = Smi::cast(*value)->value();
1036
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001037 bool both_sides_are_smi = frame_->KnownSmiAt(0);
1038
ager@chromium.org357bf652010-04-12 11:30:10 +00001039 bool something_to_inline;
1040 switch (op) {
1041 case Token::ADD:
1042 case Token::SUB:
1043 case Token::BIT_AND:
1044 case Token::BIT_OR:
1045 case Token::BIT_XOR: {
1046 something_to_inline = true;
1047 break;
1048 }
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001049 case Token::SHL: {
1050 something_to_inline = (both_sides_are_smi || !reversed);
1051 break;
1052 }
ager@chromium.org357bf652010-04-12 11:30:10 +00001053 case Token::SHR:
1054 case Token::SAR: {
1055 if (reversed) {
1056 something_to_inline = false;
1057 } else {
1058 something_to_inline = true;
1059 }
1060 break;
1061 }
1062 case Token::MOD: {
1063 if (reversed || int_value < 2 || !IsPowerOf2(int_value)) {
1064 something_to_inline = false;
1065 } else {
1066 something_to_inline = true;
1067 }
1068 break;
1069 }
1070 case Token::MUL: {
1071 if (!IsEasyToMultiplyBy(int_value)) {
1072 something_to_inline = false;
1073 } else {
1074 something_to_inline = true;
1075 }
1076 break;
1077 }
1078 default: {
1079 something_to_inline = false;
1080 break;
1081 }
1082 }
1083
1084 if (!something_to_inline) {
1085 if (!reversed) {
1086 // Push the rhs onto the virtual frame by putting it in a TOS register.
1087 Register rhs = frame_->GetTOSRegister();
1088 __ mov(rhs, Operand(value));
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001089 frame_->EmitPush(rhs, TypeInfo::Smi());
1090 GenericBinaryOperation(op, mode, GENERATE_INLINE_SMI, int_value);
ager@chromium.org357bf652010-04-12 11:30:10 +00001091 } else {
1092 // Pop the rhs, then push lhs and rhs in the right order. Only performs
1093 // at most one pop, the rest takes place in TOS registers.
1094 Register lhs = frame_->GetTOSRegister(); // Get reg for pushing.
1095 Register rhs = frame_->PopToRegister(lhs); // Don't use lhs for this.
1096 __ mov(lhs, Operand(value));
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001097 frame_->EmitPush(lhs, TypeInfo::Smi());
1098 TypeInfo t = both_sides_are_smi ? TypeInfo::Smi() : TypeInfo::Unknown();
1099 frame_->EmitPush(rhs, t);
1100 GenericBinaryOperation(op, mode, GENERATE_INLINE_SMI, kUnknownIntValue);
ager@chromium.org357bf652010-04-12 11:30:10 +00001101 }
1102 return;
1103 }
1104
1105 // We move the top of stack to a register (normally no move is invoved).
1106 Register tos = frame_->PopToRegister();
ager@chromium.org357bf652010-04-12 11:30:10 +00001107 switch (op) {
1108 case Token::ADD: {
1109 DeferredCode* deferred =
1110 new DeferredInlineSmiOperation(op, int_value, reversed, mode, tos);
1111
1112 __ add(tos, tos, Operand(value), SetCC);
1113 deferred->Branch(vs);
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001114 if (!both_sides_are_smi) {
1115 __ tst(tos, Operand(kSmiTagMask));
1116 deferred->Branch(ne);
1117 }
ager@chromium.org357bf652010-04-12 11:30:10 +00001118 deferred->BindExit();
1119 frame_->EmitPush(tos);
1120 break;
1121 }
1122
1123 case Token::SUB: {
1124 DeferredCode* deferred =
1125 new DeferredInlineSmiOperation(op, int_value, reversed, mode, tos);
1126
1127 if (reversed) {
1128 __ rsb(tos, tos, Operand(value), SetCC);
1129 } else {
1130 __ sub(tos, tos, Operand(value), SetCC);
1131 }
1132 deferred->Branch(vs);
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001133 if (!both_sides_are_smi) {
1134 __ tst(tos, Operand(kSmiTagMask));
1135 deferred->Branch(ne);
1136 }
ager@chromium.org357bf652010-04-12 11:30:10 +00001137 deferred->BindExit();
1138 frame_->EmitPush(tos);
1139 break;
1140 }
1141
1142
1143 case Token::BIT_OR:
1144 case Token::BIT_XOR:
1145 case Token::BIT_AND: {
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001146 if (both_sides_are_smi) {
1147 switch (op) {
1148 case Token::BIT_OR: __ orr(tos, tos, Operand(value)); break;
1149 case Token::BIT_XOR: __ eor(tos, tos, Operand(value)); break;
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00001150 case Token::BIT_AND: __ And(tos, tos, Operand(value)); break;
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001151 default: UNREACHABLE();
1152 }
1153 frame_->EmitPush(tos, TypeInfo::Smi());
1154 } else {
1155 DeferredCode* deferred =
1156 new DeferredInlineSmiOperation(op, int_value, reversed, mode, tos);
1157 __ tst(tos, Operand(kSmiTagMask));
1158 deferred->Branch(ne);
1159 switch (op) {
1160 case Token::BIT_OR: __ orr(tos, tos, Operand(value)); break;
1161 case Token::BIT_XOR: __ eor(tos, tos, Operand(value)); break;
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00001162 case Token::BIT_AND: __ And(tos, tos, Operand(value)); break;
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001163 default: UNREACHABLE();
1164 }
1165 deferred->BindExit();
1166 TypeInfo result_type =
1167 (op == Token::BIT_AND) ? TypeInfo::Smi() : TypeInfo::Integer32();
1168 frame_->EmitPush(tos, result_type);
ager@chromium.org357bf652010-04-12 11:30:10 +00001169 }
ager@chromium.org357bf652010-04-12 11:30:10 +00001170 break;
1171 }
1172
1173 case Token::SHL:
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001174 if (reversed) {
1175 ASSERT(both_sides_are_smi);
1176 int max_shift = 0;
1177 int max_result = int_value == 0 ? 1 : int_value;
1178 while (Smi::IsValid(max_result << 1)) {
1179 max_shift++;
1180 max_result <<= 1;
1181 }
1182 DeferredCode* deferred =
1183 new DeferredInlineSmiOperation(op, int_value, true, mode, tos);
1184 // Mask off the last 5 bits of the shift operand (rhs). This is part
1185 // of the definition of shift in JS and we know we have a Smi so we
1186 // can safely do this. The masked version gets passed to the
1187 // deferred code, but that makes no difference.
1188 __ and_(tos, tos, Operand(Smi::FromInt(0x1f)));
1189 __ cmp(tos, Operand(Smi::FromInt(max_shift)));
1190 deferred->Branch(ge);
1191 Register scratch = VirtualFrame::scratch0();
1192 __ mov(scratch, Operand(tos, ASR, kSmiTagSize)); // Untag.
1193 __ mov(tos, Operand(Smi::FromInt(int_value))); // Load constant.
1194 __ mov(tos, Operand(tos, LSL, scratch)); // Shift constant.
1195 deferred->BindExit();
1196 TypeInfo result = TypeInfo::Integer32();
1197 frame_->EmitPush(tos, result);
1198 break;
1199 }
1200 // Fall through!
ager@chromium.org357bf652010-04-12 11:30:10 +00001201 case Token::SHR:
1202 case Token::SAR: {
1203 ASSERT(!reversed);
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001204 TypeInfo result = TypeInfo::Integer32();
ager@chromium.org357bf652010-04-12 11:30:10 +00001205 Register scratch = VirtualFrame::scratch0();
1206 Register scratch2 = VirtualFrame::scratch1();
1207 int shift_value = int_value & 0x1f; // least significant 5 bits
1208 DeferredCode* deferred =
1209 new DeferredInlineSmiOperation(op, shift_value, false, mode, tos);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00001210 uint32_t problematic_mask = kSmiTagMask;
1211 // For unsigned shift by zero all negative smis are problematic.
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001212 bool skip_smi_test = both_sides_are_smi;
1213 if (shift_value == 0 && op == Token::SHR) {
1214 problematic_mask |= 0x80000000;
1215 skip_smi_test = false;
1216 }
1217 if (!skip_smi_test) {
1218 __ tst(tos, Operand(problematic_mask));
1219 deferred->Branch(ne); // Go slow for problematic input.
1220 }
ager@chromium.org357bf652010-04-12 11:30:10 +00001221 switch (op) {
1222 case Token::SHL: {
1223 if (shift_value != 0) {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00001224 int adjusted_shift = shift_value - kSmiTagSize;
1225 ASSERT(adjusted_shift >= 0);
1226 if (adjusted_shift != 0) {
1227 __ mov(scratch, Operand(tos, LSL, adjusted_shift));
1228 // Check that the *signed* result fits in a smi.
1229 __ add(scratch2, scratch, Operand(0x40000000), SetCC);
1230 deferred->Branch(mi);
1231 __ mov(tos, Operand(scratch, LSL, kSmiTagSize));
1232 } else {
1233 // Check that the *signed* result fits in a smi.
1234 __ add(scratch2, tos, Operand(0x40000000), SetCC);
1235 deferred->Branch(mi);
1236 __ mov(tos, Operand(tos, LSL, kSmiTagSize));
1237 }
ager@chromium.org357bf652010-04-12 11:30:10 +00001238 }
ager@chromium.org357bf652010-04-12 11:30:10 +00001239 break;
1240 }
1241 case Token::SHR: {
ager@chromium.org357bf652010-04-12 11:30:10 +00001242 if (shift_value != 0) {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00001243 __ mov(scratch, Operand(tos, ASR, kSmiTagSize)); // Remove tag.
1244 // LSR by immediate 0 means shifting 32 bits.
ager@chromium.org357bf652010-04-12 11:30:10 +00001245 __ mov(scratch, Operand(scratch, LSR, shift_value));
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00001246 if (shift_value == 1) {
1247 // check that the *unsigned* result fits in a smi
1248 // neither of the two high-order bits can be set:
1249 // - 0x80000000: high bit would be lost when smi tagging
1250 // - 0x40000000: this number would convert to negative when
1251 // smi tagging these two cases can only happen with shifts
1252 // by 0 or 1 when handed a valid smi
1253 __ tst(scratch, Operand(0xc0000000));
1254 deferred->Branch(ne);
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001255 } else {
1256 ASSERT(shift_value >= 2);
1257 result = TypeInfo::Smi(); // SHR by at least 2 gives a Smi.
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00001258 }
1259 __ mov(tos, Operand(scratch, LSL, kSmiTagSize));
ager@chromium.org357bf652010-04-12 11:30:10 +00001260 }
ager@chromium.org357bf652010-04-12 11:30:10 +00001261 break;
1262 }
1263 case Token::SAR: {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00001264 // In the ARM instructions set, ASR by immediate 0 means shifting 32
1265 // bits.
ager@chromium.org357bf652010-04-12 11:30:10 +00001266 if (shift_value != 0) {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00001267 // Do the shift and the tag removal in one operation. If the shift
1268 // is 31 bits (the highest possible value) then we emit the
1269 // instruction as a shift by 0 which means shift arithmetically by
1270 // 32.
1271 __ mov(tos, Operand(tos, ASR, (kSmiTagSize + shift_value) & 0x1f));
1272 // Put tag back.
1273 __ mov(tos, Operand(tos, LSL, kSmiTagSize));
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001274 // SAR by at least 1 gives a Smi.
1275 result = TypeInfo::Smi();
ager@chromium.org357bf652010-04-12 11:30:10 +00001276 }
1277 break;
1278 }
1279 default: UNREACHABLE();
1280 }
ager@chromium.org357bf652010-04-12 11:30:10 +00001281 deferred->BindExit();
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001282 frame_->EmitPush(tos, result);
ager@chromium.org357bf652010-04-12 11:30:10 +00001283 break;
1284 }
1285
1286 case Token::MOD: {
1287 ASSERT(!reversed);
1288 ASSERT(int_value >= 2);
1289 ASSERT(IsPowerOf2(int_value));
1290 DeferredCode* deferred =
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001291 new DeferredInlineSmiOperation(op, int_value, reversed, mode, tos);
ager@chromium.org357bf652010-04-12 11:30:10 +00001292 unsigned mask = (0x80000000u | kSmiTagMask);
1293 __ tst(tos, Operand(mask));
1294 deferred->Branch(ne); // Go to deferred code on non-Smis and negative.
1295 mask = (int_value << kSmiTagSize) - 1;
1296 __ and_(tos, tos, Operand(mask));
1297 deferred->BindExit();
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001298 // Mod of positive power of 2 Smi gives a Smi if the lhs is an integer.
1299 frame_->EmitPush(
1300 tos,
1301 both_sides_are_smi ? TypeInfo::Smi() : TypeInfo::Number());
ager@chromium.org357bf652010-04-12 11:30:10 +00001302 break;
1303 }
1304
1305 case Token::MUL: {
1306 ASSERT(IsEasyToMultiplyBy(int_value));
1307 DeferredCode* deferred =
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001308 new DeferredInlineSmiOperation(op, int_value, reversed, mode, tos);
ager@chromium.org357bf652010-04-12 11:30:10 +00001309 unsigned max_smi_that_wont_overflow = Smi::kMaxValue / int_value;
1310 max_smi_that_wont_overflow <<= kSmiTagSize;
1311 unsigned mask = 0x80000000u;
1312 while ((mask & max_smi_that_wont_overflow) == 0) {
1313 mask |= mask >> 1;
1314 }
1315 mask |= kSmiTagMask;
1316 // This does a single mask that checks for a too high value in a
1317 // conservative way and for a non-Smi. It also filters out negative
1318 // numbers, unfortunately, but since this code is inline we prefer
1319 // brevity to comprehensiveness.
1320 __ tst(tos, Operand(mask));
1321 deferred->Branch(ne);
1322 MultiplyByKnownInt(masm_, tos, tos, int_value);
1323 deferred->BindExit();
1324 frame_->EmitPush(tos);
1325 break;
1326 }
1327
1328 default:
1329 UNREACHABLE();
1330 break;
1331 }
1332}
1333
1334
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001335void CodeGenerator::Comparison(Condition cc,
1336 Expression* left,
1337 Expression* right,
1338 bool strict) {
ager@chromium.org357bf652010-04-12 11:30:10 +00001339 VirtualFrame::RegisterAllocationScope scope(this);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001340
ager@chromium.org357bf652010-04-12 11:30:10 +00001341 if (left != NULL) Load(left);
1342 if (right != NULL) Load(right);
1343
mads.s.ager31e71382008-08-13 09:32:07 +00001344 // sp[0] : y
1345 // sp[1] : x
1346 // result : cc register
1347
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001348 // Strict only makes sense for equality comparisons.
1349 ASSERT(!strict || cc == eq);
1350
ager@chromium.org357bf652010-04-12 11:30:10 +00001351 Register lhs;
1352 Register rhs;
1353
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001354 bool lhs_is_smi;
1355 bool rhs_is_smi;
1356
ager@chromium.org357bf652010-04-12 11:30:10 +00001357 // We load the top two stack positions into registers chosen by the virtual
1358 // frame. This should keep the register shuffling to a minimum.
mads.s.ager@gmail.com769cc962008-08-06 10:02:49 +00001359 // Implement '>' and '<=' by reversal to obtain ECMA-262 conversion order.
1360 if (cc == gt || cc == le) {
1361 cc = ReverseCondition(cc);
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001362 lhs_is_smi = frame_->KnownSmiAt(0);
1363 rhs_is_smi = frame_->KnownSmiAt(1);
ager@chromium.org357bf652010-04-12 11:30:10 +00001364 lhs = frame_->PopToRegister();
1365 rhs = frame_->PopToRegister(lhs); // Don't pop to the same register again!
mads.s.ager@gmail.com769cc962008-08-06 10:02:49 +00001366 } else {
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001367 rhs_is_smi = frame_->KnownSmiAt(0);
1368 lhs_is_smi = frame_->KnownSmiAt(1);
ager@chromium.org357bf652010-04-12 11:30:10 +00001369 rhs = frame_->PopToRegister();
1370 lhs = frame_->PopToRegister(rhs); // Don't pop to the same register again!
mads.s.ager@gmail.com769cc962008-08-06 10:02:49 +00001371 }
ager@chromium.org357bf652010-04-12 11:30:10 +00001372
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001373 bool both_sides_are_smi = (lhs_is_smi && rhs_is_smi);
1374
ager@chromium.org357bf652010-04-12 11:30:10 +00001375 ASSERT(rhs.is(r0) || rhs.is(r1));
1376 ASSERT(lhs.is(r0) || lhs.is(r1));
1377
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001378 JumpTarget exit;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001379
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001380 if (!both_sides_are_smi) {
1381 // Now we have the two sides in r0 and r1. We flush any other registers
1382 // because the stub doesn't know about register allocation.
1383 frame_->SpillAll();
1384 Register scratch = VirtualFrame::scratch0();
1385 Register smi_test_reg;
1386 if (lhs_is_smi) {
1387 smi_test_reg = rhs;
1388 } else if (rhs_is_smi) {
1389 smi_test_reg = lhs;
1390 } else {
1391 __ orr(scratch, lhs, Operand(rhs));
1392 smi_test_reg = scratch;
1393 }
1394 __ tst(smi_test_reg, Operand(kSmiTagMask));
1395 JumpTarget smi;
1396 smi.Branch(eq);
1397
1398 // Perform non-smi comparison by stub.
1399 // CompareStub takes arguments in r0 and r1, returns <0, >0 or 0 in r0.
1400 // We call with 0 args because there are 0 on the stack.
1401 if (!rhs.is(r0)) {
1402 __ Swap(rhs, lhs, ip);
1403 }
1404
1405 CompareStub stub(cc, strict);
1406 frame_->CallStub(&stub, 0);
1407 __ cmp(r0, Operand(0));
1408 exit.Jump();
1409
1410 smi.Bind();
ager@chromium.org357bf652010-04-12 11:30:10 +00001411 }
1412
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001413 // Do smi comparisons by pointer comparison.
ager@chromium.org357bf652010-04-12 11:30:10 +00001414 __ cmp(lhs, Operand(rhs));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001415
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001416 exit.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001417 cc_reg_ = cc;
1418}
1419
1420
mads.s.ager31e71382008-08-13 09:32:07 +00001421// Call the function on the stack with the given arguments.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001422void CodeGenerator::CallWithArguments(ZoneList<Expression*>* args,
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001423 CallFunctionFlags flags,
1424 int position) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001425 // Push the arguments ("left-to-right") on the stack.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001426 int arg_count = args->length();
1427 for (int i = 0; i < arg_count; i++) {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001428 Load(args->at(i));
mads.s.ager31e71382008-08-13 09:32:07 +00001429 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001430
kasper.lund7276f142008-07-30 08:49:36 +00001431 // Record the position for debugging purposes.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001432 CodeForSourcePosition(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001433
kasper.lund7276f142008-07-30 08:49:36 +00001434 // Use the shared code stub to call the function.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001435 InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001436 CallFunctionStub call_function(arg_count, in_loop, flags);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001437 frame_->CallStub(&call_function, arg_count + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001438
1439 // Restore context and pop function from the stack.
ager@chromium.org3bf7b912008-11-17 09:09:45 +00001440 __ ldr(cp, frame_->Context());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001441 frame_->Drop(); // discard the TOS
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001442}
1443
1444
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00001445void CodeGenerator::CallApplyLazy(Expression* applicand,
1446 Expression* receiver,
1447 VariableProxy* arguments,
1448 int position) {
1449 // An optimized implementation of expressions of the form
1450 // x.apply(y, arguments).
1451 // If the arguments object of the scope has not been allocated,
1452 // and x.apply is Function.prototype.apply, this optimization
1453 // just copies y and the arguments of the current function on the
1454 // stack, as receiver and arguments, and calls x.
1455 // In the implementation comments, we call x the applicand
1456 // and y the receiver.
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00001457
1458 ASSERT(ArgumentsMode() == LAZY_ARGUMENTS_ALLOCATION);
1459 ASSERT(arguments->IsArguments());
1460
1461 // Load applicand.apply onto the stack. This will usually
1462 // give us a megamorphic load site. Not super, but it works.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001463 Load(applicand);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00001464 Handle<String> name = Factory::LookupAsciiSymbol("apply");
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001465 frame_->Dup();
ager@chromium.orgac091b72010-05-05 07:34:42 +00001466 frame_->CallLoadIC(name, RelocInfo::CODE_TARGET);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00001467 frame_->EmitPush(r0);
1468
1469 // Load the receiver and the existing arguments object onto the
1470 // expression stack. Avoid allocating the arguments object here.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001471 Load(receiver);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00001472 LoadFromSlot(scope()->arguments()->var()->slot(), NOT_INSIDE_TYPEOF);
1473
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00001474 // At this point the top two stack elements are probably in registers
1475 // since they were just loaded. Ensure they are in regs and get the
1476 // regs.
1477 Register receiver_reg = frame_->Peek2();
1478 Register arguments_reg = frame_->Peek();
1479
1480 // From now on the frame is spilled.
1481 frame_->SpillAll();
1482
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00001483 // Emit the source position information after having loaded the
1484 // receiver and the arguments.
1485 CodeForSourcePosition(position);
1486 // Contents of the stack at this point:
1487 // sp[0]: arguments object of the current function or the hole.
1488 // sp[1]: receiver
1489 // sp[2]: applicand.apply
1490 // sp[3]: applicand.
1491
1492 // Check if the arguments object has been lazily allocated
1493 // already. If so, just use that instead of copying the arguments
1494 // from the stack. This also deals with cases where a local variable
1495 // named 'arguments' has been introduced.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00001496 JumpTarget slow;
1497 Label done;
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00001498 __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00001499 __ cmp(ip, arguments_reg);
1500 slow.Branch(ne);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00001501
1502 Label build_args;
1503 // Get rid of the arguments object probe.
1504 frame_->Drop();
1505 // Stack now has 3 elements on it.
1506 // Contents of stack at this point:
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00001507 // sp[0]: receiver - in the receiver_reg register.
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00001508 // sp[1]: applicand.apply
1509 // sp[2]: applicand.
1510
1511 // Check that the receiver really is a JavaScript object.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00001512 __ BranchOnSmi(receiver_reg, &build_args);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00001513 // We allow all JSObjects including JSFunctions. As long as
1514 // JS_FUNCTION_TYPE is the last instance type and it is right
1515 // after LAST_JS_OBJECT_TYPE, we do not have to check the upper
1516 // bound.
1517 ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
1518 ASSERT(JS_FUNCTION_TYPE == LAST_JS_OBJECT_TYPE + 1);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00001519 __ CompareObjectType(receiver_reg, r2, r3, FIRST_JS_OBJECT_TYPE);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00001520 __ b(lt, &build_args);
1521
1522 // Check that applicand.apply is Function.prototype.apply.
1523 __ ldr(r0, MemOperand(sp, kPointerSize));
1524 __ BranchOnSmi(r0, &build_args);
1525 __ CompareObjectType(r0, r1, r2, JS_FUNCTION_TYPE);
1526 __ b(ne, &build_args);
1527 __ ldr(r0, FieldMemOperand(r0, JSFunction::kSharedFunctionInfoOffset));
1528 Handle<Code> apply_code(Builtins::builtin(Builtins::FunctionApply));
1529 __ ldr(r1, FieldMemOperand(r0, SharedFunctionInfo::kCodeOffset));
1530 __ cmp(r1, Operand(apply_code));
1531 __ b(ne, &build_args);
1532
1533 // Check that applicand is a function.
1534 __ ldr(r1, MemOperand(sp, 2 * kPointerSize));
1535 __ BranchOnSmi(r1, &build_args);
1536 __ CompareObjectType(r1, r2, r3, JS_FUNCTION_TYPE);
1537 __ b(ne, &build_args);
1538
1539 // Copy the arguments to this function possibly from the
1540 // adaptor frame below it.
1541 Label invoke, adapted;
1542 __ ldr(r2, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
1543 __ ldr(r3, MemOperand(r2, StandardFrameConstants::kContextOffset));
1544 __ cmp(r3, Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
1545 __ b(eq, &adapted);
1546
1547 // No arguments adaptor frame. Copy fixed number of arguments.
1548 __ mov(r0, Operand(scope()->num_parameters()));
1549 for (int i = 0; i < scope()->num_parameters(); i++) {
1550 __ ldr(r2, frame_->ParameterAt(i));
1551 __ push(r2);
1552 }
1553 __ jmp(&invoke);
1554
1555 // Arguments adaptor frame present. Copy arguments from there, but
1556 // avoid copying too many arguments to avoid stack overflows.
1557 __ bind(&adapted);
1558 static const uint32_t kArgumentsLimit = 1 * KB;
1559 __ ldr(r0, MemOperand(r2, ArgumentsAdaptorFrameConstants::kLengthOffset));
1560 __ mov(r0, Operand(r0, LSR, kSmiTagSize));
1561 __ mov(r3, r0);
1562 __ cmp(r0, Operand(kArgumentsLimit));
1563 __ b(gt, &build_args);
1564
1565 // Loop through the arguments pushing them onto the execution
1566 // stack. We don't inform the virtual frame of the push, so we don't
1567 // have to worry about getting rid of the elements from the virtual
1568 // frame.
1569 Label loop;
1570 // r3 is a small non-negative integer, due to the test above.
1571 __ cmp(r3, Operand(0));
1572 __ b(eq, &invoke);
1573 // Compute the address of the first argument.
1574 __ add(r2, r2, Operand(r3, LSL, kPointerSizeLog2));
1575 __ add(r2, r2, Operand(kPointerSize));
1576 __ bind(&loop);
1577 // Post-decrement argument address by kPointerSize on each iteration.
1578 __ ldr(r4, MemOperand(r2, kPointerSize, NegPostIndex));
1579 __ push(r4);
1580 __ sub(r3, r3, Operand(1), SetCC);
1581 __ b(gt, &loop);
1582
1583 // Invoke the function.
1584 __ bind(&invoke);
1585 ParameterCount actual(r0);
1586 __ InvokeFunction(r1, actual, CALL_FUNCTION);
1587 // Drop applicand.apply and applicand from the stack, and push
1588 // the result of the function call, but leave the spilled frame
1589 // unchanged, with 3 elements, so it is correct when we compile the
1590 // slow-case code.
1591 __ add(sp, sp, Operand(2 * kPointerSize));
1592 __ push(r0);
1593 // Stack now has 1 element:
1594 // sp[0]: result
1595 __ jmp(&done);
1596
1597 // Slow-case: Allocate the arguments object since we know it isn't
1598 // there, and fall-through to the slow-case where we call
1599 // applicand.apply.
1600 __ bind(&build_args);
1601 // Stack now has 3 elements, because we have jumped from where:
1602 // sp[0]: receiver
1603 // sp[1]: applicand.apply
1604 // sp[2]: applicand.
1605 StoreArgumentsObject(false);
1606
1607 // Stack and frame now have 4 elements.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00001608 slow.Bind();
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00001609
1610 // Generic computation of x.apply(y, args) with no special optimization.
1611 // Flip applicand.apply and applicand on the stack, so
1612 // applicand looks like the receiver of the applicand.apply call.
1613 // Then process it as a normal function call.
1614 __ ldr(r0, MemOperand(sp, 3 * kPointerSize));
1615 __ ldr(r1, MemOperand(sp, 2 * kPointerSize));
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00001616 __ Strd(r0, r1, MemOperand(sp, 2 * kPointerSize));
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00001617
1618 CallFunctionStub call_function(2, NOT_IN_LOOP, NO_CALL_FUNCTION_FLAGS);
1619 frame_->CallStub(&call_function, 3);
1620 // The function and its two arguments have been dropped.
1621 frame_->Drop(); // Drop the receiver as well.
1622 frame_->EmitPush(r0);
1623 // Stack now has 1 element:
1624 // sp[0]: result
1625 __ bind(&done);
1626
1627 // Restore the context register after a call.
1628 __ ldr(cp, frame_->Context());
1629}
1630
1631
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001632void CodeGenerator::Branch(bool if_true, JumpTarget* target) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001633 ASSERT(has_cc());
1634 Condition cc = if_true ? cc_reg_ : NegateCondition(cc_reg_);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001635 target->Branch(cc);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001636 cc_reg_ = al;
1637}
1638
1639
ager@chromium.org7c537e22008-10-16 08:43:32 +00001640void CodeGenerator::CheckStack() {
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00001641 frame_->SpillAll();
ager@chromium.org3811b432009-10-28 14:53:37 +00001642 Comment cmnt(masm_, "[ check stack");
1643 __ LoadRoot(ip, Heap::kStackLimitRootIndex);
1644 // Put the lr setup instruction in the delay slot. kInstrSize is added to
1645 // the implicit 8 byte offset that always applies to operations with pc and
1646 // gives a return address 12 bytes down.
1647 masm_->add(lr, pc, Operand(Assembler::kInstrSize));
1648 masm_->cmp(sp, Operand(ip));
1649 StackCheckStub stub;
1650 // Call the stub if lower.
1651 masm_->mov(pc,
1652 Operand(reinterpret_cast<intptr_t>(stub.GetCode().location()),
1653 RelocInfo::CODE_TARGET),
1654 LeaveCC,
1655 lo);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001656}
1657
1658
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001659void CodeGenerator::VisitStatements(ZoneList<Statement*>* statements) {
1660#ifdef DEBUG
1661 int original_height = frame_->height();
1662#endif
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001663 for (int i = 0; frame_ != NULL && i < statements->length(); i++) {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001664 Visit(statements->at(i));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001665 }
1666 ASSERT(!has_valid_frame() || frame_->height() == original_height);
1667}
1668
1669
ager@chromium.org7c537e22008-10-16 08:43:32 +00001670void CodeGenerator::VisitBlock(Block* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001671#ifdef DEBUG
1672 int original_height = frame_->height();
1673#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001674 Comment cmnt(masm_, "[ Block");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001675 CodeForStatementPosition(node);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00001676 node->break_target()->SetExpectedHeight();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001677 VisitStatements(node->statements());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001678 if (node->break_target()->is_linked()) {
1679 node->break_target()->Bind();
1680 }
1681 node->break_target()->Unuse();
1682 ASSERT(!has_valid_frame() || frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001683}
1684
1685
ager@chromium.org7c537e22008-10-16 08:43:32 +00001686void CodeGenerator::DeclareGlobals(Handle<FixedArray> pairs) {
ager@chromium.org3811b432009-10-28 14:53:37 +00001687 frame_->EmitPush(cp);
ager@chromium.orgac091b72010-05-05 07:34:42 +00001688 frame_->EmitPush(Operand(pairs));
1689 frame_->EmitPush(Operand(Smi::FromInt(is_eval() ? 1 : 0)));
1690
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001691 frame_->CallRuntime(Runtime::kDeclareGlobals, 3);
mads.s.ager31e71382008-08-13 09:32:07 +00001692 // The result is discarded.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001693}
1694
1695
ager@chromium.org7c537e22008-10-16 08:43:32 +00001696void CodeGenerator::VisitDeclaration(Declaration* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001697#ifdef DEBUG
1698 int original_height = frame_->height();
1699#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001700 Comment cmnt(masm_, "[ Declaration");
1701 Variable* var = node->proxy()->var();
1702 ASSERT(var != NULL); // must have been resolved
1703 Slot* slot = var->slot();
1704
1705 // If it was not possible to allocate the variable at compile time,
1706 // we need to "declare" it at runtime to make sure it actually
1707 // exists in the local context.
1708 if (slot != NULL && slot->type() == Slot::LOOKUP) {
1709 // Variables with a "LOOKUP" slot were introduced as non-locals
1710 // during variable resolution and must have mode DYNAMIC.
ager@chromium.org381abbb2009-02-25 13:23:22 +00001711 ASSERT(var->is_dynamic());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001712 // For now, just do a runtime call.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001713 frame_->EmitPush(cp);
ager@chromium.orgac091b72010-05-05 07:34:42 +00001714 frame_->EmitPush(Operand(var->name()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001715 // Declaration nodes are always declared in only two modes.
1716 ASSERT(node->mode() == Variable::VAR || node->mode() == Variable::CONST);
1717 PropertyAttributes attr = node->mode() == Variable::VAR ? NONE : READ_ONLY;
ager@chromium.orgac091b72010-05-05 07:34:42 +00001718 frame_->EmitPush(Operand(Smi::FromInt(attr)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001719 // Push initial value, if any.
1720 // Note: For variables we must not push an initial value (such as
1721 // 'undefined') because we may have a (legal) redeclaration and we
1722 // must not destroy the current value.
1723 if (node->mode() == Variable::CONST) {
ager@chromium.orgac091b72010-05-05 07:34:42 +00001724 frame_->EmitPushRoot(Heap::kTheHoleValueRootIndex);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001725 } else if (node->fun() != NULL) {
ager@chromium.orgac091b72010-05-05 07:34:42 +00001726 Load(node->fun());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001727 } else {
ager@chromium.orgac091b72010-05-05 07:34:42 +00001728 frame_->EmitPush(Operand(0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001729 }
ager@chromium.orgac091b72010-05-05 07:34:42 +00001730
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001731 frame_->CallRuntime(Runtime::kDeclareContextSlot, 4);
ager@chromium.org7c537e22008-10-16 08:43:32 +00001732 // Ignore the return value (declarations are statements).
ager@chromium.orgac091b72010-05-05 07:34:42 +00001733
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001734 ASSERT(frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001735 return;
1736 }
1737
1738 ASSERT(!var->is_global());
1739
1740 // If we have a function or a constant, we need to initialize the variable.
1741 Expression* val = NULL;
1742 if (node->mode() == Variable::CONST) {
1743 val = new Literal(Factory::the_hole_value());
1744 } else {
1745 val = node->fun(); // NULL if we don't have a function
1746 }
1747
1748 if (val != NULL) {
ager@chromium.orgac091b72010-05-05 07:34:42 +00001749 // Set initial value.
1750 Reference target(this, node->proxy());
1751 Load(val);
1752 target.SetValue(NOT_CONST_INIT);
1753
iposva@chromium.org245aa852009-02-10 00:49:54 +00001754 // Get rid of the assigned value (declarations are statements).
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001755 frame_->Drop();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001756 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001757 ASSERT(frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001758}
1759
1760
ager@chromium.org7c537e22008-10-16 08:43:32 +00001761void CodeGenerator::VisitExpressionStatement(ExpressionStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001762#ifdef DEBUG
1763 int original_height = frame_->height();
1764#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001765 Comment cmnt(masm_, "[ ExpressionStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001766 CodeForStatementPosition(node);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001767 Expression* expression = node->expression();
1768 expression->MarkAsStatement();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001769 Load(expression);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001770 frame_->Drop();
1771 ASSERT(frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001772}
1773
1774
ager@chromium.org7c537e22008-10-16 08:43:32 +00001775void CodeGenerator::VisitEmptyStatement(EmptyStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001776#ifdef DEBUG
1777 int original_height = frame_->height();
1778#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001779 Comment cmnt(masm_, "// EmptyStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001780 CodeForStatementPosition(node);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001781 // nothing to do
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001782 ASSERT(frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001783}
1784
1785
ager@chromium.org7c537e22008-10-16 08:43:32 +00001786void CodeGenerator::VisitIfStatement(IfStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001787#ifdef DEBUG
1788 int original_height = frame_->height();
1789#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001790 Comment cmnt(masm_, "[ IfStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001791 // Generate different code depending on which parts of the if statement
1792 // are present or not.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001793 bool has_then_stm = node->HasThenStatement();
1794 bool has_else_stm = node->HasElseStatement();
1795
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001796 CodeForStatementPosition(node);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001797
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001798 JumpTarget exit;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001799 if (has_then_stm && has_else_stm) {
mads.s.ager31e71382008-08-13 09:32:07 +00001800 Comment cmnt(masm_, "[ IfThenElse");
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001801 JumpTarget then;
1802 JumpTarget else_;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001803 // if (cond)
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001804 LoadCondition(node->condition(), &then, &else_, true);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001805 if (frame_ != NULL) {
1806 Branch(false, &else_);
1807 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001808 // then
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001809 if (frame_ != NULL || then.is_linked()) {
1810 then.Bind();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001811 Visit(node->then_statement());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001812 }
1813 if (frame_ != NULL) {
1814 exit.Jump();
1815 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001816 // else
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001817 if (else_.is_linked()) {
1818 else_.Bind();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001819 Visit(node->else_statement());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001820 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001821
1822 } else if (has_then_stm) {
mads.s.ager31e71382008-08-13 09:32:07 +00001823 Comment cmnt(masm_, "[ IfThen");
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001824 ASSERT(!has_else_stm);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001825 JumpTarget then;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001826 // if (cond)
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001827 LoadCondition(node->condition(), &then, &exit, true);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001828 if (frame_ != NULL) {
1829 Branch(false, &exit);
1830 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001831 // then
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001832 if (frame_ != NULL || then.is_linked()) {
1833 then.Bind();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001834 Visit(node->then_statement());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001835 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001836
1837 } else if (has_else_stm) {
mads.s.ager31e71382008-08-13 09:32:07 +00001838 Comment cmnt(masm_, "[ IfElse");
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001839 ASSERT(!has_then_stm);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001840 JumpTarget else_;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001841 // if (!cond)
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001842 LoadCondition(node->condition(), &exit, &else_, true);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001843 if (frame_ != NULL) {
1844 Branch(true, &exit);
1845 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001846 // else
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001847 if (frame_ != NULL || else_.is_linked()) {
1848 else_.Bind();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001849 Visit(node->else_statement());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001850 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001851
1852 } else {
mads.s.ager31e71382008-08-13 09:32:07 +00001853 Comment cmnt(masm_, "[ If");
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001854 ASSERT(!has_then_stm && !has_else_stm);
1855 // if (cond)
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001856 LoadCondition(node->condition(), &exit, &exit, false);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001857 if (frame_ != NULL) {
1858 if (has_cc()) {
1859 cc_reg_ = al;
1860 } else {
1861 frame_->Drop();
1862 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001863 }
1864 }
1865
1866 // end
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001867 if (exit.is_linked()) {
1868 exit.Bind();
1869 }
1870 ASSERT(!has_valid_frame() || frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001871}
1872
1873
ager@chromium.org7c537e22008-10-16 08:43:32 +00001874void CodeGenerator::VisitContinueStatement(ContinueStatement* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001875 Comment cmnt(masm_, "[ ContinueStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001876 CodeForStatementPosition(node);
1877 node->target()->continue_target()->Jump();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001878}
1879
1880
ager@chromium.org7c537e22008-10-16 08:43:32 +00001881void CodeGenerator::VisitBreakStatement(BreakStatement* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001882 Comment cmnt(masm_, "[ BreakStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001883 CodeForStatementPosition(node);
1884 node->target()->break_target()->Jump();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001885}
1886
1887
ager@chromium.org7c537e22008-10-16 08:43:32 +00001888void CodeGenerator::VisitReturnStatement(ReturnStatement* node) {
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00001889 frame_->SpillAll();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001890 Comment cmnt(masm_, "[ ReturnStatement");
mads.s.ager31e71382008-08-13 09:32:07 +00001891
ager@chromium.org4af710e2009-09-15 12:20:11 +00001892 CodeForStatementPosition(node);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001893 Load(node->expression());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001894 if (function_return_is_shadowed_) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001895 frame_->EmitPop(r0);
1896 function_return_.Jump();
1897 } else {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001898 // Pop the result from the frame and prepare the frame for
1899 // returning thus making it easier to merge.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00001900 frame_->PopToR0();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001901 frame_->PrepareForReturn();
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00001902 if (function_return_.is_bound()) {
1903 // If the function return label is already bound we reuse the
1904 // code by jumping to the return site.
1905 function_return_.Jump();
1906 } else {
1907 function_return_.Bind();
1908 GenerateReturnSequence();
1909 }
1910 }
1911}
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001912
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00001913
1914void CodeGenerator::GenerateReturnSequence() {
1915 if (FLAG_trace) {
1916 // Push the return value on the stack as the parameter.
1917 // Runtime::TraceExit returns the parameter as it is.
1918 frame_->EmitPush(r0);
1919 frame_->CallRuntime(Runtime::kTraceExit, 1);
1920 }
1921
1922#ifdef DEBUG
1923 // Add a label for checking the size of the code used for returning.
1924 Label check_exit_codesize;
1925 masm_->bind(&check_exit_codesize);
1926#endif
1927 // Make sure that the constant pool is not emitted inside of the return
1928 // sequence.
1929 { Assembler::BlockConstPoolScope block_const_pool(masm_);
1930 // Tear down the frame which will restore the caller's frame pointer and
1931 // the link register.
1932 frame_->Exit();
1933
1934 // Here we use masm_-> instead of the __ macro to avoid the code coverage
1935 // tool from instrumenting as we rely on the code size here.
1936 int32_t sp_delta = (scope()->num_parameters() + 1) * kPointerSize;
1937 masm_->add(sp, sp, Operand(sp_delta));
1938 masm_->Jump(lr);
1939 DeleteFrame();
1940
1941#ifdef DEBUG
1942 // Check that the size of the code used for returning matches what is
1943 // expected by the debugger. If the sp_delts above cannot be encoded in
1944 // the add instruction the add will generate two instructions.
1945 int return_sequence_length =
1946 masm_->InstructionsGeneratedSince(&check_exit_codesize);
1947 CHECK(return_sequence_length ==
1948 Assembler::kJSReturnSequenceInstructions ||
1949 return_sequence_length ==
1950 Assembler::kJSReturnSequenceInstructions + 1);
1951#endif
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001952 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001953}
1954
1955
ager@chromium.org7c537e22008-10-16 08:43:32 +00001956void CodeGenerator::VisitWithEnterStatement(WithEnterStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001957#ifdef DEBUG
1958 int original_height = frame_->height();
1959#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001960 Comment cmnt(masm_, "[ WithEnterStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001961 CodeForStatementPosition(node);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001962 Load(node->expression());
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001963 if (node->is_catch_block()) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001964 frame_->CallRuntime(Runtime::kPushCatchContext, 1);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001965 } else {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001966 frame_->CallRuntime(Runtime::kPushContext, 1);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001967 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00001968#ifdef DEBUG
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001969 JumpTarget verified_true;
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00001970 __ cmp(r0, cp);
ager@chromium.org65dad4b2009-04-23 08:48:43 +00001971 verified_true.Branch(eq);
1972 __ stop("PushContext: r0 is expected to be the same as cp");
1973 verified_true.Bind();
1974#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001975 // Update context local.
ager@chromium.org3bf7b912008-11-17 09:09:45 +00001976 __ str(cp, frame_->Context());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001977 ASSERT(frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001978}
1979
1980
ager@chromium.org7c537e22008-10-16 08:43:32 +00001981void CodeGenerator::VisitWithExitStatement(WithExitStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001982#ifdef DEBUG
1983 int original_height = frame_->height();
1984#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001985 Comment cmnt(masm_, "[ WithExitStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001986 CodeForStatementPosition(node);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001987 // Pop context.
1988 __ ldr(cp, ContextOperand(cp, Context::PREVIOUS_INDEX));
1989 // Update context local.
ager@chromium.org3bf7b912008-11-17 09:09:45 +00001990 __ str(cp, frame_->Context());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001991 ASSERT(frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001992}
1993
1994
ager@chromium.org7c537e22008-10-16 08:43:32 +00001995void CodeGenerator::VisitSwitchStatement(SwitchStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001996#ifdef DEBUG
1997 int original_height = frame_->height();
1998#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001999 Comment cmnt(masm_, "[ SwitchStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002000 CodeForStatementPosition(node);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00002001 node->break_target()->SetExpectedHeight();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002002
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002003 Load(node->tag());
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002004
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002005 JumpTarget next_test;
2006 JumpTarget fall_through;
2007 JumpTarget default_entry;
2008 JumpTarget default_exit(JumpTarget::BIDIRECTIONAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002009 ZoneList<CaseClause*>* cases = node->cases();
2010 int length = cases->length();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002011 CaseClause* default_clause = NULL;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002012
2013 for (int i = 0; i < length; i++) {
2014 CaseClause* clause = cases->at(i);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002015 if (clause->is_default()) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002016 // Remember the default clause and compile it at the end.
2017 default_clause = clause;
2018 continue;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002019 }
2020
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002021 Comment cmnt(masm_, "[ Case clause");
2022 // Compile the test.
2023 next_test.Bind();
2024 next_test.Unuse();
2025 // Duplicate TOS.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00002026 frame_->Dup();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00002027 Comparison(eq, NULL, clause->label(), true);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002028 Branch(false, &next_test);
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00002029
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002030 // Before entering the body from the test, remove the switch value from
2031 // the stack.
2032 frame_->Drop();
2033
2034 // Label the body so that fall through is enabled.
2035 if (i > 0 && cases->at(i - 1)->is_default()) {
2036 default_exit.Bind();
2037 } else {
2038 fall_through.Bind();
2039 fall_through.Unuse();
2040 }
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002041 VisitStatements(clause->statements());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002042
2043 // If control flow can fall through from the body, jump to the next body
2044 // or the end of the statement.
2045 if (frame_ != NULL) {
2046 if (i < length - 1 && cases->at(i + 1)->is_default()) {
2047 default_entry.Jump();
2048 } else {
2049 fall_through.Jump();
2050 }
2051 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002052 }
2053
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002054 // The final "test" removes the switch value.
2055 next_test.Bind();
2056 frame_->Drop();
2057
2058 // If there is a default clause, compile it.
2059 if (default_clause != NULL) {
2060 Comment cmnt(masm_, "[ Default clause");
2061 default_entry.Bind();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002062 VisitStatements(default_clause->statements());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002063 // If control flow can fall out of the default and there is a case after
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00002064 // it, jump to that case's body.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002065 if (frame_ != NULL && default_exit.is_bound()) {
2066 default_exit.Jump();
2067 }
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00002068 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002069
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002070 if (fall_through.is_linked()) {
2071 fall_through.Bind();
2072 }
2073
2074 if (node->break_target()->is_linked()) {
2075 node->break_target()->Bind();
2076 }
2077 node->break_target()->Unuse();
2078 ASSERT(!has_valid_frame() || frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002079}
2080
2081
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002082void CodeGenerator::VisitDoWhileStatement(DoWhileStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002083#ifdef DEBUG
2084 int original_height = frame_->height();
2085#endif
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002086 Comment cmnt(masm_, "[ DoWhileStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002087 CodeForStatementPosition(node);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00002088 node->break_target()->SetExpectedHeight();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002089 JumpTarget body(JumpTarget::BIDIRECTIONAL);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00002090 IncrementLoopNesting();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002091
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002092 // Label the top of the loop for the backward CFG edge. If the test
2093 // is always true we can use the continue target, and if the test is
2094 // always false there is no need.
2095 ConditionAnalysis info = AnalyzeCondition(node->cond());
2096 switch (info) {
2097 case ALWAYS_TRUE:
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00002098 node->continue_target()->SetExpectedHeight();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002099 node->continue_target()->Bind();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002100 break;
2101 case ALWAYS_FALSE:
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00002102 node->continue_target()->SetExpectedHeight();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002103 break;
2104 case DONT_KNOW:
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00002105 node->continue_target()->SetExpectedHeight();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002106 body.Bind();
2107 break;
2108 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002109
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002110 CheckStack(); // TODO(1222600): ignore if body contains calls.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002111 Visit(node->body());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002112
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00002113 // Compile the test.
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002114 switch (info) {
2115 case ALWAYS_TRUE:
2116 // If control can fall off the end of the body, jump back to the
2117 // top.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002118 if (has_valid_frame()) {
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002119 node->continue_target()->Jump();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002120 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002121 break;
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002122 case ALWAYS_FALSE:
2123 // If we have a continue in the body, we only have to bind its
2124 // jump target.
2125 if (node->continue_target()->is_linked()) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002126 node->continue_target()->Bind();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002127 }
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002128 break;
2129 case DONT_KNOW:
2130 // We have to compile the test expression if it can be reached by
2131 // control flow falling out of the body or via continue.
2132 if (node->continue_target()->is_linked()) {
2133 node->continue_target()->Bind();
2134 }
2135 if (has_valid_frame()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002136 Comment cmnt(masm_, "[ DoWhileCondition");
2137 CodeForDoWhileConditionPosition(node);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002138 LoadCondition(node->cond(), &body, node->break_target(), true);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002139 if (has_valid_frame()) {
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002140 // A invalid frame here indicates that control did not
2141 // fall out of the test expression.
2142 Branch(true, &body);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002143 }
2144 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002145 break;
2146 }
2147
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002148 if (node->break_target()->is_linked()) {
2149 node->break_target()->Bind();
2150 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00002151 DecrementLoopNesting();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002152 ASSERT(!has_valid_frame() || frame_->height() == original_height);
2153}
2154
2155
2156void CodeGenerator::VisitWhileStatement(WhileStatement* node) {
2157#ifdef DEBUG
2158 int original_height = frame_->height();
2159#endif
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002160 Comment cmnt(masm_, "[ WhileStatement");
2161 CodeForStatementPosition(node);
2162
2163 // If the test is never true and has no side effects there is no need
2164 // to compile the test or body.
2165 ConditionAnalysis info = AnalyzeCondition(node->cond());
2166 if (info == ALWAYS_FALSE) return;
2167
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00002168 node->break_target()->SetExpectedHeight();
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00002169 IncrementLoopNesting();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002170
2171 // Label the top of the loop with the continue target for the backward
2172 // CFG edge.
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00002173 node->continue_target()->SetExpectedHeight();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002174 node->continue_target()->Bind();
2175
2176 if (info == DONT_KNOW) {
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00002177 JumpTarget body(JumpTarget::BIDIRECTIONAL);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002178 LoadCondition(node->cond(), &body, node->break_target(), true);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002179 if (has_valid_frame()) {
2180 // A NULL frame indicates that control did not fall out of the
2181 // test expression.
2182 Branch(false, node->break_target());
2183 }
2184 if (has_valid_frame() || body.is_linked()) {
2185 body.Bind();
2186 }
2187 }
2188
2189 if (has_valid_frame()) {
2190 CheckStack(); // TODO(1222600): ignore if body contains calls.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002191 Visit(node->body());
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002192
2193 // If control flow can fall out of the body, jump back to the top.
2194 if (has_valid_frame()) {
2195 node->continue_target()->Jump();
2196 }
2197 }
2198 if (node->break_target()->is_linked()) {
2199 node->break_target()->Bind();
2200 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00002201 DecrementLoopNesting();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002202 ASSERT(!has_valid_frame() || frame_->height() == original_height);
2203}
2204
2205
2206void CodeGenerator::VisitForStatement(ForStatement* node) {
2207#ifdef DEBUG
2208 int original_height = frame_->height();
2209#endif
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002210 Comment cmnt(masm_, "[ ForStatement");
2211 CodeForStatementPosition(node);
2212 if (node->init() != NULL) {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002213 Visit(node->init());
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002214 }
2215
2216 // If the test is never true there is no need to compile the test or
2217 // body.
2218 ConditionAnalysis info = AnalyzeCondition(node->cond());
2219 if (info == ALWAYS_FALSE) return;
2220
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00002221 node->break_target()->SetExpectedHeight();
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00002222 IncrementLoopNesting();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002223
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00002224 // We know that the loop index is a smi if it is not modified in the
2225 // loop body and it is checked against a constant limit in the loop
2226 // condition. In this case, we reset the static type information of the
2227 // loop index to smi before compiling the body, the update expression, and
2228 // the bottom check of the loop condition.
2229 TypeInfoCodeGenState type_info_scope(this,
2230 node->is_fast_smi_loop() ?
2231 node->loop_variable()->slot() :
2232 NULL,
2233 TypeInfo::Smi());
2234
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002235 // If there is no update statement, label the top of the loop with the
2236 // continue target, otherwise with the loop target.
2237 JumpTarget loop(JumpTarget::BIDIRECTIONAL);
2238 if (node->next() == NULL) {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00002239 node->continue_target()->SetExpectedHeight();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002240 node->continue_target()->Bind();
2241 } else {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00002242 node->continue_target()->SetExpectedHeight();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002243 loop.Bind();
2244 }
2245
2246 // If the test is always true, there is no need to compile it.
2247 if (info == DONT_KNOW) {
2248 JumpTarget body;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002249 LoadCondition(node->cond(), &body, node->break_target(), true);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002250 if (has_valid_frame()) {
2251 Branch(false, node->break_target());
2252 }
2253 if (has_valid_frame() || body.is_linked()) {
2254 body.Bind();
2255 }
2256 }
2257
2258 if (has_valid_frame()) {
2259 CheckStack(); // TODO(1222600): ignore if body contains calls.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002260 Visit(node->body());
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002261
2262 if (node->next() == NULL) {
2263 // If there is no update statement and control flow can fall out
2264 // of the loop, jump directly to the continue label.
2265 if (has_valid_frame()) {
2266 node->continue_target()->Jump();
2267 }
2268 } else {
2269 // If there is an update statement and control flow can reach it
2270 // via falling out of the body of the loop or continuing, we
2271 // compile the update statement.
2272 if (node->continue_target()->is_linked()) {
2273 node->continue_target()->Bind();
2274 }
2275 if (has_valid_frame()) {
2276 // Record source position of the statement as this code which is
2277 // after the code for the body actually belongs to the loop
2278 // statement and not the body.
2279 CodeForStatementPosition(node);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002280 Visit(node->next());
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002281 loop.Jump();
2282 }
2283 }
2284 }
2285 if (node->break_target()->is_linked()) {
2286 node->break_target()->Bind();
2287 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00002288 DecrementLoopNesting();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002289 ASSERT(!has_valid_frame() || frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002290}
2291
2292
ager@chromium.org7c537e22008-10-16 08:43:32 +00002293void CodeGenerator::VisitForInStatement(ForInStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002294#ifdef DEBUG
2295 int original_height = frame_->height();
2296#endif
ager@chromium.org357bf652010-04-12 11:30:10 +00002297 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002298 Comment cmnt(masm_, "[ ForInStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002299 CodeForStatementPosition(node);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002300
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002301 JumpTarget primitive;
2302 JumpTarget jsobject;
2303 JumpTarget fixed_array;
2304 JumpTarget entry(JumpTarget::BIDIRECTIONAL);
2305 JumpTarget end_del_check;
2306 JumpTarget exit;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002307
2308 // Get the object to enumerate over (converted to JSObject).
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002309 Load(node->enumerable());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002310
2311 // Both SpiderMonkey and kjs ignore null and undefined in contrast
2312 // to the specification. 12.6.4 mandates a call to ToObject.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002313 frame_->EmitPop(r0);
ager@chromium.orgab99eea2009-08-25 07:05:41 +00002314 __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
2315 __ cmp(r0, ip);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002316 exit.Branch(eq);
ager@chromium.orgab99eea2009-08-25 07:05:41 +00002317 __ LoadRoot(ip, Heap::kNullValueRootIndex);
2318 __ cmp(r0, ip);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002319 exit.Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002320
2321 // Stack layout in body:
2322 // [iteration counter (Smi)]
2323 // [length of array]
2324 // [FixedArray]
2325 // [Map or 0]
2326 // [Object]
2327
2328 // Check if enumerable is already a JSObject
2329 __ tst(r0, Operand(kSmiTagMask));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002330 primitive.Branch(eq);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002331 __ CompareObjectType(r0, r1, r1, FIRST_JS_OBJECT_TYPE);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002332 jsobject.Branch(hs);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002333
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002334 primitive.Bind();
2335 frame_->EmitPush(r0);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00002336 frame_->InvokeBuiltin(Builtins::TO_OBJECT, CALL_JS, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002337
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002338 jsobject.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002339 // Get the set of properties (as a FixedArray or Map).
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00002340 // r0: value to be iterated over
2341 frame_->EmitPush(r0); // Push the object being iterated over.
2342
2343 // Check cache validity in generated code. This is a fast case for
2344 // the JSObject::IsSimpleEnum cache validity checks. If we cannot
2345 // guarantee cache validity, call the runtime system to check cache
2346 // validity or get the property names in a fixed array.
2347 JumpTarget call_runtime;
2348 JumpTarget loop(JumpTarget::BIDIRECTIONAL);
2349 JumpTarget check_prototype;
2350 JumpTarget use_cache;
2351 __ mov(r1, Operand(r0));
2352 loop.Bind();
2353 // Check that there are no elements.
2354 __ ldr(r2, FieldMemOperand(r1, JSObject::kElementsOffset));
2355 __ LoadRoot(r4, Heap::kEmptyFixedArrayRootIndex);
2356 __ cmp(r2, r4);
2357 call_runtime.Branch(ne);
2358 // Check that instance descriptors are not empty so that we can
2359 // check for an enum cache. Leave the map in r3 for the subsequent
2360 // prototype load.
2361 __ ldr(r3, FieldMemOperand(r1, HeapObject::kMapOffset));
2362 __ ldr(r2, FieldMemOperand(r3, Map::kInstanceDescriptorsOffset));
2363 __ LoadRoot(ip, Heap::kEmptyDescriptorArrayRootIndex);
2364 __ cmp(r2, ip);
2365 call_runtime.Branch(eq);
2366 // Check that there in an enum cache in the non-empty instance
2367 // descriptors. This is the case if the next enumeration index
2368 // field does not contain a smi.
2369 __ ldr(r2, FieldMemOperand(r2, DescriptorArray::kEnumerationIndexOffset));
2370 __ tst(r2, Operand(kSmiTagMask));
2371 call_runtime.Branch(eq);
2372 // For all objects but the receiver, check that the cache is empty.
2373 // r4: empty fixed array root.
2374 __ cmp(r1, r0);
2375 check_prototype.Branch(eq);
2376 __ ldr(r2, FieldMemOperand(r2, DescriptorArray::kEnumCacheBridgeCacheOffset));
2377 __ cmp(r2, r4);
2378 call_runtime.Branch(ne);
2379 check_prototype.Bind();
2380 // Load the prototype from the map and loop if non-null.
2381 __ ldr(r1, FieldMemOperand(r3, Map::kPrototypeOffset));
2382 __ LoadRoot(ip, Heap::kNullValueRootIndex);
2383 __ cmp(r1, ip);
2384 loop.Branch(ne);
2385 // The enum cache is valid. Load the map of the object being
2386 // iterated over and use the cache for the iteration.
2387 __ ldr(r0, FieldMemOperand(r0, HeapObject::kMapOffset));
2388 use_cache.Jump();
2389
2390 call_runtime.Bind();
2391 // Call the runtime to get the property names for the object.
2392 frame_->EmitPush(r0); // push the object (slot 4) for the runtime call
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002393 frame_->CallRuntime(Runtime::kGetPropertyNamesFast, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002394
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00002395 // If we got a map from the runtime call, we can do a fast
2396 // modification check. Otherwise, we got a fixed array, and we have
2397 // to do a slow check.
2398 // r0: map or fixed array (result from call to
2399 // Runtime::kGetPropertyNamesFast)
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002400 __ mov(r2, Operand(r0));
2401 __ ldr(r1, FieldMemOperand(r2, HeapObject::kMapOffset));
ager@chromium.orgab99eea2009-08-25 07:05:41 +00002402 __ LoadRoot(ip, Heap::kMetaMapRootIndex);
2403 __ cmp(r1, ip);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002404 fixed_array.Branch(ne);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002405
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00002406 use_cache.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002407 // Get enum cache
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00002408 // r0: map (either the result from a call to
2409 // Runtime::kGetPropertyNamesFast or has been fetched directly from
2410 // the object)
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002411 __ mov(r1, Operand(r0));
2412 __ ldr(r1, FieldMemOperand(r1, Map::kInstanceDescriptorsOffset));
2413 __ ldr(r1, FieldMemOperand(r1, DescriptorArray::kEnumerationIndexOffset));
2414 __ ldr(r2,
2415 FieldMemOperand(r1, DescriptorArray::kEnumCacheBridgeCacheOffset));
2416
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002417 frame_->EmitPush(r0); // map
2418 frame_->EmitPush(r2); // enum cache bridge cache
mads.s.ager31e71382008-08-13 09:32:07 +00002419 __ ldr(r0, FieldMemOperand(r2, FixedArray::kLengthOffset));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002420 frame_->EmitPush(r0);
mads.s.ager31e71382008-08-13 09:32:07 +00002421 __ mov(r0, Operand(Smi::FromInt(0)));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002422 frame_->EmitPush(r0);
2423 entry.Jump();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002424
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002425 fixed_array.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002426 __ mov(r1, Operand(Smi::FromInt(0)));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002427 frame_->EmitPush(r1); // insert 0 in place of Map
2428 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002429
2430 // Push the length of the array and the initial index onto the stack.
mads.s.ager31e71382008-08-13 09:32:07 +00002431 __ ldr(r0, FieldMemOperand(r0, FixedArray::kLengthOffset));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002432 frame_->EmitPush(r0);
mads.s.ager31e71382008-08-13 09:32:07 +00002433 __ mov(r0, Operand(Smi::FromInt(0))); // init index
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002434 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002435
2436 // Condition.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002437 entry.Bind();
mads.s.ager31e71382008-08-13 09:32:07 +00002438 // sp[0] : index
2439 // sp[1] : array/enum cache length
2440 // sp[2] : array or enum cache
2441 // sp[3] : 0 or map
2442 // sp[4] : enumerable
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002443 // Grab the current frame's height for the break and continue
2444 // targets only after all the state is pushed on the frame.
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00002445 node->break_target()->SetExpectedHeight();
2446 node->continue_target()->SetExpectedHeight();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002447
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00002448 // Load the current count to r0, load the length to r1.
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00002449 __ Ldrd(r0, r1, frame_->ElementAt(0));
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00002450 __ cmp(r0, r1); // compare to the array length
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002451 node->break_target()->Branch(hs);
2452
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002453 // Get the i'th entry of the array.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002454 __ ldr(r2, frame_->ElementAt(2));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002455 __ add(r2, r2, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
2456 __ ldr(r3, MemOperand(r2, r0, LSL, kPointerSizeLog2 - kSmiTagSize));
2457
2458 // Get Map or 0.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002459 __ ldr(r2, frame_->ElementAt(3));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002460 // Check if this (still) matches the map of the enumerable.
2461 // If not, we have to filter the key.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002462 __ ldr(r1, frame_->ElementAt(4));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002463 __ ldr(r1, FieldMemOperand(r1, HeapObject::kMapOffset));
2464 __ cmp(r1, Operand(r2));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002465 end_del_check.Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002466
2467 // Convert the entry to a string (or null if it isn't a property anymore).
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002468 __ ldr(r0, frame_->ElementAt(4)); // push enumerable
2469 frame_->EmitPush(r0);
2470 frame_->EmitPush(r3); // push entry
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00002471 frame_->InvokeBuiltin(Builtins::FILTER_KEY, CALL_JS, 2);
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00002472 __ mov(r3, Operand(r0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002473
2474 // If the property has been removed while iterating, we just skip it.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00002475 __ LoadRoot(ip, Heap::kNullValueRootIndex);
2476 __ cmp(r3, ip);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002477 node->continue_target()->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002478
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002479 end_del_check.Bind();
2480 // Store the entry in the 'each' expression and take another spin in the
2481 // loop. r3: i'th entry of the enum cache (or string there of)
2482 frame_->EmitPush(r3); // push entry
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002483 { Reference each(this, node->each());
2484 if (!each.is_illegal()) {
mads.s.ager31e71382008-08-13 09:32:07 +00002485 if (each.size() > 0) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002486 __ ldr(r0, frame_->ElementAt(each.size()));
2487 frame_->EmitPush(r0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002488 each.SetValue(NOT_CONST_INIT);
2489 frame_->Drop(2);
2490 } else {
2491 // If the reference was to a slot we rely on the convenient property
2492 // that it doesn't matter whether a value (eg, r3 pushed above) is
2493 // right on top of or right underneath a zero-sized reference.
2494 each.SetValue(NOT_CONST_INIT);
2495 frame_->Drop();
mads.s.ager31e71382008-08-13 09:32:07 +00002496 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002497 }
2498 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002499 // Body.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002500 CheckStack(); // TODO(1222600): ignore if body contains calls.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002501 Visit(node->body());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002502
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002503 // Next. Reestablish a spilled frame in case we are coming here via
2504 // a continue in the body.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002505 node->continue_target()->Bind();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002506 frame_->SpillAll();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002507 frame_->EmitPop(r0);
2508 __ add(r0, r0, Operand(Smi::FromInt(1)));
2509 frame_->EmitPush(r0);
2510 entry.Jump();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002511
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002512 // Cleanup. No need to spill because VirtualFrame::Drop is safe for
2513 // any frame.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002514 node->break_target()->Bind();
ager@chromium.org3bf7b912008-11-17 09:09:45 +00002515 frame_->Drop(5);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002516
2517 // Exit.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002518 exit.Bind();
2519 node->continue_target()->Unuse();
2520 node->break_target()->Unuse();
2521 ASSERT(frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002522}
2523
2524
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002525void CodeGenerator::VisitTryCatchStatement(TryCatchStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002526#ifdef DEBUG
2527 int original_height = frame_->height();
2528#endif
ager@chromium.org357bf652010-04-12 11:30:10 +00002529 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002530 Comment cmnt(masm_, "[ TryCatchStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002531 CodeForStatementPosition(node);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002532
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002533 JumpTarget try_block;
2534 JumpTarget exit;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002535
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002536 try_block.Call();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002537 // --- Catch block ---
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002538 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002539
2540 // Store the caught exception in the catch variable.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002541 Variable* catch_var = node->catch_var()->var();
2542 ASSERT(catch_var != NULL && catch_var->slot() != NULL);
2543 StoreToSlot(catch_var->slot(), NOT_CONST_INIT);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002544
2545 // Remove the exception from the stack.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002546 frame_->Drop();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002547
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002548 VisitStatements(node->catch_block()->statements());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002549 if (frame_ != NULL) {
2550 exit.Jump();
2551 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002552
2553
2554 // --- Try block ---
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002555 try_block.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002556
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002557 frame_->PushTryHandler(TRY_CATCH_HANDLER);
2558 int handler_height = frame_->height();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002559
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002560 // Shadow the labels for all escapes from the try block, including
2561 // returns. During shadowing, the original label is hidden as the
2562 // LabelShadow and operations on the original actually affect the
2563 // shadowing label.
2564 //
2565 // We should probably try to unify the escaping labels and the return
2566 // label.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002567 int nof_escapes = node->escaping_targets()->length();
2568 List<ShadowTarget*> shadows(1 + nof_escapes);
2569
2570 // Add the shadow target for the function return.
2571 static const int kReturnShadowIndex = 0;
2572 shadows.Add(new ShadowTarget(&function_return_));
2573 bool function_return_was_shadowed = function_return_is_shadowed_;
2574 function_return_is_shadowed_ = true;
2575 ASSERT(shadows[kReturnShadowIndex]->other_target() == &function_return_);
2576
2577 // Add the remaining shadow targets.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002578 for (int i = 0; i < nof_escapes; i++) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002579 shadows.Add(new ShadowTarget(node->escaping_targets()->at(i)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002580 }
2581
2582 // Generate code for the statements in the try block.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002583 VisitStatements(node->try_block()->statements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002584
2585 // Stop the introduced shadowing and count the number of required unlinks.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002586 // After shadowing stops, the original labels are unshadowed and the
2587 // LabelShadows represent the formerly shadowing labels.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002588 bool has_unlinks = false;
2589 for (int i = 0; i < shadows.length(); i++) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002590 shadows[i]->StopShadowing();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002591 has_unlinks = has_unlinks || shadows[i]->is_linked();
2592 }
2593 function_return_is_shadowed_ = function_return_was_shadowed;
2594
2595 // Get an external reference to the handler address.
2596 ExternalReference handler_address(Top::k_handler_address);
2597
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002598 // If we can fall off the end of the try block, unlink from try chain.
2599 if (has_valid_frame()) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002600 // The next handler address is on top of the frame. Unlink from
2601 // the handler list and drop the rest of this handler from the
2602 // frame.
2603 ASSERT(StackHandlerConstants::kNextOffset == 0);
2604 frame_->EmitPop(r1);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002605 __ mov(r3, Operand(handler_address));
2606 __ str(r1, MemOperand(r3));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002607 frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002608 if (has_unlinks) {
2609 exit.Jump();
2610 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002611 }
2612
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002613 // Generate unlink code for the (formerly) shadowing labels that have been
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002614 // jumped to. Deallocate each shadow target.
2615 for (int i = 0; i < shadows.length(); i++) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002616 if (shadows[i]->is_linked()) {
mads.s.ager31e71382008-08-13 09:32:07 +00002617 // Unlink from try chain;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002618 shadows[i]->Bind();
2619 // Because we can be jumping here (to spilled code) from unspilled
2620 // code, we need to reestablish a spilled frame at this block.
2621 frame_->SpillAll();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002622
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002623 // Reload sp from the top handler, because some statements that we
2624 // break from (eg, for...in) may have left stuff on the stack.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002625 __ mov(r3, Operand(handler_address));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002626 __ ldr(sp, MemOperand(r3));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002627 frame_->Forget(frame_->height() - handler_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002628
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002629 ASSERT(StackHandlerConstants::kNextOffset == 0);
2630 frame_->EmitPop(r1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002631 __ str(r1, MemOperand(r3));
ager@chromium.org3bf7b912008-11-17 09:09:45 +00002632 frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002633
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002634 if (!function_return_is_shadowed_ && i == kReturnShadowIndex) {
2635 frame_->PrepareForReturn();
2636 }
2637 shadows[i]->other_target()->Jump();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002638 }
2639 }
2640
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002641 exit.Bind();
2642 ASSERT(!has_valid_frame() || frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002643}
2644
2645
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002646void CodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002647#ifdef DEBUG
2648 int original_height = frame_->height();
2649#endif
ager@chromium.org357bf652010-04-12 11:30:10 +00002650 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002651 Comment cmnt(masm_, "[ TryFinallyStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002652 CodeForStatementPosition(node);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002653
2654 // State: Used to keep track of reason for entering the finally
2655 // block. Should probably be extended to hold information for
2656 // break/continue from within the try block.
2657 enum { FALLING, THROWING, JUMPING };
2658
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002659 JumpTarget try_block;
2660 JumpTarget finally_block;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002661
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002662 try_block.Call();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002663
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002664 frame_->EmitPush(r0); // save exception object on the stack
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002665 // In case of thrown exceptions, this is where we continue.
2666 __ mov(r2, Operand(Smi::FromInt(THROWING)));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002667 finally_block.Jump();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002668
2669 // --- Try block ---
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002670 try_block.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002671
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002672 frame_->PushTryHandler(TRY_FINALLY_HANDLER);
2673 int handler_height = frame_->height();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002674
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002675 // Shadow the labels for all escapes from the try block, including
2676 // returns. Shadowing hides the original label as the LabelShadow and
2677 // operations on the original actually affect the shadowing label.
2678 //
2679 // We should probably try to unify the escaping labels and the return
2680 // label.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002681 int nof_escapes = node->escaping_targets()->length();
2682 List<ShadowTarget*> shadows(1 + nof_escapes);
2683
2684 // Add the shadow target for the function return.
2685 static const int kReturnShadowIndex = 0;
2686 shadows.Add(new ShadowTarget(&function_return_));
2687 bool function_return_was_shadowed = function_return_is_shadowed_;
2688 function_return_is_shadowed_ = true;
2689 ASSERT(shadows[kReturnShadowIndex]->other_target() == &function_return_);
2690
2691 // Add the remaining shadow targets.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002692 for (int i = 0; i < nof_escapes; i++) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002693 shadows.Add(new ShadowTarget(node->escaping_targets()->at(i)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002694 }
2695
2696 // Generate code for the statements in the try block.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002697 VisitStatements(node->try_block()->statements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002698
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002699 // Stop the introduced shadowing and count the number of required unlinks.
2700 // After shadowing stops, the original labels are unshadowed and the
2701 // LabelShadows represent the formerly shadowing labels.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002702 int nof_unlinks = 0;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002703 for (int i = 0; i < shadows.length(); i++) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002704 shadows[i]->StopShadowing();
2705 if (shadows[i]->is_linked()) nof_unlinks++;
2706 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002707 function_return_is_shadowed_ = function_return_was_shadowed;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002708
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002709 // Get an external reference to the handler address.
2710 ExternalReference handler_address(Top::k_handler_address);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002711
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002712 // If we can fall off the end of the try block, unlink from the try
2713 // chain and set the state on the frame to FALLING.
2714 if (has_valid_frame()) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002715 // The next handler address is on top of the frame.
2716 ASSERT(StackHandlerConstants::kNextOffset == 0);
2717 frame_->EmitPop(r1);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002718 __ mov(r3, Operand(handler_address));
2719 __ str(r1, MemOperand(r3));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002720 frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002721
2722 // Fake a top of stack value (unneeded when FALLING) and set the
2723 // state in r2, then jump around the unlink blocks if any.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00002724 __ LoadRoot(r0, Heap::kUndefinedValueRootIndex);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002725 frame_->EmitPush(r0);
2726 __ mov(r2, Operand(Smi::FromInt(FALLING)));
2727 if (nof_unlinks > 0) {
2728 finally_block.Jump();
2729 }
2730 }
2731
2732 // Generate code to unlink and set the state for the (formerly)
2733 // shadowing targets that have been jumped to.
2734 for (int i = 0; i < shadows.length(); i++) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002735 if (shadows[i]->is_linked()) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002736 // If we have come from the shadowed return, the return value is
2737 // in (a non-refcounted reference to) r0. We must preserve it
2738 // until it is pushed.
2739 //
2740 // Because we can be jumping here (to spilled code) from
2741 // unspilled code, we need to reestablish a spilled frame at
2742 // this block.
2743 shadows[i]->Bind();
2744 frame_->SpillAll();
2745
2746 // Reload sp from the top handler, because some statements that
2747 // we break from (eg, for...in) may have left stuff on the
2748 // stack.
2749 __ mov(r3, Operand(handler_address));
2750 __ ldr(sp, MemOperand(r3));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002751 frame_->Forget(frame_->height() - handler_height);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002752
2753 // Unlink this handler and drop it from the frame. The next
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002754 // handler address is currently on top of the frame.
2755 ASSERT(StackHandlerConstants::kNextOffset == 0);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002756 frame_->EmitPop(r1);
2757 __ str(r1, MemOperand(r3));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002758 frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002759
2760 if (i == kReturnShadowIndex) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002761 // If this label shadowed the function return, materialize the
2762 // return value on the stack.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002763 frame_->EmitPush(r0);
mads.s.ager31e71382008-08-13 09:32:07 +00002764 } else {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002765 // Fake TOS for targets that shadowed breaks and continues.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00002766 __ LoadRoot(r0, Heap::kUndefinedValueRootIndex);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002767 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002768 }
2769 __ mov(r2, Operand(Smi::FromInt(JUMPING + i)));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002770 if (--nof_unlinks > 0) {
2771 // If this is not the last unlink block, jump around the next.
2772 finally_block.Jump();
2773 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002774 }
2775 }
2776
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002777 // --- Finally block ---
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002778 finally_block.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002779
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002780 // Push the state on the stack.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002781 frame_->EmitPush(r2);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002782
2783 // We keep two elements on the stack - the (possibly faked) result
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002784 // and the state - while evaluating the finally block.
2785 //
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002786 // Generate code for the statements in the finally block.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002787 VisitStatements(node->finally_block()->statements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002788
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002789 if (has_valid_frame()) {
2790 // Restore state and return value or faked TOS.
2791 frame_->EmitPop(r2);
2792 frame_->EmitPop(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002793 }
2794
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002795 // Generate code to jump to the right destination for all used
2796 // formerly shadowing targets. Deallocate each shadow target.
2797 for (int i = 0; i < shadows.length(); i++) {
2798 if (has_valid_frame() && shadows[i]->is_bound()) {
2799 JumpTarget* original = shadows[i]->other_target();
2800 __ cmp(r2, Operand(Smi::FromInt(JUMPING + i)));
2801 if (!function_return_is_shadowed_ && i == kReturnShadowIndex) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002802 JumpTarget skip;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002803 skip.Branch(ne);
2804 frame_->PrepareForReturn();
2805 original->Jump();
2806 skip.Bind();
2807 } else {
2808 original->Branch(eq);
2809 }
2810 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002811 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002812
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002813 if (has_valid_frame()) {
2814 // Check if we need to rethrow the exception.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002815 JumpTarget exit;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002816 __ cmp(r2, Operand(Smi::FromInt(THROWING)));
2817 exit.Branch(ne);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002818
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002819 // Rethrow exception.
2820 frame_->EmitPush(r0);
2821 frame_->CallRuntime(Runtime::kReThrow, 1);
2822
2823 // Done.
2824 exit.Bind();
2825 }
2826 ASSERT(!has_valid_frame() || frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002827}
2828
2829
ager@chromium.org7c537e22008-10-16 08:43:32 +00002830void CodeGenerator::VisitDebuggerStatement(DebuggerStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002831#ifdef DEBUG
2832 int original_height = frame_->height();
2833#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002834 Comment cmnt(masm_, "[ DebuggerStatament");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002835 CodeForStatementPosition(node);
ager@chromium.org65dad4b2009-04-23 08:48:43 +00002836#ifdef ENABLE_DEBUGGER_SUPPORT
ager@chromium.org5c838252010-02-19 08:53:10 +00002837 frame_->DebugBreak();
ager@chromium.org65dad4b2009-04-23 08:48:43 +00002838#endif
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00002839 // Ignore the return value.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002840 ASSERT(frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002841}
2842
2843
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00002844void CodeGenerator::InstantiateFunction(
2845 Handle<SharedFunctionInfo> function_info) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002846 // Use the fast case closure allocation code that allocates in new
2847 // space for nested functions that don't need literals cloning.
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00002848 if (scope()->is_function_scope() && function_info->num_literals() == 0) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002849 FastNewClosureStub stub;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002850 frame_->EmitPush(Operand(function_info));
2851 frame_->SpillAll();
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002852 frame_->CallStub(&stub, 1);
2853 frame_->EmitPush(r0);
2854 } else {
2855 // Create a new closure.
2856 frame_->EmitPush(cp);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002857 frame_->EmitPush(Operand(function_info));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002858 frame_->CallRuntime(Runtime::kNewClosure, 2);
2859 frame_->EmitPush(r0);
2860 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002861}
2862
2863
ager@chromium.org7c537e22008-10-16 08:43:32 +00002864void CodeGenerator::VisitFunctionLiteral(FunctionLiteral* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002865#ifdef DEBUG
2866 int original_height = frame_->height();
2867#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002868 Comment cmnt(masm_, "[ FunctionLiteral");
2869
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00002870 // Build the function info and instantiate it.
2871 Handle<SharedFunctionInfo> function_info =
2872 Compiler::BuildFunctionInfo(node, script(), this);
kasper.lund212ac232008-07-16 07:07:30 +00002873 // Check for stack-overflow exception.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002874 if (HasStackOverflow()) {
2875 ASSERT(frame_->height() == original_height);
2876 return;
2877 }
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00002878 InstantiateFunction(function_info);
ager@chromium.orgac091b72010-05-05 07:34:42 +00002879 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002880}
2881
2882
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00002883void CodeGenerator::VisitSharedFunctionInfoLiteral(
2884 SharedFunctionInfoLiteral* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002885#ifdef DEBUG
2886 int original_height = frame_->height();
2887#endif
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00002888 Comment cmnt(masm_, "[ SharedFunctionInfoLiteral");
2889 InstantiateFunction(node->shared_function_info());
ager@chromium.orgac091b72010-05-05 07:34:42 +00002890 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002891}
2892
2893
ager@chromium.org7c537e22008-10-16 08:43:32 +00002894void CodeGenerator::VisitConditional(Conditional* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002895#ifdef DEBUG
2896 int original_height = frame_->height();
2897#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002898 Comment cmnt(masm_, "[ Conditional");
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002899 JumpTarget then;
2900 JumpTarget else_;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002901 LoadCondition(node->condition(), &then, &else_, true);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00002902 if (has_valid_frame()) {
2903 Branch(false, &else_);
2904 }
2905 if (has_valid_frame() || then.is_linked()) {
2906 then.Bind();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002907 Load(node->then_expression());
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00002908 }
2909 if (else_.is_linked()) {
2910 JumpTarget exit;
2911 if (has_valid_frame()) exit.Jump();
2912 else_.Bind();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002913 Load(node->else_expression());
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00002914 if (exit.is_linked()) exit.Bind();
2915 }
ager@chromium.orgac091b72010-05-05 07:34:42 +00002916 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002917}
2918
2919
ager@chromium.org7c537e22008-10-16 08:43:32 +00002920void CodeGenerator::LoadFromSlot(Slot* slot, TypeofState typeof_state) {
2921 if (slot->type() == Slot::LOOKUP) {
ager@chromium.org381abbb2009-02-25 13:23:22 +00002922 ASSERT(slot->var()->is_dynamic());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002923
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00002924 // JumpTargets do not yet support merging frames so the frame must be
2925 // spilled when jumping to these targets.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002926 JumpTarget slow;
2927 JumpTarget done;
ager@chromium.org381abbb2009-02-25 13:23:22 +00002928
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00002929 // Generate fast case for loading from slots that correspond to
2930 // local/global variables or arguments unless they are shadowed by
2931 // eval-introduced bindings.
2932 EmitDynamicLoadFromSlotFastCase(slot,
2933 typeof_state,
2934 &slow,
2935 &done);
ager@chromium.org381abbb2009-02-25 13:23:22 +00002936
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002937 slow.Bind();
2938 frame_->EmitPush(cp);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00002939 frame_->EmitPush(Operand(slot->var()->name()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002940
ager@chromium.org7c537e22008-10-16 08:43:32 +00002941 if (typeof_state == INSIDE_TYPEOF) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002942 frame_->CallRuntime(Runtime::kLoadContextSlotNoReferenceError, 2);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002943 } else {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002944 frame_->CallRuntime(Runtime::kLoadContextSlot, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002945 }
ager@chromium.org381abbb2009-02-25 13:23:22 +00002946
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002947 done.Bind();
2948 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002949
2950 } else {
ager@chromium.org357bf652010-04-12 11:30:10 +00002951 Register scratch = VirtualFrame::scratch0();
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00002952 TypeInfo info = type_info(slot);
2953 frame_->EmitPush(SlotOperand(slot, scratch), info);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00002954
ager@chromium.org7c537e22008-10-16 08:43:32 +00002955 if (slot->var()->mode() == Variable::CONST) {
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00002956 // Const slots may contain 'the hole' value (the constant hasn't been
2957 // initialized yet) which needs to be converted into the 'undefined'
2958 // value.
2959 Comment cmnt(masm_, "[ Unhole const");
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00002960 Register tos = frame_->PopToRegister();
ager@chromium.orgab99eea2009-08-25 07:05:41 +00002961 __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00002962 __ cmp(tos, ip);
2963 __ LoadRoot(tos, Heap::kUndefinedValueRootIndex, eq);
2964 frame_->EmitPush(tos);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002965 }
2966 }
2967}
2968
2969
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00002970void CodeGenerator::LoadFromSlotCheckForArguments(Slot* slot,
2971 TypeofState state) {
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00002972 VirtualFrame::RegisterAllocationScope scope(this);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00002973 LoadFromSlot(slot, state);
2974
2975 // Bail out quickly if we're not using lazy arguments allocation.
2976 if (ArgumentsMode() != LAZY_ARGUMENTS_ALLOCATION) return;
2977
2978 // ... or if the slot isn't a non-parameter arguments slot.
2979 if (slot->type() == Slot::PARAMETER || !slot->is_arguments()) return;
2980
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00002981 // Load the loaded value from the stack into a register but leave it on the
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00002982 // stack.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00002983 Register tos = frame_->Peek();
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00002984
2985 // If the loaded value is the sentinel that indicates that we
2986 // haven't loaded the arguments object yet, we need to do it now.
2987 JumpTarget exit;
2988 __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00002989 __ cmp(tos, ip);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00002990 exit.Branch(ne);
2991 frame_->Drop();
2992 StoreArgumentsObject(false);
2993 exit.Bind();
2994}
2995
2996
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002997void CodeGenerator::StoreToSlot(Slot* slot, InitState init_state) {
2998 ASSERT(slot != NULL);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00002999 VirtualFrame::RegisterAllocationScope scope(this);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003000 if (slot->type() == Slot::LOOKUP) {
3001 ASSERT(slot->var()->is_dynamic());
3002
3003 // For now, just do a runtime call.
3004 frame_->EmitPush(cp);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003005 frame_->EmitPush(Operand(slot->var()->name()));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003006
3007 if (init_state == CONST_INIT) {
3008 // Same as the case for a normal store, but ignores attribute
3009 // (e.g. READ_ONLY) of context slot so that we can initialize
3010 // const properties (introduced via eval("const foo = (some
3011 // expr);")). Also, uses the current function context instead of
3012 // the top context.
3013 //
3014 // Note that we must declare the foo upon entry of eval(), via a
3015 // context slot declaration, but we cannot initialize it at the
3016 // same time, because the const declaration may be at the end of
3017 // the eval code (sigh...) and the const variable may have been
3018 // used before (where its value is 'undefined'). Thus, we can only
3019 // do the initialization when we actually encounter the expression
3020 // and when the expression operands are defined and valid, and
3021 // thus we need the split into 2 operations: declaration of the
3022 // context slot followed by initialization.
3023 frame_->CallRuntime(Runtime::kInitializeConstContextSlot, 3);
3024 } else {
3025 frame_->CallRuntime(Runtime::kStoreContextSlot, 3);
3026 }
3027 // Storing a variable must keep the (new) value on the expression
3028 // stack. This is necessary for compiling assignment expressions.
3029 frame_->EmitPush(r0);
3030
3031 } else {
3032 ASSERT(!slot->var()->is_dynamic());
ager@chromium.org357bf652010-04-12 11:30:10 +00003033 Register scratch = VirtualFrame::scratch0();
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003034 Register scratch2 = VirtualFrame::scratch1();
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003035
ager@chromium.org357bf652010-04-12 11:30:10 +00003036 // The frame must be spilled when branching to this target.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003037 JumpTarget exit;
ager@chromium.org357bf652010-04-12 11:30:10 +00003038
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003039 if (init_state == CONST_INIT) {
3040 ASSERT(slot->var()->mode() == Variable::CONST);
3041 // Only the first const initialization must be executed (the slot
3042 // still contains 'the hole' value). When the assignment is
3043 // executed, the code is identical to a normal store (see below).
3044 Comment cmnt(masm_, "[ Init const");
ager@chromium.org357bf652010-04-12 11:30:10 +00003045 __ ldr(scratch, SlotOperand(slot, scratch));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003046 __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
ager@chromium.org357bf652010-04-12 11:30:10 +00003047 __ cmp(scratch, ip);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003048 exit.Branch(ne);
3049 }
3050
3051 // We must execute the store. Storing a variable must keep the
3052 // (new) value on the stack. This is necessary for compiling
3053 // assignment expressions.
3054 //
3055 // Note: We will reach here even with slot->var()->mode() ==
3056 // Variable::CONST because of const declarations which will
3057 // initialize consts to 'the hole' value and by doing so, end up
3058 // calling this code. r2 may be loaded with context; used below in
3059 // RecordWrite.
ager@chromium.org357bf652010-04-12 11:30:10 +00003060 Register tos = frame_->Peek();
3061 __ str(tos, SlotOperand(slot, scratch));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003062 if (slot->type() == Slot::CONTEXT) {
3063 // Skip write barrier if the written value is a smi.
ager@chromium.org357bf652010-04-12 11:30:10 +00003064 __ tst(tos, Operand(kSmiTagMask));
3065 // We don't use tos any more after here.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003066 exit.Branch(eq);
ager@chromium.org357bf652010-04-12 11:30:10 +00003067 // scratch is loaded with context when calling SlotOperand above.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003068 int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize;
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003069 // We need an extra register. Until we have a way to do that in the
3070 // virtual frame we will cheat and ask for a free TOS register.
3071 Register scratch3 = frame_->GetTOSRegister();
3072 __ RecordWrite(scratch, Operand(offset), scratch2, scratch3);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003073 }
3074 // If we definitely did not jump over the assignment, we do not need
3075 // to bind the exit label. Doing so can defeat peephole
3076 // optimization.
3077 if (init_state == CONST_INIT || slot->type() == Slot::CONTEXT) {
3078 exit.Bind();
3079 }
3080 }
3081}
3082
3083
ager@chromium.org381abbb2009-02-25 13:23:22 +00003084void CodeGenerator::LoadFromGlobalSlotCheckExtensions(Slot* slot,
3085 TypeofState typeof_state,
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003086 JumpTarget* slow) {
ager@chromium.org381abbb2009-02-25 13:23:22 +00003087 // Check that no extension objects have been created by calls to
3088 // eval from the current scope to the global scope.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003089 Register tmp = frame_->scratch0();
3090 Register tmp2 = frame_->scratch1();
ager@chromium.org381abbb2009-02-25 13:23:22 +00003091 Register context = cp;
3092 Scope* s = scope();
3093 while (s != NULL) {
3094 if (s->num_heap_slots() > 0) {
3095 if (s->calls_eval()) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003096 frame_->SpillAll();
ager@chromium.org381abbb2009-02-25 13:23:22 +00003097 // Check that extension is NULL.
3098 __ ldr(tmp2, ContextOperand(context, Context::EXTENSION_INDEX));
3099 __ tst(tmp2, tmp2);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003100 slow->Branch(ne);
ager@chromium.org381abbb2009-02-25 13:23:22 +00003101 }
3102 // Load next context in chain.
3103 __ ldr(tmp, ContextOperand(context, Context::CLOSURE_INDEX));
3104 __ ldr(tmp, FieldMemOperand(tmp, JSFunction::kContextOffset));
3105 context = tmp;
3106 }
3107 // If no outer scope calls eval, we do not need to check more
3108 // context extensions.
3109 if (!s->outer_scope_calls_eval() || s->is_eval_scope()) break;
3110 s = s->outer_scope();
3111 }
3112
3113 if (s->is_eval_scope()) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003114 frame_->SpillAll();
ager@chromium.org381abbb2009-02-25 13:23:22 +00003115 Label next, fast;
ager@chromium.org357bf652010-04-12 11:30:10 +00003116 __ Move(tmp, context);
ager@chromium.org381abbb2009-02-25 13:23:22 +00003117 __ bind(&next);
3118 // Terminate at global context.
3119 __ ldr(tmp2, FieldMemOperand(tmp, HeapObject::kMapOffset));
ager@chromium.orgab99eea2009-08-25 07:05:41 +00003120 __ LoadRoot(ip, Heap::kGlobalContextMapRootIndex);
3121 __ cmp(tmp2, ip);
ager@chromium.org381abbb2009-02-25 13:23:22 +00003122 __ b(eq, &fast);
3123 // Check that extension is NULL.
3124 __ ldr(tmp2, ContextOperand(tmp, Context::EXTENSION_INDEX));
3125 __ tst(tmp2, tmp2);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003126 slow->Branch(ne);
ager@chromium.org381abbb2009-02-25 13:23:22 +00003127 // Load next context in chain.
3128 __ ldr(tmp, ContextOperand(tmp, Context::CLOSURE_INDEX));
3129 __ ldr(tmp, FieldMemOperand(tmp, JSFunction::kContextOffset));
3130 __ b(&next);
3131 __ bind(&fast);
3132 }
3133
ager@chromium.org381abbb2009-02-25 13:23:22 +00003134 // Load the global object.
3135 LoadGlobal();
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00003136 // Setup the name register and call load IC.
ager@chromium.orgac091b72010-05-05 07:34:42 +00003137 frame_->CallLoadIC(slot->var()->name(),
3138 typeof_state == INSIDE_TYPEOF
3139 ? RelocInfo::CODE_TARGET
3140 : RelocInfo::CODE_TARGET_CONTEXT);
ager@chromium.org381abbb2009-02-25 13:23:22 +00003141}
3142
3143
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00003144void CodeGenerator::EmitDynamicLoadFromSlotFastCase(Slot* slot,
3145 TypeofState typeof_state,
3146 JumpTarget* slow,
3147 JumpTarget* done) {
3148 // Generate fast-case code for variables that might be shadowed by
3149 // eval-introduced variables. Eval is used a lot without
3150 // introducing variables. In those cases, we do not want to
3151 // perform a runtime call for all variables in the scope
3152 // containing the eval.
3153 if (slot->var()->mode() == Variable::DYNAMIC_GLOBAL) {
3154 LoadFromGlobalSlotCheckExtensions(slot, typeof_state, slow);
3155 frame_->SpillAll();
3156 done->Jump();
3157
3158 } else if (slot->var()->mode() == Variable::DYNAMIC_LOCAL) {
3159 frame_->SpillAll();
3160 Slot* potential_slot = slot->var()->local_if_not_shadowed()->slot();
3161 Expression* rewrite = slot->var()->local_if_not_shadowed()->rewrite();
3162 if (potential_slot != NULL) {
3163 // Generate fast case for locals that rewrite to slots.
3164 __ ldr(r0,
3165 ContextSlotOperandCheckExtensions(potential_slot,
3166 r1,
3167 r2,
3168 slow));
3169 if (potential_slot->var()->mode() == Variable::CONST) {
3170 __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
3171 __ cmp(r0, ip);
3172 __ LoadRoot(r0, Heap::kUndefinedValueRootIndex, eq);
3173 }
3174 done->Jump();
3175 } else if (rewrite != NULL) {
3176 // Generate fast case for argument loads.
3177 Property* property = rewrite->AsProperty();
3178 if (property != NULL) {
3179 VariableProxy* obj_proxy = property->obj()->AsVariableProxy();
3180 Literal* key_literal = property->key()->AsLiteral();
3181 if (obj_proxy != NULL &&
3182 key_literal != NULL &&
3183 obj_proxy->IsArguments() &&
3184 key_literal->handle()->IsSmi()) {
3185 // Load arguments object if there are no eval-introduced
3186 // variables. Then load the argument from the arguments
3187 // object using keyed load.
3188 __ ldr(r0,
3189 ContextSlotOperandCheckExtensions(obj_proxy->var()->slot(),
3190 r1,
3191 r2,
3192 slow));
3193 frame_->EmitPush(r0);
3194 __ mov(r1, Operand(key_literal->handle()));
3195 frame_->EmitPush(r1);
3196 EmitKeyedLoad();
3197 done->Jump();
3198 }
3199 }
3200 }
3201 }
3202}
3203
3204
ager@chromium.org7c537e22008-10-16 08:43:32 +00003205void CodeGenerator::VisitSlot(Slot* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003206#ifdef DEBUG
3207 int original_height = frame_->height();
3208#endif
ager@chromium.org7c537e22008-10-16 08:43:32 +00003209 Comment cmnt(masm_, "[ Slot");
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00003210 LoadFromSlotCheckForArguments(node, NOT_INSIDE_TYPEOF);
ager@chromium.orgac091b72010-05-05 07:34:42 +00003211 ASSERT_EQ(original_height + 1, frame_->height());
ager@chromium.org7c537e22008-10-16 08:43:32 +00003212}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003213
ager@chromium.org7c537e22008-10-16 08:43:32 +00003214
3215void CodeGenerator::VisitVariableProxy(VariableProxy* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003216#ifdef DEBUG
3217 int original_height = frame_->height();
3218#endif
ager@chromium.org7c537e22008-10-16 08:43:32 +00003219 Comment cmnt(masm_, "[ VariableProxy");
3220
3221 Variable* var = node->var();
3222 Expression* expr = var->rewrite();
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00003223 if (expr != NULL) {
3224 Visit(expr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003225 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +00003226 ASSERT(var->is_global());
3227 Reference ref(this, node);
ager@chromium.org357bf652010-04-12 11:30:10 +00003228 ref.GetValue();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003229 }
ager@chromium.orgac091b72010-05-05 07:34:42 +00003230 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003231}
3232
3233
ager@chromium.org7c537e22008-10-16 08:43:32 +00003234void CodeGenerator::VisitLiteral(Literal* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003235#ifdef DEBUG
3236 int original_height = frame_->height();
3237#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003238 Comment cmnt(masm_, "[ Literal");
ager@chromium.org357bf652010-04-12 11:30:10 +00003239 Register reg = frame_->GetTOSRegister();
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00003240 bool is_smi = node->handle()->IsSmi();
ager@chromium.org357bf652010-04-12 11:30:10 +00003241 __ mov(reg, Operand(node->handle()));
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00003242 frame_->EmitPush(reg, is_smi ? TypeInfo::Smi() : TypeInfo::Unknown());
ager@chromium.orgac091b72010-05-05 07:34:42 +00003243 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003244}
3245
3246
ager@chromium.org7c537e22008-10-16 08:43:32 +00003247void CodeGenerator::VisitRegExpLiteral(RegExpLiteral* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003248#ifdef DEBUG
3249 int original_height = frame_->height();
3250#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003251 Comment cmnt(masm_, "[ RexExp Literal");
3252
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003253 Register tmp = VirtualFrame::scratch0();
3254 // Free up a TOS register that can be used to push the literal.
3255 Register literal = frame_->GetTOSRegister();
3256
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003257 // Retrieve the literal array and check the allocated entry.
3258
3259 // Load the function of this activation.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003260 __ ldr(tmp, frame_->Function());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003261
3262 // Load the literals array of the function.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003263 __ ldr(tmp, FieldMemOperand(tmp, JSFunction::kLiteralsOffset));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003264
3265 // Load the literal at the ast saved index.
3266 int literal_offset =
3267 FixedArray::kHeaderSize + node->literal_index() * kPointerSize;
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003268 __ ldr(literal, FieldMemOperand(tmp, literal_offset));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003269
kasperl@chromium.org71affb52009-05-26 05:44:31 +00003270 JumpTarget done;
ager@chromium.orgab99eea2009-08-25 07:05:41 +00003271 __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003272 __ cmp(literal, ip);
3273 // This branch locks the virtual frame at the done label to match the
3274 // one we have here, where the literal register is not on the stack and
3275 // nothing is spilled.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003276 done.Branch(ne);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003277
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003278 // If the entry is undefined we call the runtime system to compute
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003279 // the literal.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003280 // literal array (0)
3281 frame_->EmitPush(tmp);
3282 // literal index (1)
3283 frame_->EmitPush(Operand(Smi::FromInt(node->literal_index())));
3284 // RegExp pattern (2)
3285 frame_->EmitPush(Operand(node->pattern()));
3286 // RegExp flags (3)
3287 frame_->EmitPush(Operand(node->flags()));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003288 frame_->CallRuntime(Runtime::kMaterializeRegExpLiteral, 4);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003289 __ Move(literal, r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003290
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003291 // This call to bind will get us back to the virtual frame we had before
3292 // where things are not spilled and the literal register is not on the stack.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003293 done.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003294 // Push the literal.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003295 frame_->EmitPush(literal);
ager@chromium.orgac091b72010-05-05 07:34:42 +00003296 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003297}
3298
3299
ager@chromium.org7c537e22008-10-16 08:43:32 +00003300void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003301#ifdef DEBUG
3302 int original_height = frame_->height();
3303#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003304 Comment cmnt(masm_, "[ ObjectLiteral");
3305
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003306 Register literal = frame_->GetTOSRegister();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003307 // Load the function of this activation.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003308 __ ldr(literal, frame_->Function());
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003309 // Literal array.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003310 __ ldr(literal, FieldMemOperand(literal, JSFunction::kLiteralsOffset));
3311 frame_->EmitPush(literal);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003312 // Literal index.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003313 frame_->EmitPush(Operand(Smi::FromInt(node->literal_index())));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003314 // Constant properties.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003315 frame_->EmitPush(Operand(node->constant_properties()));
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00003316 // Should the object literal have fast elements?
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003317 frame_->EmitPush(Operand(Smi::FromInt(node->fast_elements() ? 1 : 0)));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003318 if (node->depth() > 1) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00003319 frame_->CallRuntime(Runtime::kCreateObjectLiteral, 4);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003320 } else {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00003321 frame_->CallRuntime(Runtime::kCreateObjectLiteralShallow, 4);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003322 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003323 frame_->EmitPush(r0); // save the result
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003324 for (int i = 0; i < node->properties()->length(); i++) {
ager@chromium.org5c838252010-02-19 08:53:10 +00003325 // At the start of each iteration, the top of stack contains
3326 // the newly created object literal.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003327 ObjectLiteral::Property* property = node->properties()->at(i);
3328 Literal* key = property->key();
3329 Expression* value = property->value();
3330 switch (property->kind()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003331 case ObjectLiteral::Property::CONSTANT:
3332 break;
3333 case ObjectLiteral::Property::MATERIALIZED_LITERAL:
3334 if (CompileTimeValue::IsCompileTimeValue(property->value())) break;
3335 // else fall through
ager@chromium.org5c838252010-02-19 08:53:10 +00003336 case ObjectLiteral::Property::COMPUTED:
3337 if (key->handle()->IsSymbol()) {
3338 Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003339 Load(value);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003340 frame_->PopToR0();
3341 // Fetch the object literal.
3342 frame_->SpillAllButCopyTOSToR1();
ager@chromium.org5c838252010-02-19 08:53:10 +00003343 __ mov(r2, Operand(key->handle()));
ager@chromium.org5c838252010-02-19 08:53:10 +00003344 frame_->CallCodeObject(ic, RelocInfo::CODE_TARGET, 0);
3345 break;
3346 }
3347 // else fall through
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003348 case ObjectLiteral::Property::PROTOTYPE: {
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003349 frame_->Dup();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003350 Load(key);
3351 Load(value);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003352 frame_->CallRuntime(Runtime::kSetProperty, 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003353 break;
3354 }
3355 case ObjectLiteral::Property::SETTER: {
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003356 frame_->Dup();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003357 Load(key);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003358 frame_->EmitPush(Operand(Smi::FromInt(1)));
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003359 Load(value);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003360 frame_->CallRuntime(Runtime::kDefineAccessor, 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003361 break;
3362 }
3363 case ObjectLiteral::Property::GETTER: {
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003364 frame_->Dup();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003365 Load(key);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003366 frame_->EmitPush(Operand(Smi::FromInt(0)));
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003367 Load(value);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003368 frame_->CallRuntime(Runtime::kDefineAccessor, 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003369 break;
3370 }
3371 }
3372 }
ager@chromium.orgac091b72010-05-05 07:34:42 +00003373 ASSERT_EQ(original_height + 1, frame_->height());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003374}
3375
3376
ager@chromium.org7c537e22008-10-16 08:43:32 +00003377void CodeGenerator::VisitArrayLiteral(ArrayLiteral* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003378#ifdef DEBUG
3379 int original_height = frame_->height();
3380#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003381 Comment cmnt(masm_, "[ ArrayLiteral");
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003382
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003383 Register tos = frame_->GetTOSRegister();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003384 // Load the function of this activation.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003385 __ ldr(tos, frame_->Function());
ager@chromium.org5c838252010-02-19 08:53:10 +00003386 // Load the literals array of the function.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003387 __ ldr(tos, FieldMemOperand(tos, JSFunction::kLiteralsOffset));
3388 frame_->EmitPush(tos);
3389 frame_->EmitPush(Operand(Smi::FromInt(node->literal_index())));
3390 frame_->EmitPush(Operand(node->constant_elements()));
ager@chromium.org5c838252010-02-19 08:53:10 +00003391 int length = node->values()->length();
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003392 if (node->depth() > 1) {
3393 frame_->CallRuntime(Runtime::kCreateArrayLiteral, 3);
ager@chromium.org5c838252010-02-19 08:53:10 +00003394 } else if (length > FastCloneShallowArrayStub::kMaximumLength) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003395 frame_->CallRuntime(Runtime::kCreateArrayLiteralShallow, 3);
ager@chromium.org5c838252010-02-19 08:53:10 +00003396 } else {
3397 FastCloneShallowArrayStub stub(length);
3398 frame_->CallStub(&stub, 3);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003399 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003400 frame_->EmitPush(r0); // save the result
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003401 // r0: created object literal
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003402
3403 // Generate code to set the elements in the array that are not
3404 // literals.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003405 for (int i = 0; i < node->values()->length(); i++) {
3406 Expression* value = node->values()->at(i);
3407
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003408 // If value is a literal the property value is already set in the
3409 // boilerplate object.
3410 if (value->AsLiteral() != NULL) continue;
3411 // If value is a materialized literal the property value is already set
3412 // in the boilerplate object if it is simple.
3413 if (CompileTimeValue::IsCompileTimeValue(value)) continue;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003414
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003415 // The property must be set by generated code.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003416 Load(value);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003417 frame_->PopToR0();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003418 // Fetch the object literal.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003419 frame_->SpillAllButCopyTOSToR1();
3420
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003421 // Get the elements array.
3422 __ ldr(r1, FieldMemOperand(r1, JSObject::kElementsOffset));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003423
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003424 // Write to the indexed properties array.
kasperl@chromium.orge959c182009-07-27 08:59:04 +00003425 int offset = i * kPointerSize + FixedArray::kHeaderSize;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003426 __ str(r0, FieldMemOperand(r1, offset));
3427
3428 // Update the write barrier for the array address.
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00003429 __ RecordWrite(r1, Operand(offset), r3, r2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003430 }
ager@chromium.orgac091b72010-05-05 07:34:42 +00003431 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003432}
3433
3434
ager@chromium.org32912102009-01-16 10:38:43 +00003435void CodeGenerator::VisitCatchExtensionObject(CatchExtensionObject* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003436#ifdef DEBUG
3437 int original_height = frame_->height();
3438#endif
ager@chromium.org32912102009-01-16 10:38:43 +00003439 // Call runtime routine to allocate the catch extension object and
3440 // assign the exception value to the catch variable.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003441 Comment cmnt(masm_, "[ CatchExtensionObject");
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003442 Load(node->key());
3443 Load(node->value());
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00003444 frame_->CallRuntime(Runtime::kCreateCatchExtensionObject, 2);
3445 frame_->EmitPush(r0);
ager@chromium.orgac091b72010-05-05 07:34:42 +00003446 ASSERT_EQ(original_height + 1, frame_->height());
3447}
3448
3449
3450void CodeGenerator::EmitSlotAssignment(Assignment* node) {
3451#ifdef DEBUG
3452 int original_height = frame_->height();
3453#endif
3454 Comment cmnt(masm(), "[ Variable Assignment");
3455 Variable* var = node->target()->AsVariableProxy()->AsVariable();
3456 ASSERT(var != NULL);
3457 Slot* slot = var->slot();
3458 ASSERT(slot != NULL);
3459
3460 // Evaluate the right-hand side.
3461 if (node->is_compound()) {
3462 // For a compound assignment the right-hand side is a binary operation
3463 // between the current property value and the actual right-hand side.
3464 LoadFromSlotCheckForArguments(slot, NOT_INSIDE_TYPEOF);
3465
3466 // Perform the binary operation.
3467 Literal* literal = node->value()->AsLiteral();
3468 bool overwrite_value =
3469 (node->value()->AsBinaryOperation() != NULL &&
3470 node->value()->AsBinaryOperation()->ResultOverwriteAllowed());
3471 if (literal != NULL && literal->handle()->IsSmi()) {
3472 SmiOperation(node->binary_op(),
3473 literal->handle(),
3474 false,
3475 overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE);
3476 } else {
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00003477 GenerateInlineSmi inline_smi =
3478 loop_nesting() > 0 ? GENERATE_INLINE_SMI : DONT_GENERATE_INLINE_SMI;
3479 if (literal != NULL) {
3480 ASSERT(!literal->handle()->IsSmi());
3481 inline_smi = DONT_GENERATE_INLINE_SMI;
3482 }
ager@chromium.orgac091b72010-05-05 07:34:42 +00003483 Load(node->value());
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00003484 GenericBinaryOperation(node->binary_op(),
3485 overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE,
3486 inline_smi);
ager@chromium.orgac091b72010-05-05 07:34:42 +00003487 }
3488 } else {
3489 Load(node->value());
3490 }
3491
3492 // Perform the assignment.
3493 if (var->mode() != Variable::CONST || node->op() == Token::INIT_CONST) {
3494 CodeForSourcePosition(node->position());
3495 StoreToSlot(slot,
3496 node->op() == Token::INIT_CONST ? CONST_INIT : NOT_CONST_INIT);
3497 }
3498 ASSERT_EQ(original_height + 1, frame_->height());
3499}
3500
3501
3502void CodeGenerator::EmitNamedPropertyAssignment(Assignment* node) {
3503#ifdef DEBUG
3504 int original_height = frame_->height();
3505#endif
3506 Comment cmnt(masm(), "[ Named Property Assignment");
3507 Variable* var = node->target()->AsVariableProxy()->AsVariable();
3508 Property* prop = node->target()->AsProperty();
3509 ASSERT(var == NULL || (prop == NULL && var->is_global()));
3510
3511 // Initialize name and evaluate the receiver sub-expression if necessary. If
3512 // the receiver is trivial it is not placed on the stack at this point, but
3513 // loaded whenever actually needed.
3514 Handle<String> name;
3515 bool is_trivial_receiver = false;
3516 if (var != NULL) {
3517 name = var->name();
3518 } else {
3519 Literal* lit = prop->key()->AsLiteral();
3520 ASSERT_NOT_NULL(lit);
3521 name = Handle<String>::cast(lit->handle());
3522 // Do not materialize the receiver on the frame if it is trivial.
3523 is_trivial_receiver = prop->obj()->IsTrivial();
3524 if (!is_trivial_receiver) Load(prop->obj());
3525 }
3526
3527 // Change to slow case in the beginning of an initialization block to
3528 // avoid the quadratic behavior of repeatedly adding fast properties.
3529 if (node->starts_initialization_block()) {
3530 // Initialization block consists of assignments of the form expr.x = ..., so
3531 // this will never be an assignment to a variable, so there must be a
3532 // receiver object.
3533 ASSERT_EQ(NULL, var);
3534 if (is_trivial_receiver) {
3535 Load(prop->obj());
3536 } else {
3537 frame_->Dup();
3538 }
3539 frame_->CallRuntime(Runtime::kToSlowProperties, 1);
3540 }
3541
3542 // Change to fast case at the end of an initialization block. To prepare for
3543 // that add an extra copy of the receiver to the frame, so that it can be
3544 // converted back to fast case after the assignment.
3545 if (node->ends_initialization_block() && !is_trivial_receiver) {
3546 frame_->Dup();
3547 }
3548
3549 // Stack layout:
3550 // [tos] : receiver (only materialized if non-trivial)
3551 // [tos+1] : receiver if at the end of an initialization block
3552
3553 // Evaluate the right-hand side.
3554 if (node->is_compound()) {
3555 // For a compound assignment the right-hand side is a binary operation
3556 // between the current property value and the actual right-hand side.
3557 if (is_trivial_receiver) {
3558 Load(prop->obj());
3559 } else if (var != NULL) {
3560 LoadGlobal();
3561 } else {
3562 frame_->Dup();
3563 }
3564 EmitNamedLoad(name, var != NULL);
ager@chromium.orgac091b72010-05-05 07:34:42 +00003565
3566 // Perform the binary operation.
3567 Literal* literal = node->value()->AsLiteral();
3568 bool overwrite_value =
3569 (node->value()->AsBinaryOperation() != NULL &&
3570 node->value()->AsBinaryOperation()->ResultOverwriteAllowed());
3571 if (literal != NULL && literal->handle()->IsSmi()) {
3572 SmiOperation(node->binary_op(),
3573 literal->handle(),
3574 false,
3575 overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE);
3576 } else {
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00003577 GenerateInlineSmi inline_smi =
3578 loop_nesting() > 0 ? GENERATE_INLINE_SMI : DONT_GENERATE_INLINE_SMI;
3579 if (literal != NULL) {
3580 ASSERT(!literal->handle()->IsSmi());
3581 inline_smi = DONT_GENERATE_INLINE_SMI;
3582 }
ager@chromium.orgac091b72010-05-05 07:34:42 +00003583 Load(node->value());
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00003584 GenericBinaryOperation(node->binary_op(),
3585 overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE,
3586 inline_smi);
ager@chromium.orgac091b72010-05-05 07:34:42 +00003587 }
3588 } else {
3589 // For non-compound assignment just load the right-hand side.
3590 Load(node->value());
3591 }
3592
3593 // Stack layout:
3594 // [tos] : value
3595 // [tos+1] : receiver (only materialized if non-trivial)
3596 // [tos+2] : receiver if at the end of an initialization block
3597
3598 // Perform the assignment. It is safe to ignore constants here.
3599 ASSERT(var == NULL || var->mode() != Variable::CONST);
3600 ASSERT_NE(Token::INIT_CONST, node->op());
3601 if (is_trivial_receiver) {
3602 // Load the receiver and swap with the value.
3603 Load(prop->obj());
3604 Register t0 = frame_->PopToRegister();
3605 Register t1 = frame_->PopToRegister(t0);
3606 frame_->EmitPush(t0);
3607 frame_->EmitPush(t1);
3608 }
3609 CodeForSourcePosition(node->position());
3610 bool is_contextual = (var != NULL);
3611 EmitNamedStore(name, is_contextual);
3612 frame_->EmitPush(r0);
3613
3614 // Change to fast case at the end of an initialization block.
3615 if (node->ends_initialization_block()) {
3616 ASSERT_EQ(NULL, var);
3617 // The argument to the runtime call is the receiver.
3618 if (is_trivial_receiver) {
3619 Load(prop->obj());
3620 } else {
3621 // A copy of the receiver is below the value of the assignment. Swap
3622 // the receiver and the value of the assignment expression.
3623 Register t0 = frame_->PopToRegister();
3624 Register t1 = frame_->PopToRegister(t0);
3625 frame_->EmitPush(t0);
3626 frame_->EmitPush(t1);
3627 }
3628 frame_->CallRuntime(Runtime::kToFastProperties, 1);
3629 }
3630
3631 // Stack layout:
3632 // [tos] : result
3633
3634 ASSERT_EQ(original_height + 1, frame_->height());
3635}
3636
3637
3638void CodeGenerator::EmitKeyedPropertyAssignment(Assignment* node) {
3639#ifdef DEBUG
3640 int original_height = frame_->height();
3641#endif
3642 Comment cmnt(masm_, "[ Keyed Property Assignment");
3643 Property* prop = node->target()->AsProperty();
3644 ASSERT_NOT_NULL(prop);
3645
3646 // Evaluate the receiver subexpression.
3647 Load(prop->obj());
3648
3649 // Change to slow case in the beginning of an initialization block to
3650 // avoid the quadratic behavior of repeatedly adding fast properties.
3651 if (node->starts_initialization_block()) {
3652 frame_->Dup();
3653 frame_->CallRuntime(Runtime::kToSlowProperties, 1);
3654 }
3655
3656 // Change to fast case at the end of an initialization block. To prepare for
3657 // that add an extra copy of the receiver to the frame, so that it can be
3658 // converted back to fast case after the assignment.
3659 if (node->ends_initialization_block()) {
3660 frame_->Dup();
3661 }
3662
3663 // Evaluate the key subexpression.
3664 Load(prop->key());
3665
3666 // Stack layout:
3667 // [tos] : key
3668 // [tos+1] : receiver
3669 // [tos+2] : receiver if at the end of an initialization block
3670
3671 // Evaluate the right-hand side.
3672 if (node->is_compound()) {
3673 // For a compound assignment the right-hand side is a binary operation
3674 // between the current property value and the actual right-hand side.
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00003675 // Duplicate receiver and key for loading the current property value.
3676 frame_->Dup2();
ager@chromium.orgac091b72010-05-05 07:34:42 +00003677 EmitKeyedLoad();
3678 frame_->EmitPush(r0);
3679
3680 // Perform the binary operation.
3681 Literal* literal = node->value()->AsLiteral();
3682 bool overwrite_value =
3683 (node->value()->AsBinaryOperation() != NULL &&
3684 node->value()->AsBinaryOperation()->ResultOverwriteAllowed());
3685 if (literal != NULL && literal->handle()->IsSmi()) {
3686 SmiOperation(node->binary_op(),
3687 literal->handle(),
3688 false,
3689 overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE);
3690 } else {
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00003691 GenerateInlineSmi inline_smi =
3692 loop_nesting() > 0 ? GENERATE_INLINE_SMI : DONT_GENERATE_INLINE_SMI;
3693 if (literal != NULL) {
3694 ASSERT(!literal->handle()->IsSmi());
3695 inline_smi = DONT_GENERATE_INLINE_SMI;
3696 }
ager@chromium.orgac091b72010-05-05 07:34:42 +00003697 Load(node->value());
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00003698 GenericBinaryOperation(node->binary_op(),
3699 overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE,
3700 inline_smi);
ager@chromium.orgac091b72010-05-05 07:34:42 +00003701 }
3702 } else {
3703 // For non-compound assignment just load the right-hand side.
3704 Load(node->value());
3705 }
3706
3707 // Stack layout:
3708 // [tos] : value
3709 // [tos+1] : key
3710 // [tos+2] : receiver
3711 // [tos+3] : receiver if at the end of an initialization block
3712
3713 // Perform the assignment. It is safe to ignore constants here.
3714 ASSERT(node->op() != Token::INIT_CONST);
3715 CodeForSourcePosition(node->position());
ager@chromium.orgac091b72010-05-05 07:34:42 +00003716 EmitKeyedStore(prop->key()->type());
ager@chromium.orgac091b72010-05-05 07:34:42 +00003717 frame_->EmitPush(r0);
3718
3719 // Stack layout:
3720 // [tos] : result
3721 // [tos+1] : receiver if at the end of an initialization block
3722
3723 // Change to fast case at the end of an initialization block.
3724 if (node->ends_initialization_block()) {
3725 // The argument to the runtime call is the extra copy of the receiver,
3726 // which is below the value of the assignment. Swap the receiver and
3727 // the value of the assignment expression.
3728 Register t0 = frame_->PopToRegister();
3729 Register t1 = frame_->PopToRegister(t0);
3730 frame_->EmitPush(t1);
3731 frame_->EmitPush(t0);
3732 frame_->CallRuntime(Runtime::kToFastProperties, 1);
3733 }
3734
3735 // Stack layout:
3736 // [tos] : result
3737
3738 ASSERT_EQ(original_height + 1, frame_->height());
ager@chromium.org32912102009-01-16 10:38:43 +00003739}
3740
3741
ager@chromium.org7c537e22008-10-16 08:43:32 +00003742void CodeGenerator::VisitAssignment(Assignment* node) {
ager@chromium.org357bf652010-04-12 11:30:10 +00003743 VirtualFrame::RegisterAllocationScope scope(this);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003744#ifdef DEBUG
3745 int original_height = frame_->height();
3746#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003747 Comment cmnt(masm_, "[ Assignment");
mads.s.ager31e71382008-08-13 09:32:07 +00003748
ager@chromium.orgac091b72010-05-05 07:34:42 +00003749 Variable* var = node->target()->AsVariableProxy()->AsVariable();
3750 Property* prop = node->target()->AsProperty();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003751
ager@chromium.orgac091b72010-05-05 07:34:42 +00003752 if (var != NULL && !var->is_global()) {
3753 EmitSlotAssignment(node);
mads.s.ager31e71382008-08-13 09:32:07 +00003754
ager@chromium.orgac091b72010-05-05 07:34:42 +00003755 } else if ((prop != NULL && prop->key()->IsPropertyName()) ||
3756 (var != NULL && var->is_global())) {
3757 // Properties whose keys are property names and global variables are
3758 // treated as named property references. We do not need to consider
3759 // global 'this' because it is not a valid left-hand side.
3760 EmitNamedPropertyAssignment(node);
3761
3762 } else if (prop != NULL) {
3763 // Other properties (including rewritten parameters for a function that
3764 // uses arguments) are keyed property assignments.
3765 EmitKeyedPropertyAssignment(node);
3766
3767 } else {
3768 // Invalid left-hand side.
3769 Load(node->target());
3770 frame_->CallRuntime(Runtime::kThrowReferenceError, 1);
3771 // The runtime call doesn't actually return but the code generator will
3772 // still generate code and expects a certain frame height.
3773 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003774 }
ager@chromium.orgac091b72010-05-05 07:34:42 +00003775 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003776}
3777
3778
ager@chromium.org7c537e22008-10-16 08:43:32 +00003779void CodeGenerator::VisitThrow(Throw* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003780#ifdef DEBUG
3781 int original_height = frame_->height();
3782#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003783 Comment cmnt(masm_, "[ Throw");
3784
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003785 Load(node->exception());
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00003786 CodeForSourcePosition(node->position());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003787 frame_->CallRuntime(Runtime::kThrow, 1);
3788 frame_->EmitPush(r0);
ager@chromium.orgac091b72010-05-05 07:34:42 +00003789 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003790}
3791
3792
ager@chromium.org7c537e22008-10-16 08:43:32 +00003793void CodeGenerator::VisitProperty(Property* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003794#ifdef DEBUG
3795 int original_height = frame_->height();
3796#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003797 Comment cmnt(masm_, "[ Property");
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00003798
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003799 { Reference property(this, node);
ager@chromium.org357bf652010-04-12 11:30:10 +00003800 property.GetValue();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003801 }
ager@chromium.orgac091b72010-05-05 07:34:42 +00003802 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003803}
3804
3805
ager@chromium.org7c537e22008-10-16 08:43:32 +00003806void CodeGenerator::VisitCall(Call* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003807#ifdef DEBUG
3808 int original_height = frame_->height();
3809#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003810 Comment cmnt(masm_, "[ Call");
3811
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003812 Expression* function = node->expression();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003813 ZoneList<Expression*>* args = node->arguments();
3814
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003815 // Standard function call.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003816 // Check if the function is a variable or a property.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003817 Variable* var = function->AsVariableProxy()->AsVariable();
3818 Property* property = function->AsProperty();
3819
3820 // ------------------------------------------------------------------------
3821 // Fast-case: Use inline caching.
3822 // ---
3823 // According to ECMA-262, section 11.2.3, page 44, the function to call
3824 // must be resolved after the arguments have been evaluated. The IC code
3825 // automatically handles this by loading the arguments before the function
3826 // is resolved in cache misses (this also holds for megamorphic calls).
3827 // ------------------------------------------------------------------------
3828
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003829 if (var != NULL && var->is_possibly_eval()) {
3830 // ----------------------------------
3831 // JavaScript example: 'eval(arg)' // eval is not known to be shadowed
3832 // ----------------------------------
3833
3834 // In a call to eval, we first call %ResolvePossiblyDirectEval to
3835 // resolve the function we need to call and the receiver of the
3836 // call. Then we call the resolved function using the given
3837 // arguments.
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00003838
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003839 // Prepare stack for call to resolved function.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003840 Load(function);
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00003841
3842 // Allocate a frame slot for the receiver.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003843 frame_->EmitPushRoot(Heap::kUndefinedValueRootIndex);
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00003844
3845 // Load the arguments.
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003846 int arg_count = args->length();
3847 for (int i = 0; i < arg_count; i++) {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003848 Load(args->at(i));
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003849 }
3850
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003851 VirtualFrame::SpilledScope spilled_scope(frame_);
3852
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00003853 // If we know that eval can only be shadowed by eval-introduced
3854 // variables we attempt to load the global eval function directly
3855 // in generated code. If we succeed, there is no need to perform a
3856 // context lookup in the runtime system.
3857 JumpTarget done;
3858 if (var->slot() != NULL && var->mode() == Variable::DYNAMIC_GLOBAL) {
3859 ASSERT(var->slot()->type() == Slot::LOOKUP);
3860 JumpTarget slow;
3861 // Prepare the stack for the call to
3862 // ResolvePossiblyDirectEvalNoLookup by pushing the loaded
3863 // function, the first argument to the eval call and the
3864 // receiver.
3865 LoadFromGlobalSlotCheckExtensions(var->slot(),
3866 NOT_INSIDE_TYPEOF,
3867 &slow);
3868 frame_->EmitPush(r0);
3869 if (arg_count > 0) {
3870 __ ldr(r1, MemOperand(sp, arg_count * kPointerSize));
3871 frame_->EmitPush(r1);
3872 } else {
3873 frame_->EmitPush(r2);
3874 }
3875 __ ldr(r1, frame_->Receiver());
3876 frame_->EmitPush(r1);
3877
3878 frame_->CallRuntime(Runtime::kResolvePossiblyDirectEvalNoLookup, 3);
3879
3880 done.Jump();
3881 slow.Bind();
3882 }
3883
3884 // Prepare the stack for the call to ResolvePossiblyDirectEval by
3885 // pushing the loaded function, the first argument to the eval
3886 // call and the receiver.
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003887 __ ldr(r1, MemOperand(sp, arg_count * kPointerSize + kPointerSize));
3888 frame_->EmitPush(r1);
3889 if (arg_count > 0) {
3890 __ ldr(r1, MemOperand(sp, arg_count * kPointerSize));
3891 frame_->EmitPush(r1);
3892 } else {
3893 frame_->EmitPush(r2);
3894 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003895 __ ldr(r1, frame_->Receiver());
3896 frame_->EmitPush(r1);
3897
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003898 // Resolve the call.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003899 frame_->CallRuntime(Runtime::kResolvePossiblyDirectEval, 3);
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003900
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00003901 // If we generated fast-case code bind the jump-target where fast
3902 // and slow case merge.
3903 if (done.is_linked()) done.Bind();
3904
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003905 // Touch up stack with the right values for the function and the receiver.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003906 __ str(r0, MemOperand(sp, (arg_count + 1) * kPointerSize));
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003907 __ str(r1, MemOperand(sp, arg_count * kPointerSize));
3908
3909 // Call the function.
3910 CodeForSourcePosition(node->position());
3911
3912 InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003913 CallFunctionStub call_function(arg_count, in_loop, RECEIVER_MIGHT_BE_VALUE);
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003914 frame_->CallStub(&call_function, arg_count + 1);
3915
3916 __ ldr(cp, frame_->Context());
3917 // Remove the function from the stack.
3918 frame_->Drop();
3919 frame_->EmitPush(r0);
3920
3921 } else if (var != NULL && !var->is_this() && var->is_global()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003922 // ----------------------------------
3923 // JavaScript example: 'foo(1, 2, 3)' // foo is global
3924 // ----------------------------------
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003925 // Pass the global object as the receiver and let the IC stub
3926 // patch the stack to use the global proxy as 'this' in the
3927 // invoked function.
3928 LoadGlobal();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003929
3930 // Load the arguments.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003931 int arg_count = args->length();
3932 for (int i = 0; i < arg_count; i++) {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003933 Load(args->at(i));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003934 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003935
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003936 VirtualFrame::SpilledScope spilled_scope(frame_);
ager@chromium.org5c838252010-02-19 08:53:10 +00003937 // Setup the name register and call the IC initialization code.
3938 __ mov(r2, Operand(var->name()));
kasperl@chromium.org71affb52009-05-26 05:44:31 +00003939 InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP;
3940 Handle<Code> stub = ComputeCallInitialize(arg_count, in_loop);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00003941 CodeForSourcePosition(node->position());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003942 frame_->CallCodeObject(stub, RelocInfo::CODE_TARGET_CONTEXT,
3943 arg_count + 1);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00003944 __ ldr(cp, frame_->Context());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003945 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003946
3947 } else if (var != NULL && var->slot() != NULL &&
3948 var->slot()->type() == Slot::LOOKUP) {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003949 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003950 // ----------------------------------
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00003951 // JavaScript examples:
3952 //
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00003953 // with (obj) foo(1, 2, 3) // foo may be in obj.
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00003954 //
3955 // function f() {};
3956 // function g() {
3957 // eval(...);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00003958 // f(); // f could be in extension object.
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00003959 // }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003960 // ----------------------------------
3961
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00003962 // JumpTargets do not yet support merging frames so the frame must be
3963 // spilled when jumping to these targets.
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00003964 JumpTarget slow, done;
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00003965
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00003966 // Generate fast case for loading functions from slots that
3967 // correspond to local/global variables or arguments unless they
3968 // are shadowed by eval-introduced bindings.
3969 EmitDynamicLoadFromSlotFastCase(var->slot(),
3970 NOT_INSIDE_TYPEOF,
3971 &slow,
3972 &done);
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00003973
3974 slow.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003975 // Load the function
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003976 frame_->EmitPush(cp);
mads.s.ager31e71382008-08-13 09:32:07 +00003977 __ mov(r0, Operand(var->name()));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003978 frame_->EmitPush(r0);
3979 frame_->CallRuntime(Runtime::kLoadContextSlot, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003980 // r0: slot value; r1: receiver
3981
3982 // Load the receiver.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003983 frame_->EmitPush(r0); // function
3984 frame_->EmitPush(r1); // receiver
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003985
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00003986 // If fast case code has been generated, emit code to push the
3987 // function and receiver and have the slow path jump around this
3988 // code.
3989 if (done.is_linked()) {
3990 JumpTarget call;
3991 call.Jump();
3992 done.Bind();
3993 frame_->EmitPush(r0); // function
3994 LoadGlobalReceiver(r1); // receiver
3995 call.Bind();
3996 }
3997
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00003998 // Call the function. At this point, everything is spilled but the
3999 // function and receiver are in r0 and r1.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004000 CallWithArguments(args, NO_CALL_FUNCTION_FLAGS, node->position());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004001 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004002
4003 } else if (property != NULL) {
4004 // Check if the key is a literal string.
4005 Literal* literal = property->key()->AsLiteral();
4006
4007 if (literal != NULL && literal->handle()->IsSymbol()) {
4008 // ------------------------------------------------------------------
4009 // JavaScript example: 'object.foo(1, 2, 3)' or 'map["key"](1, 2, 3)'
4010 // ------------------------------------------------------------------
4011
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00004012 Handle<String> name = Handle<String>::cast(literal->handle());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004013
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00004014 if (ArgumentsMode() == LAZY_ARGUMENTS_ALLOCATION &&
4015 name->IsEqualTo(CStrVector("apply")) &&
4016 args->length() == 2 &&
4017 args->at(1)->AsVariableProxy() != NULL &&
4018 args->at(1)->AsVariableProxy()->IsArguments()) {
4019 // Use the optimized Function.prototype.apply that avoids
4020 // allocating lazily allocated arguments objects.
4021 CallApplyLazy(property->obj(),
4022 args->at(0),
4023 args->at(1)->AsVariableProxy(),
4024 node->position());
4025
4026 } else {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004027 Load(property->obj()); // Receiver.
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00004028 // Load the arguments.
4029 int arg_count = args->length();
4030 for (int i = 0; i < arg_count; i++) {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004031 Load(args->at(i));
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00004032 }
4033
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004034 VirtualFrame::SpilledScope spilled_scope(frame_);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00004035 // Set the name register and call the IC initialization code.
4036 __ mov(r2, Operand(name));
4037 InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP;
4038 Handle<Code> stub = ComputeCallInitialize(arg_count, in_loop);
4039 CodeForSourcePosition(node->position());
4040 frame_->CallCodeObject(stub, RelocInfo::CODE_TARGET, arg_count + 1);
4041 __ ldr(cp, frame_->Context());
4042 frame_->EmitPush(r0);
4043 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004044
4045 } else {
4046 // -------------------------------------------
4047 // JavaScript example: 'array[index](1, 2, 3)'
4048 // -------------------------------------------
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004049 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004050
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004051 Load(property->obj());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004052 if (property->is_synthetic()) {
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00004053 Load(property->key());
4054 EmitKeyedLoad();
4055 // Put the function below the receiver.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004056 // Use the global receiver.
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00004057 frame_->EmitPush(r0); // Function.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004058 LoadGlobalReceiver(r0);
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00004059 // Call the function.
4060 CallWithArguments(args, RECEIVER_MIGHT_BE_VALUE, node->position());
4061 frame_->EmitPush(r0);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004062 } else {
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00004063 // Load the arguments.
4064 int arg_count = args->length();
4065 for (int i = 0; i < arg_count; i++) {
4066 Load(args->at(i));
4067 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004068
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00004069 // Set the name register and call the IC initialization code.
4070 Load(property->key());
4071 frame_->EmitPop(r2); // Function name.
4072
4073 InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP;
4074 Handle<Code> stub = ComputeKeyedCallInitialize(arg_count, in_loop);
4075 CodeForSourcePosition(node->position());
4076 frame_->CallCodeObject(stub, RelocInfo::CODE_TARGET, arg_count + 1);
4077 __ ldr(cp, frame_->Context());
4078 frame_->EmitPush(r0);
4079 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004080 }
4081
4082 } else {
4083 // ----------------------------------
4084 // JavaScript example: 'foo(1, 2, 3)' // foo is not global
4085 // ----------------------------------
4086
4087 // Load the function.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004088 Load(function);
4089
4090 VirtualFrame::SpilledScope spilled_scope(frame_);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004091
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004092 // Pass the global proxy as the receiver.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004093 LoadGlobalReceiver(r0);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004094
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004095 // Call the function.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004096 CallWithArguments(args, NO_CALL_FUNCTION_FLAGS, node->position());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004097 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004098 }
ager@chromium.orgac091b72010-05-05 07:34:42 +00004099 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004100}
4101
4102
ager@chromium.org7c537e22008-10-16 08:43:32 +00004103void CodeGenerator::VisitCallNew(CallNew* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004104#ifdef DEBUG
4105 int original_height = frame_->height();
4106#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004107 Comment cmnt(masm_, "[ CallNew");
4108
4109 // According to ECMA-262, section 11.2.2, page 44, the function
4110 // expression in new calls must be evaluated before the
4111 // arguments. This is different from ordinary calls, where the
4112 // actual function to call is resolved after the arguments have been
4113 // evaluated.
4114
4115 // Compute function to call and use the global object as the
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004116 // receiver. There is no need to use the global proxy here because
4117 // it will always be replaced with a newly allocated object.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004118 Load(node->expression());
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004119 LoadGlobal();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004120
4121 // Push the arguments ("left-to-right") on the stack.
4122 ZoneList<Expression*>* args = node->arguments();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004123 int arg_count = args->length();
4124 for (int i = 0; i < arg_count; i++) {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004125 Load(args->at(i));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004126 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004127
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004128 VirtualFrame::SpilledScope spilled_scope(frame_);
4129
mads.s.ager31e71382008-08-13 09:32:07 +00004130 // r0: the number of arguments.
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00004131 __ mov(r0, Operand(arg_count));
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00004132 // Load the function into r1 as per calling convention.
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00004133 __ ldr(r1, frame_->ElementAt(arg_count + 1));
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00004134
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004135 // Call the construct call builtin that handles allocation and
4136 // constructor invocation.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004137 CodeForSourcePosition(node->position());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004138 Handle<Code> ic(Builtins::builtin(Builtins::JSConstructCall));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004139 frame_->CallCodeObject(ic, RelocInfo::CONSTRUCT_CALL, arg_count + 1);
mads.s.ager31e71382008-08-13 09:32:07 +00004140
4141 // Discard old TOS value and push r0 on the stack (same as Pop(), push(r0)).
ager@chromium.org3bf7b912008-11-17 09:09:45 +00004142 __ str(r0, frame_->Top());
ager@chromium.orgac091b72010-05-05 07:34:42 +00004143 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004144}
4145
4146
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004147void CodeGenerator::GenerateClassOf(ZoneList<Expression*>* args) {
ager@chromium.org357bf652010-04-12 11:30:10 +00004148 VirtualFrame::SpilledScope spilled_scope(frame_);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004149 ASSERT(args->length() == 1);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00004150 JumpTarget leave, null, function, non_function_constructor;
4151
4152 // Load the object into r0.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004153 Load(args->at(0));
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00004154 frame_->EmitPop(r0);
4155
4156 // If the object is a smi, we return null.
4157 __ tst(r0, Operand(kSmiTagMask));
4158 null.Branch(eq);
4159
4160 // Check that the object is a JS object but take special care of JS
4161 // functions to make sure they have 'Function' as their class.
4162 __ CompareObjectType(r0, r0, r1, FIRST_JS_OBJECT_TYPE);
4163 null.Branch(lt);
4164
4165 // As long as JS_FUNCTION_TYPE is the last instance type and it is
4166 // right after LAST_JS_OBJECT_TYPE, we can avoid checking for
4167 // LAST_JS_OBJECT_TYPE.
4168 ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
4169 ASSERT(JS_FUNCTION_TYPE == LAST_JS_OBJECT_TYPE + 1);
4170 __ cmp(r1, Operand(JS_FUNCTION_TYPE));
4171 function.Branch(eq);
4172
4173 // Check if the constructor in the map is a function.
4174 __ ldr(r0, FieldMemOperand(r0, Map::kConstructorOffset));
4175 __ CompareObjectType(r0, r1, r1, JS_FUNCTION_TYPE);
4176 non_function_constructor.Branch(ne);
4177
4178 // The r0 register now contains the constructor function. Grab the
4179 // instance class name from there.
4180 __ ldr(r0, FieldMemOperand(r0, JSFunction::kSharedFunctionInfoOffset));
4181 __ ldr(r0, FieldMemOperand(r0, SharedFunctionInfo::kInstanceClassNameOffset));
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004182 frame_->EmitPush(r0);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00004183 leave.Jump();
4184
4185 // Functions have class 'Function'.
4186 function.Bind();
4187 __ mov(r0, Operand(Factory::function_class_symbol()));
4188 frame_->EmitPush(r0);
4189 leave.Jump();
4190
4191 // Objects with a non-function constructor have class 'Object'.
4192 non_function_constructor.Bind();
4193 __ mov(r0, Operand(Factory::Object_symbol()));
4194 frame_->EmitPush(r0);
4195 leave.Jump();
4196
4197 // Non-JS objects have class null.
4198 null.Bind();
ager@chromium.orgab99eea2009-08-25 07:05:41 +00004199 __ LoadRoot(r0, Heap::kNullValueRootIndex);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00004200 frame_->EmitPush(r0);
4201
4202 // All done.
4203 leave.Bind();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004204}
4205
4206
ager@chromium.org7c537e22008-10-16 08:43:32 +00004207void CodeGenerator::GenerateValueOf(ZoneList<Expression*>* args) {
ager@chromium.org357bf652010-04-12 11:30:10 +00004208 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004209 ASSERT(args->length() == 1);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004210 JumpTarget leave;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004211 Load(args->at(0));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004212 frame_->EmitPop(r0); // r0 contains object.
mads.s.ager31e71382008-08-13 09:32:07 +00004213 // if (object->IsSmi()) return the object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004214 __ tst(r0, Operand(kSmiTagMask));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004215 leave.Branch(eq);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00004216 // It is a heap object - get map. If (!object->IsJSValue()) return the object.
4217 __ CompareObjectType(r0, r1, r1, JS_VALUE_TYPE);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004218 leave.Branch(ne);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004219 // Load the value.
4220 __ ldr(r0, FieldMemOperand(r0, JSValue::kValueOffset));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004221 leave.Bind();
4222 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004223}
4224
4225
ager@chromium.org7c537e22008-10-16 08:43:32 +00004226void CodeGenerator::GenerateSetValueOf(ZoneList<Expression*>* args) {
ager@chromium.org357bf652010-04-12 11:30:10 +00004227 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004228 ASSERT(args->length() == 2);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004229 JumpTarget leave;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004230 Load(args->at(0)); // Load the object.
4231 Load(args->at(1)); // Load the value.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004232 frame_->EmitPop(r0); // r0 contains value
4233 frame_->EmitPop(r1); // r1 contains object
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004234 // if (object->IsSmi()) return object.
4235 __ tst(r1, Operand(kSmiTagMask));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004236 leave.Branch(eq);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00004237 // It is a heap object - get map. If (!object->IsJSValue()) return the object.
4238 __ CompareObjectType(r1, r2, r2, JS_VALUE_TYPE);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004239 leave.Branch(ne);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004240 // Store the value.
4241 __ str(r0, FieldMemOperand(r1, JSValue::kValueOffset));
4242 // Update the write barrier.
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00004243 __ RecordWrite(r1, Operand(JSValue::kValueOffset - kHeapObjectTag), r2, r3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004244 // Leave.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004245 leave.Bind();
4246 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004247}
4248
4249
ager@chromium.org7c537e22008-10-16 08:43:32 +00004250void CodeGenerator::GenerateIsSmi(ZoneList<Expression*>* args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004251 ASSERT(args->length() == 1);
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004252 Load(args->at(0));
4253 Register reg = frame_->PopToRegister();
4254 __ tst(reg, Operand(kSmiTagMask));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004255 cc_reg_ = eq;
4256}
4257
4258
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004259void CodeGenerator::GenerateLog(ZoneList<Expression*>* args) {
4260 // See comment in CodeGenerator::GenerateLog in codegen-ia32.cc.
4261 ASSERT_EQ(args->length(), 3);
christian.plesner.hansen@gmail.comaca49682009-01-07 14:29:04 +00004262#ifdef ENABLE_LOGGING_AND_PROFILING
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004263 if (ShouldGenerateLog(args->at(0))) {
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004264 Load(args->at(1));
4265 Load(args->at(2));
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004266 frame_->CallRuntime(Runtime::kLog, 2);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004267 }
christian.plesner.hansen@gmail.comaca49682009-01-07 14:29:04 +00004268#endif
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004269 frame_->EmitPushRoot(Heap::kUndefinedValueRootIndex);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004270}
4271
4272
ager@chromium.org7c537e22008-10-16 08:43:32 +00004273void CodeGenerator::GenerateIsNonNegativeSmi(ZoneList<Expression*>* args) {
ager@chromium.orgc27e4e72008-09-04 13:52:27 +00004274 ASSERT(args->length() == 1);
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004275 Load(args->at(0));
4276 Register reg = frame_->PopToRegister();
4277 __ tst(reg, Operand(kSmiTagMask | 0x80000000u));
ager@chromium.orgc27e4e72008-09-04 13:52:27 +00004278 cc_reg_ = eq;
4279}
4280
4281
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004282// Generates the Math.pow method - currently just calls runtime.
4283void CodeGenerator::GenerateMathPow(ZoneList<Expression*>* args) {
4284 ASSERT(args->length() == 2);
4285 Load(args->at(0));
4286 Load(args->at(1));
4287 frame_->CallRuntime(Runtime::kMath_pow, 2);
4288 frame_->EmitPush(r0);
4289}
4290
4291
4292// Generates the Math.sqrt method - currently just calls runtime.
4293void CodeGenerator::GenerateMathSqrt(ZoneList<Expression*>* args) {
4294 ASSERT(args->length() == 1);
4295 Load(args->at(0));
4296 frame_->CallRuntime(Runtime::kMath_sqrt, 1);
4297 frame_->EmitPush(r0);
4298}
4299
4300
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004301class DeferredStringCharCodeAt : public DeferredCode {
4302 public:
4303 DeferredStringCharCodeAt(Register object,
4304 Register index,
4305 Register scratch,
4306 Register result)
4307 : result_(result),
4308 char_code_at_generator_(object,
4309 index,
4310 scratch,
4311 result,
4312 &need_conversion_,
4313 &need_conversion_,
4314 &index_out_of_range_,
4315 STRING_INDEX_IS_NUMBER) {}
4316
4317 StringCharCodeAtGenerator* fast_case_generator() {
4318 return &char_code_at_generator_;
4319 }
4320
4321 virtual void Generate() {
4322 VirtualFrameRuntimeCallHelper call_helper(frame_state());
4323 char_code_at_generator_.GenerateSlow(masm(), call_helper);
4324
4325 __ bind(&need_conversion_);
4326 // Move the undefined value into the result register, which will
4327 // trigger conversion.
4328 __ LoadRoot(result_, Heap::kUndefinedValueRootIndex);
4329 __ jmp(exit_label());
4330
4331 __ bind(&index_out_of_range_);
4332 // When the index is out of range, the spec requires us to return
4333 // NaN.
4334 __ LoadRoot(result_, Heap::kNanValueRootIndex);
4335 __ jmp(exit_label());
4336 }
4337
4338 private:
4339 Register result_;
4340
4341 Label need_conversion_;
4342 Label index_out_of_range_;
4343
4344 StringCharCodeAtGenerator char_code_at_generator_;
4345};
4346
4347
4348// This generates code that performs a String.prototype.charCodeAt() call
4349// or returns a smi in order to trigger conversion.
4350void CodeGenerator::GenerateStringCharCodeAt(ZoneList<Expression*>* args) {
4351 VirtualFrame::SpilledScope spilled_scope(frame_);
4352 Comment(masm_, "[ GenerateStringCharCodeAt");
kasper.lund7276f142008-07-30 08:49:36 +00004353 ASSERT(args->length() == 2);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004354
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004355 Load(args->at(0));
4356 Load(args->at(1));
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004357
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004358 Register index = r1;
4359 Register object = r2;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004360
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004361 frame_->EmitPop(r1);
4362 frame_->EmitPop(r2);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004363
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004364 // We need two extra registers.
4365 Register scratch = r3;
4366 Register result = r0;
4367
4368 DeferredStringCharCodeAt* deferred =
4369 new DeferredStringCharCodeAt(object,
4370 index,
4371 scratch,
4372 result);
4373 deferred->fast_case_generator()->GenerateFast(masm_);
4374 deferred->BindExit();
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004375 frame_->EmitPush(result);
kasper.lund7276f142008-07-30 08:49:36 +00004376}
4377
4378
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004379class DeferredStringCharFromCode : public DeferredCode {
4380 public:
4381 DeferredStringCharFromCode(Register code,
4382 Register result)
4383 : char_from_code_generator_(code, result) {}
4384
4385 StringCharFromCodeGenerator* fast_case_generator() {
4386 return &char_from_code_generator_;
4387 }
4388
4389 virtual void Generate() {
4390 VirtualFrameRuntimeCallHelper call_helper(frame_state());
4391 char_from_code_generator_.GenerateSlow(masm(), call_helper);
4392 }
4393
4394 private:
4395 StringCharFromCodeGenerator char_from_code_generator_;
4396};
4397
4398
4399// Generates code for creating a one-char string from a char code.
4400void CodeGenerator::GenerateStringCharFromCode(ZoneList<Expression*>* args) {
4401 VirtualFrame::SpilledScope spilled_scope(frame_);
4402 Comment(masm_, "[ GenerateStringCharFromCode");
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004403 ASSERT(args->length() == 1);
4404
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004405 Load(args->at(0));
4406
ager@chromium.orgac091b72010-05-05 07:34:42 +00004407 Register code = r1;
ager@chromium.orgac091b72010-05-05 07:34:42 +00004408 Register result = r0;
4409
ager@chromium.orgac091b72010-05-05 07:34:42 +00004410 frame_->EmitPop(code);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004411
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004412 DeferredStringCharFromCode* deferred = new DeferredStringCharFromCode(
4413 code, result);
4414 deferred->fast_case_generator()->GenerateFast(masm_);
4415 deferred->BindExit();
4416 frame_->EmitPush(result);
4417}
4418
4419
4420class DeferredStringCharAt : public DeferredCode {
4421 public:
4422 DeferredStringCharAt(Register object,
4423 Register index,
4424 Register scratch1,
4425 Register scratch2,
4426 Register result)
4427 : result_(result),
4428 char_at_generator_(object,
4429 index,
4430 scratch1,
4431 scratch2,
4432 result,
4433 &need_conversion_,
4434 &need_conversion_,
4435 &index_out_of_range_,
4436 STRING_INDEX_IS_NUMBER) {}
4437
4438 StringCharAtGenerator* fast_case_generator() {
4439 return &char_at_generator_;
4440 }
4441
4442 virtual void Generate() {
4443 VirtualFrameRuntimeCallHelper call_helper(frame_state());
4444 char_at_generator_.GenerateSlow(masm(), call_helper);
4445
4446 __ bind(&need_conversion_);
4447 // Move smi zero into the result register, which will trigger
4448 // conversion.
4449 __ mov(result_, Operand(Smi::FromInt(0)));
4450 __ jmp(exit_label());
4451
4452 __ bind(&index_out_of_range_);
4453 // When the index is out of range, the spec requires us to return
4454 // the empty string.
4455 __ LoadRoot(result_, Heap::kEmptyStringRootIndex);
4456 __ jmp(exit_label());
4457 }
4458
4459 private:
4460 Register result_;
4461
4462 Label need_conversion_;
4463 Label index_out_of_range_;
4464
4465 StringCharAtGenerator char_at_generator_;
4466};
4467
4468
4469// This generates code that performs a String.prototype.charAt() call
4470// or returns a smi in order to trigger conversion.
4471void CodeGenerator::GenerateStringCharAt(ZoneList<Expression*>* args) {
4472 VirtualFrame::SpilledScope spilled_scope(frame_);
4473 Comment(masm_, "[ GenerateStringCharAt");
4474 ASSERT(args->length() == 2);
4475
4476 Load(args->at(0));
4477 Load(args->at(1));
4478
4479 Register index = r1;
4480 Register object = r2;
4481
4482 frame_->EmitPop(r1);
4483 frame_->EmitPop(r2);
4484
4485 // We need three extra registers.
4486 Register scratch1 = r3;
4487 Register scratch2 = r4;
4488 Register result = r0;
4489
4490 DeferredStringCharAt* deferred =
4491 new DeferredStringCharAt(object,
4492 index,
4493 scratch1,
4494 scratch2,
4495 result);
4496 deferred->fast_case_generator()->GenerateFast(masm_);
4497 deferred->BindExit();
ager@chromium.orgac091b72010-05-05 07:34:42 +00004498 frame_->EmitPush(result);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004499}
4500
4501
ager@chromium.org7c537e22008-10-16 08:43:32 +00004502void CodeGenerator::GenerateIsArray(ZoneList<Expression*>* args) {
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00004503 ASSERT(args->length() == 1);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004504 Load(args->at(0));
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004505 JumpTarget answer;
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00004506 // We need the CC bits to come out as not_equal in the case where the
4507 // object is a smi. This can't be done with the usual test opcode so
4508 // we use XOR to get the right CC bits.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004509 Register possible_array = frame_->PopToRegister();
4510 Register scratch = VirtualFrame::scratch0();
4511 __ and_(scratch, possible_array, Operand(kSmiTagMask));
4512 __ eor(scratch, scratch, Operand(kSmiTagMask), SetCC);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004513 answer.Branch(ne);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00004514 // It is a heap object - get the map. Check if the object is a JS array.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004515 __ CompareObjectType(possible_array, scratch, scratch, JS_ARRAY_TYPE);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004516 answer.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004517 cc_reg_ = eq;
4518}
4519
4520
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00004521void CodeGenerator::GenerateIsRegExp(ZoneList<Expression*>* args) {
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00004522 ASSERT(args->length() == 1);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004523 Load(args->at(0));
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00004524 JumpTarget answer;
4525 // We need the CC bits to come out as not_equal in the case where the
4526 // object is a smi. This can't be done with the usual test opcode so
4527 // we use XOR to get the right CC bits.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004528 Register possible_regexp = frame_->PopToRegister();
4529 Register scratch = VirtualFrame::scratch0();
4530 __ and_(scratch, possible_regexp, Operand(kSmiTagMask));
4531 __ eor(scratch, scratch, Operand(kSmiTagMask), SetCC);
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00004532 answer.Branch(ne);
4533 // It is a heap object - get the map. Check if the object is a regexp.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004534 __ CompareObjectType(possible_regexp, scratch, scratch, JS_REGEXP_TYPE);
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00004535 answer.Bind();
4536 cc_reg_ = eq;
4537}
4538
4539
ager@chromium.org6141cbe2009-11-20 12:14:52 +00004540void CodeGenerator::GenerateIsObject(ZoneList<Expression*>* args) {
4541 // This generates a fast version of:
4542 // (typeof(arg) === 'object' || %_ClassOf(arg) == 'RegExp')
ager@chromium.org6141cbe2009-11-20 12:14:52 +00004543 ASSERT(args->length() == 1);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004544 Load(args->at(0));
4545 Register possible_object = frame_->PopToRegister();
4546 __ tst(possible_object, Operand(kSmiTagMask));
ager@chromium.org6141cbe2009-11-20 12:14:52 +00004547 false_target()->Branch(eq);
4548
4549 __ LoadRoot(ip, Heap::kNullValueRootIndex);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004550 __ cmp(possible_object, ip);
ager@chromium.org6141cbe2009-11-20 12:14:52 +00004551 true_target()->Branch(eq);
4552
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004553 Register map_reg = VirtualFrame::scratch0();
4554 __ ldr(map_reg, FieldMemOperand(possible_object, HeapObject::kMapOffset));
ager@chromium.org6141cbe2009-11-20 12:14:52 +00004555 // Undetectable objects behave like undefined when tested with typeof.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004556 __ ldrb(possible_object, FieldMemOperand(map_reg, Map::kBitFieldOffset));
4557 __ tst(possible_object, Operand(1 << Map::kIsUndetectable));
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004558 false_target()->Branch(ne);
ager@chromium.org6141cbe2009-11-20 12:14:52 +00004559
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004560 __ ldrb(possible_object, FieldMemOperand(map_reg, Map::kInstanceTypeOffset));
4561 __ cmp(possible_object, Operand(FIRST_JS_OBJECT_TYPE));
ager@chromium.org6141cbe2009-11-20 12:14:52 +00004562 false_target()->Branch(lt);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004563 __ cmp(possible_object, Operand(LAST_JS_OBJECT_TYPE));
ager@chromium.org6141cbe2009-11-20 12:14:52 +00004564 cc_reg_ = le;
4565}
4566
4567
4568void CodeGenerator::GenerateIsFunction(ZoneList<Expression*>* args) {
4569 // This generates a fast version of:
4570 // (%_ClassOf(arg) === 'Function')
ager@chromium.org6141cbe2009-11-20 12:14:52 +00004571 ASSERT(args->length() == 1);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004572 Load(args->at(0));
4573 Register possible_function = frame_->PopToRegister();
4574 __ tst(possible_function, Operand(kSmiTagMask));
ager@chromium.org6141cbe2009-11-20 12:14:52 +00004575 false_target()->Branch(eq);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004576 Register map_reg = VirtualFrame::scratch0();
4577 Register scratch = VirtualFrame::scratch1();
4578 __ CompareObjectType(possible_function, map_reg, scratch, JS_FUNCTION_TYPE);
ager@chromium.org6141cbe2009-11-20 12:14:52 +00004579 cc_reg_ = eq;
4580}
4581
4582
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004583void CodeGenerator::GenerateIsUndetectableObject(ZoneList<Expression*>* args) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004584 ASSERT(args->length() == 1);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004585 Load(args->at(0));
4586 Register possible_undetectable = frame_->PopToRegister();
4587 __ tst(possible_undetectable, Operand(kSmiTagMask));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004588 false_target()->Branch(eq);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004589 Register scratch = VirtualFrame::scratch0();
4590 __ ldr(scratch,
4591 FieldMemOperand(possible_undetectable, HeapObject::kMapOffset));
4592 __ ldrb(scratch, FieldMemOperand(scratch, Map::kBitFieldOffset));
4593 __ tst(scratch, Operand(1 << Map::kIsUndetectable));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004594 cc_reg_ = ne;
4595}
4596
4597
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004598void CodeGenerator::GenerateIsConstructCall(ZoneList<Expression*>* args) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004599 ASSERT(args->length() == 0);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00004600
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004601 Register scratch0 = VirtualFrame::scratch0();
4602 Register scratch1 = VirtualFrame::scratch1();
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00004603 // Get the frame pointer for the calling frame.
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004604 __ ldr(scratch0, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00004605
4606 // Skip the arguments adaptor frame if it exists.
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004607 __ ldr(scratch1,
4608 MemOperand(scratch0, StandardFrameConstants::kContextOffset));
4609 __ cmp(scratch1, Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
4610 __ ldr(scratch0,
4611 MemOperand(scratch0, StandardFrameConstants::kCallerFPOffset), eq);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00004612
4613 // Check the marker in the calling frame.
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004614 __ ldr(scratch1,
4615 MemOperand(scratch0, StandardFrameConstants::kMarkerOffset));
4616 __ cmp(scratch1, Operand(Smi::FromInt(StackFrame::CONSTRUCT)));
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00004617 cc_reg_ = eq;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004618}
4619
4620
ager@chromium.org7c537e22008-10-16 08:43:32 +00004621void CodeGenerator::GenerateArgumentsLength(ZoneList<Expression*>* args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004622 ASSERT(args->length() == 0);
4623
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004624 Register tos = frame_->GetTOSRegister();
4625 Register scratch0 = VirtualFrame::scratch0();
4626 Register scratch1 = VirtualFrame::scratch1();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004627
lrn@chromium.org25156de2010-04-06 13:10:27 +00004628 // Check if the calling frame is an arguments adaptor frame.
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004629 __ ldr(scratch0,
4630 MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
4631 __ ldr(scratch1,
4632 MemOperand(scratch0, StandardFrameConstants::kContextOffset));
4633 __ cmp(scratch1, Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
4634
4635 // Get the number of formal parameters.
4636 __ mov(tos, Operand(Smi::FromInt(scope()->num_parameters())), LeaveCC, ne);
lrn@chromium.org25156de2010-04-06 13:10:27 +00004637
4638 // Arguments adaptor case: Read the arguments length from the
4639 // adaptor frame.
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004640 __ ldr(tos,
4641 MemOperand(scratch0, ArgumentsAdaptorFrameConstants::kLengthOffset),
4642 eq);
lrn@chromium.org25156de2010-04-06 13:10:27 +00004643
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004644 frame_->EmitPush(tos);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004645}
4646
4647
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00004648void CodeGenerator::GenerateArguments(ZoneList<Expression*>* args) {
ager@chromium.org357bf652010-04-12 11:30:10 +00004649 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004650 ASSERT(args->length() == 1);
4651
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00004652 // Satisfy contract with ArgumentsAccessStub:
4653 // Load the key into r1 and the formal parameters count into r0.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004654 Load(args->at(0));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004655 frame_->EmitPop(r1);
ager@chromium.org5c838252010-02-19 08:53:10 +00004656 __ mov(r0, Operand(Smi::FromInt(scope()->num_parameters())));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004657
4658 // Call the shared stub to get to arguments[key].
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004659 ArgumentsAccessStub stub(ArgumentsAccessStub::READ_ELEMENT);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004660 frame_->CallStub(&stub, 0);
4661 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004662}
4663
4664
ager@chromium.org357bf652010-04-12 11:30:10 +00004665void CodeGenerator::GenerateRandomHeapNumber(
4666 ZoneList<Expression*>* args) {
4667 VirtualFrame::SpilledScope spilled_scope(frame_);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00004668 ASSERT(args->length() == 0);
ager@chromium.org357bf652010-04-12 11:30:10 +00004669
4670 Label slow_allocate_heapnumber;
4671 Label heapnumber_allocated;
4672
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00004673 __ LoadRoot(r6, Heap::kHeapNumberMapRootIndex);
4674 __ AllocateHeapNumber(r4, r1, r2, r6, &slow_allocate_heapnumber);
ager@chromium.org357bf652010-04-12 11:30:10 +00004675 __ jmp(&heapnumber_allocated);
4676
4677 __ bind(&slow_allocate_heapnumber);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00004678 // To allocate a heap number, and ensure that it is not a smi, we
4679 // call the runtime function FUnaryMinus on 0, returning the double
4680 // -0.0. A new, distinct heap number is returned each time.
ager@chromium.org357bf652010-04-12 11:30:10 +00004681 __ mov(r0, Operand(Smi::FromInt(0)));
4682 __ push(r0);
4683 __ CallRuntime(Runtime::kNumberUnaryMinus, 1);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00004684 __ mov(r4, Operand(r0));
ager@chromium.org357bf652010-04-12 11:30:10 +00004685
4686 __ bind(&heapnumber_allocated);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00004687
4688 // Convert 32 random bits in r0 to 0.(32 random bits) in a double
4689 // by computing:
4690 // ( 1.(20 0s)(32 random bits) x 2^20 ) - (1.0 x 2^20)).
4691 if (CpuFeatures::IsSupported(VFP3)) {
4692 __ PrepareCallCFunction(0, r1);
4693 __ CallCFunction(ExternalReference::random_uint32_function(), 0);
4694
4695 CpuFeatures::Scope scope(VFP3);
4696 // 0x41300000 is the top half of 1.0 x 2^20 as a double.
4697 // Create this constant using mov/orr to avoid PC relative load.
4698 __ mov(r1, Operand(0x41000000));
4699 __ orr(r1, r1, Operand(0x300000));
4700 // Move 0x41300000xxxxxxxx (x = random bits) to VFP.
4701 __ vmov(d7, r0, r1);
4702 // Move 0x4130000000000000 to VFP.
4703 __ mov(r0, Operand(0));
4704 __ vmov(d8, r0, r1);
4705 // Subtract and store the result in the heap number.
4706 __ vsub(d7, d7, d8);
4707 __ sub(r0, r4, Operand(kHeapObjectTag));
4708 __ vstr(d7, r0, HeapNumber::kValueOffset);
4709 frame_->EmitPush(r4);
4710 } else {
4711 __ mov(r0, Operand(r4));
4712 __ PrepareCallCFunction(1, r1);
4713 __ CallCFunction(
4714 ExternalReference::fill_heap_number_with_random_function(), 1);
4715 frame_->EmitPush(r0);
4716 }
ager@chromium.orgeadaf222009-06-16 09:43:10 +00004717}
4718
4719
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00004720void CodeGenerator::GenerateStringAdd(ZoneList<Expression*>* args) {
4721 ASSERT_EQ(2, args->length());
4722
4723 Load(args->at(0));
4724 Load(args->at(1));
4725
ager@chromium.org5c838252010-02-19 08:53:10 +00004726 StringAddStub stub(NO_STRING_ADD_FLAGS);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004727 frame_->SpillAll();
ager@chromium.org5c838252010-02-19 08:53:10 +00004728 frame_->CallStub(&stub, 2);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00004729 frame_->EmitPush(r0);
4730}
4731
4732
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004733void CodeGenerator::GenerateSubString(ZoneList<Expression*>* args) {
4734 ASSERT_EQ(3, args->length());
4735
4736 Load(args->at(0));
4737 Load(args->at(1));
4738 Load(args->at(2));
4739
ager@chromium.org5c838252010-02-19 08:53:10 +00004740 SubStringStub stub;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004741 frame_->SpillAll();
ager@chromium.org5c838252010-02-19 08:53:10 +00004742 frame_->CallStub(&stub, 3);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004743 frame_->EmitPush(r0);
4744}
4745
4746
4747void CodeGenerator::GenerateStringCompare(ZoneList<Expression*>* args) {
4748 ASSERT_EQ(2, args->length());
4749
4750 Load(args->at(0));
4751 Load(args->at(1));
4752
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004753 StringCompareStub stub;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004754 frame_->SpillAll();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004755 frame_->CallStub(&stub, 2);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004756 frame_->EmitPush(r0);
4757}
4758
4759
4760void CodeGenerator::GenerateRegExpExec(ZoneList<Expression*>* args) {
4761 ASSERT_EQ(4, args->length());
4762
4763 Load(args->at(0));
4764 Load(args->at(1));
4765 Load(args->at(2));
4766 Load(args->at(3));
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00004767 RegExpExecStub stub;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004768 frame_->SpillAll();
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00004769 frame_->CallStub(&stub, 4);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004770 frame_->EmitPush(r0);
4771}
4772
4773
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00004774void CodeGenerator::GenerateRegExpConstructResult(ZoneList<Expression*>* args) {
4775 // No stub. This code only occurs a few times in regexp.js.
4776 const int kMaxInlineLength = 100;
4777 ASSERT_EQ(3, args->length());
4778 Load(args->at(0)); // Size of array, smi.
4779 Load(args->at(1)); // "index" property value.
4780 Load(args->at(2)); // "input" property value.
4781 {
4782 VirtualFrame::SpilledScope spilled_scope(frame_);
4783 Label slowcase;
4784 Label done;
4785 __ ldr(r1, MemOperand(sp, kPointerSize * 2));
4786 STATIC_ASSERT(kSmiTag == 0);
4787 STATIC_ASSERT(kSmiTagSize == 1);
4788 __ tst(r1, Operand(kSmiTagMask));
4789 __ b(ne, &slowcase);
4790 __ cmp(r1, Operand(Smi::FromInt(kMaxInlineLength)));
4791 __ b(hi, &slowcase);
4792 // Smi-tagging is equivalent to multiplying by 2.
4793 // Allocate RegExpResult followed by FixedArray with size in ebx.
4794 // JSArray: [Map][empty properties][Elements][Length-smi][index][input]
4795 // Elements: [Map][Length][..elements..]
4796 // Size of JSArray with two in-object properties and the header of a
4797 // FixedArray.
4798 int objects_size =
4799 (JSRegExpResult::kSize + FixedArray::kHeaderSize) / kPointerSize;
4800 __ mov(r5, Operand(r1, LSR, kSmiTagSize + kSmiShiftSize));
4801 __ add(r2, r5, Operand(objects_size));
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00004802 __ AllocateInNewSpace(
4803 r2, // In: Size, in words.
4804 r0, // Out: Start of allocation (tagged).
4805 r3, // Scratch register.
4806 r4, // Scratch register.
4807 &slowcase,
4808 static_cast<AllocationFlags>(TAG_OBJECT | SIZE_IN_WORDS));
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00004809 // r0: Start of allocated area, object-tagged.
4810 // r1: Number of elements in array, as smi.
4811 // r5: Number of elements, untagged.
4812
4813 // Set JSArray map to global.regexp_result_map().
4814 // Set empty properties FixedArray.
4815 // Set elements to point to FixedArray allocated right after the JSArray.
4816 // Interleave operations for better latency.
4817 __ ldr(r2, ContextOperand(cp, Context::GLOBAL_INDEX));
4818 __ add(r3, r0, Operand(JSRegExpResult::kSize));
4819 __ mov(r4, Operand(Factory::empty_fixed_array()));
4820 __ ldr(r2, FieldMemOperand(r2, GlobalObject::kGlobalContextOffset));
4821 __ str(r3, FieldMemOperand(r0, JSObject::kElementsOffset));
4822 __ ldr(r2, ContextOperand(r2, Context::REGEXP_RESULT_MAP_INDEX));
4823 __ str(r4, FieldMemOperand(r0, JSObject::kPropertiesOffset));
4824 __ str(r2, FieldMemOperand(r0, HeapObject::kMapOffset));
4825
4826 // Set input, index and length fields from arguments.
4827 __ ldm(ia_w, sp, static_cast<RegList>(r2.bit() | r4.bit()));
4828 __ str(r1, FieldMemOperand(r0, JSArray::kLengthOffset));
4829 __ add(sp, sp, Operand(kPointerSize));
4830 __ str(r4, FieldMemOperand(r0, JSRegExpResult::kIndexOffset));
4831 __ str(r2, FieldMemOperand(r0, JSRegExpResult::kInputOffset));
4832
4833 // Fill out the elements FixedArray.
4834 // r0: JSArray, tagged.
4835 // r3: FixedArray, tagged.
4836 // r5: Number of elements in array, untagged.
4837
4838 // Set map.
4839 __ mov(r2, Operand(Factory::fixed_array_map()));
4840 __ str(r2, FieldMemOperand(r3, HeapObject::kMapOffset));
4841 // Set FixedArray length.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004842 __ mov(r6, Operand(r5, LSL, kSmiTagSize));
4843 __ str(r6, FieldMemOperand(r3, FixedArray::kLengthOffset));
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00004844 // Fill contents of fixed-array with the-hole.
4845 __ mov(r2, Operand(Factory::the_hole_value()));
4846 __ add(r3, r3, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
4847 // Fill fixed array elements with hole.
4848 // r0: JSArray, tagged.
4849 // r2: the hole.
4850 // r3: Start of elements in FixedArray.
4851 // r5: Number of elements to fill.
4852 Label loop;
4853 __ tst(r5, Operand(r5));
4854 __ bind(&loop);
4855 __ b(le, &done); // Jump if r1 is negative or zero.
4856 __ sub(r5, r5, Operand(1), SetCC);
4857 __ str(r2, MemOperand(r3, r5, LSL, kPointerSizeLog2));
4858 __ jmp(&loop);
4859
4860 __ bind(&slowcase);
4861 __ CallRuntime(Runtime::kRegExpConstructResult, 3);
4862
4863 __ bind(&done);
4864 }
4865 frame_->Forget(3);
4866 frame_->EmitPush(r0);
4867}
4868
4869
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00004870class DeferredSearchCache: public DeferredCode {
4871 public:
4872 DeferredSearchCache(Register dst, Register cache, Register key)
4873 : dst_(dst), cache_(cache), key_(key) {
4874 set_comment("[ DeferredSearchCache");
4875 }
4876
4877 virtual void Generate();
4878
4879 private:
4880 Register dst_, cache_, key_;
4881};
4882
4883
4884void DeferredSearchCache::Generate() {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00004885 __ Push(cache_, key_);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00004886 __ CallRuntime(Runtime::kGetFromCache, 2);
4887 if (!dst_.is(r0)) {
4888 __ mov(dst_, r0);
4889 }
4890}
4891
4892
4893void CodeGenerator::GenerateGetFromCache(ZoneList<Expression*>* args) {
4894 ASSERT_EQ(2, args->length());
4895
4896 ASSERT_NE(NULL, args->at(0)->AsLiteral());
4897 int cache_id = Smi::cast(*(args->at(0)->AsLiteral()->handle()))->value();
4898
4899 Handle<FixedArray> jsfunction_result_caches(
4900 Top::global_context()->jsfunction_result_caches());
4901 if (jsfunction_result_caches->length() <= cache_id) {
4902 __ Abort("Attempt to use undefined cache.");
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004903 frame_->EmitPushRoot(Heap::kUndefinedValueRootIndex);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00004904 return;
4905 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00004906
4907 Load(args->at(1));
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004908
4909 VirtualFrame::SpilledScope spilled_scope(frame_);
4910
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00004911 frame_->EmitPop(r2);
4912
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00004913 __ ldr(r1, ContextOperand(cp, Context::GLOBAL_INDEX));
4914 __ ldr(r1, FieldMemOperand(r1, GlobalObject::kGlobalContextOffset));
4915 __ ldr(r1, ContextOperand(r1, Context::JSFUNCTION_RESULT_CACHES_INDEX));
4916 __ ldr(r1, FieldMemOperand(r1, FixedArray::OffsetOfElementAt(cache_id)));
4917
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00004918 DeferredSearchCache* deferred = new DeferredSearchCache(r0, r1, r2);
4919
4920 const int kFingerOffset =
4921 FixedArray::OffsetOfElementAt(JSFunctionResultCache::kFingerIndex);
4922 ASSERT(kSmiTag == 0 && kSmiTagSize == 1);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00004923 __ ldr(r0, FieldMemOperand(r1, kFingerOffset));
4924 // r0 now holds finger offset as a smi.
4925 __ add(r3, r1, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
4926 // r3 now points to the start of fixed array elements.
4927 __ ldr(r0, MemOperand(r3, r0, LSL, kPointerSizeLog2 - kSmiTagSize, PreIndex));
4928 // Note side effect of PreIndex: r3 now points to the key of the pair.
4929 __ cmp(r2, r0);
4930 deferred->Branch(ne);
4931
4932 __ ldr(r0, MemOperand(r3, kPointerSize));
4933
4934 deferred->BindExit();
4935 frame_->EmitPush(r0);
4936}
4937
4938
ager@chromium.org5c838252010-02-19 08:53:10 +00004939void CodeGenerator::GenerateNumberToString(ZoneList<Expression*>* args) {
4940 ASSERT_EQ(args->length(), 1);
4941
4942 // Load the argument on the stack and jump to the runtime.
4943 Load(args->at(0));
4944
fschneider@chromium.org086aac62010-03-17 13:18:24 +00004945 NumberToStringStub stub;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004946 frame_->SpillAll();
fschneider@chromium.org086aac62010-03-17 13:18:24 +00004947 frame_->CallStub(&stub, 1);
ager@chromium.org5c838252010-02-19 08:53:10 +00004948 frame_->EmitPush(r0);
4949}
4950
4951
ager@chromium.orgac091b72010-05-05 07:34:42 +00004952class DeferredSwapElements: public DeferredCode {
4953 public:
4954 DeferredSwapElements(Register object, Register index1, Register index2)
4955 : object_(object), index1_(index1), index2_(index2) {
4956 set_comment("[ DeferredSwapElements");
4957 }
4958
4959 virtual void Generate();
4960
4961 private:
4962 Register object_, index1_, index2_;
4963};
4964
4965
4966void DeferredSwapElements::Generate() {
4967 __ push(object_);
4968 __ push(index1_);
4969 __ push(index2_);
4970 __ CallRuntime(Runtime::kSwapElements, 3);
4971}
4972
4973
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00004974void CodeGenerator::GenerateSwapElements(ZoneList<Expression*>* args) {
4975 Comment cmnt(masm_, "[ GenerateSwapElements");
4976
4977 ASSERT_EQ(3, args->length());
4978
4979 Load(args->at(0));
4980 Load(args->at(1));
4981 Load(args->at(2));
4982
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004983 VirtualFrame::SpilledScope spilled_scope(frame_);
4984
ager@chromium.orgac091b72010-05-05 07:34:42 +00004985 Register index2 = r2;
4986 Register index1 = r1;
4987 Register object = r0;
4988 Register tmp1 = r3;
4989 Register tmp2 = r4;
4990
4991 frame_->EmitPop(index2);
4992 frame_->EmitPop(index1);
4993 frame_->EmitPop(object);
4994
4995 DeferredSwapElements* deferred =
4996 new DeferredSwapElements(object, index1, index2);
4997
4998 // Fetch the map and check if array is in fast case.
4999 // Check that object doesn't require security checks and
5000 // has no indexed interceptor.
5001 __ CompareObjectType(object, tmp1, tmp2, FIRST_JS_OBJECT_TYPE);
5002 deferred->Branch(lt);
5003 __ ldrb(tmp2, FieldMemOperand(tmp1, Map::kBitFieldOffset));
5004 __ tst(tmp2, Operand(KeyedLoadIC::kSlowCaseBitFieldMask));
5005 deferred->Branch(nz);
5006
5007 // Check the object's elements are in fast case.
5008 __ ldr(tmp1, FieldMemOperand(object, JSObject::kElementsOffset));
5009 __ ldr(tmp2, FieldMemOperand(tmp1, HeapObject::kMapOffset));
5010 __ LoadRoot(ip, Heap::kFixedArrayMapRootIndex);
5011 __ cmp(tmp2, ip);
5012 deferred->Branch(ne);
5013
5014 // Smi-tagging is equivalent to multiplying by 2.
5015 STATIC_ASSERT(kSmiTag == 0);
5016 STATIC_ASSERT(kSmiTagSize == 1);
5017
5018 // Check that both indices are smis.
5019 __ mov(tmp2, index1);
5020 __ orr(tmp2, tmp2, index2);
5021 __ tst(tmp2, Operand(kSmiTagMask));
5022 deferred->Branch(nz);
5023
5024 // Bring the offsets into the fixed array in tmp1 into index1 and
5025 // index2.
5026 __ mov(tmp2, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
5027 __ add(index1, tmp2, Operand(index1, LSL, kPointerSizeLog2 - kSmiTagSize));
5028 __ add(index2, tmp2, Operand(index2, LSL, kPointerSizeLog2 - kSmiTagSize));
5029
5030 // Swap elements.
5031 Register tmp3 = object;
5032 object = no_reg;
5033 __ ldr(tmp3, MemOperand(tmp1, index1));
5034 __ ldr(tmp2, MemOperand(tmp1, index2));
5035 __ str(tmp3, MemOperand(tmp1, index2));
5036 __ str(tmp2, MemOperand(tmp1, index1));
5037
5038 Label done;
5039 __ InNewSpace(tmp1, tmp2, eq, &done);
5040 // Possible optimization: do a check that both values are Smis
5041 // (or them and test against Smi mask.)
5042
5043 __ mov(tmp2, tmp1);
5044 RecordWriteStub recordWrite1(tmp1, index1, tmp3);
5045 __ CallStub(&recordWrite1);
5046
5047 RecordWriteStub recordWrite2(tmp2, index2, tmp3);
5048 __ CallStub(&recordWrite2);
5049
5050 __ bind(&done);
5051
5052 deferred->BindExit();
5053 __ LoadRoot(tmp1, Heap::kUndefinedValueRootIndex);
5054 frame_->EmitPush(tmp1);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00005055}
5056
5057
ager@chromium.org357bf652010-04-12 11:30:10 +00005058void CodeGenerator::GenerateCallFunction(ZoneList<Expression*>* args) {
5059 Comment cmnt(masm_, "[ GenerateCallFunction");
5060
5061 ASSERT(args->length() >= 2);
5062
5063 int n_args = args->length() - 2; // for receiver and function.
5064 Load(args->at(0)); // receiver
5065 for (int i = 0; i < n_args; i++) {
5066 Load(args->at(i + 1));
5067 }
5068 Load(args->at(n_args + 1)); // function
5069 frame_->CallJSFunction(n_args);
5070 frame_->EmitPush(r0);
5071}
5072
5073
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005074void CodeGenerator::GenerateMathSin(ZoneList<Expression*>* args) {
5075 ASSERT_EQ(args->length(), 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005076 Load(args->at(0));
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00005077 if (CpuFeatures::IsSupported(VFP3)) {
5078 TranscendentalCacheStub stub(TranscendentalCache::SIN);
5079 frame_->SpillAllButCopyTOSToR0();
5080 frame_->CallStub(&stub, 1);
5081 } else {
5082 frame_->CallRuntime(Runtime::kMath_sin, 1);
5083 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005084 frame_->EmitPush(r0);
5085}
5086
5087
5088void CodeGenerator::GenerateMathCos(ZoneList<Expression*>* args) {
5089 ASSERT_EQ(args->length(), 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005090 Load(args->at(0));
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00005091 if (CpuFeatures::IsSupported(VFP3)) {
5092 TranscendentalCacheStub stub(TranscendentalCache::COS);
5093 frame_->SpillAllButCopyTOSToR0();
5094 frame_->CallStub(&stub, 1);
5095 } else {
5096 frame_->CallRuntime(Runtime::kMath_cos, 1);
5097 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005098 frame_->EmitPush(r0);
5099}
5100
5101
ager@chromium.org7c537e22008-10-16 08:43:32 +00005102void CodeGenerator::GenerateObjectEquals(ZoneList<Expression*>* args) {
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005103 ASSERT(args->length() == 2);
5104
5105 // Load the two objects into registers and perform the comparison.
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00005106 Load(args->at(0));
5107 Load(args->at(1));
5108 Register lhs = frame_->PopToRegister();
5109 Register rhs = frame_->PopToRegister(lhs);
5110 __ cmp(lhs, rhs);
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005111 cc_reg_ = eq;
5112}
5113
5114
ager@chromium.org7c537e22008-10-16 08:43:32 +00005115void CodeGenerator::VisitCallRuntime(CallRuntime* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005116#ifdef DEBUG
5117 int original_height = frame_->height();
5118#endif
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005119 if (CheckForInlineRuntimeCall(node)) {
5120 ASSERT((has_cc() && frame_->height() == original_height) ||
5121 (!has_cc() && frame_->height() == original_height + 1));
5122 return;
5123 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005124
5125 ZoneList<Expression*>* args = node->arguments();
5126 Comment cmnt(masm_, "[ CallRuntime");
5127 Runtime::Function* function = node->function();
5128
ager@chromium.org41826e72009-03-30 13:30:57 +00005129 if (function == NULL) {
mads.s.ager31e71382008-08-13 09:32:07 +00005130 // Prepare stack for calling JS runtime function.
mads.s.ager31e71382008-08-13 09:32:07 +00005131 // Push the builtins object found in the current global object.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005132 Register scratch = VirtualFrame::scratch0();
5133 __ ldr(scratch, GlobalObject());
5134 Register builtins = frame_->GetTOSRegister();
5135 __ ldr(builtins, FieldMemOperand(scratch, GlobalObject::kBuiltinsOffset));
5136 frame_->EmitPush(builtins);
ager@chromium.org41826e72009-03-30 13:30:57 +00005137 }
mads.s.ager31e71382008-08-13 09:32:07 +00005138
ager@chromium.org41826e72009-03-30 13:30:57 +00005139 // Push the arguments ("left-to-right").
5140 int arg_count = args->length();
5141 for (int i = 0; i < arg_count; i++) {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005142 Load(args->at(i));
ager@chromium.org41826e72009-03-30 13:30:57 +00005143 }
mads.s.ager31e71382008-08-13 09:32:07 +00005144
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005145 VirtualFrame::SpilledScope spilled_scope(frame_);
5146
ager@chromium.org41826e72009-03-30 13:30:57 +00005147 if (function == NULL) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005148 // Call the JS runtime function.
ager@chromium.org5c838252010-02-19 08:53:10 +00005149 __ mov(r2, Operand(node->name()));
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005150 InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP;
5151 Handle<Code> stub = ComputeCallInitialize(arg_count, in_loop);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005152 frame_->CallCodeObject(stub, RelocInfo::CODE_TARGET, arg_count + 1);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005153 __ ldr(cp, frame_->Context());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005154 frame_->EmitPush(r0);
ager@chromium.org41826e72009-03-30 13:30:57 +00005155 } else {
5156 // Call the C runtime function.
5157 frame_->CallRuntime(function, arg_count);
5158 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005159 }
ager@chromium.orgac091b72010-05-05 07:34:42 +00005160 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005161}
5162
5163
ager@chromium.org7c537e22008-10-16 08:43:32 +00005164void CodeGenerator::VisitUnaryOperation(UnaryOperation* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005165#ifdef DEBUG
5166 int original_height = frame_->height();
5167#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005168 Comment cmnt(masm_, "[ UnaryOperation");
5169
5170 Token::Value op = node->op();
5171
5172 if (op == Token::NOT) {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005173 LoadCondition(node->expression(), false_target(), true_target(), true);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00005174 // LoadCondition may (and usually does) leave a test and branch to
5175 // be emitted by the caller. In that case, negate the condition.
5176 if (has_cc()) cc_reg_ = NegateCondition(cc_reg_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005177
5178 } else if (op == Token::DELETE) {
5179 Property* property = node->expression()->AsProperty();
mads.s.ager31e71382008-08-13 09:32:07 +00005180 Variable* variable = node->expression()->AsVariableProxy()->AsVariable();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005181 if (property != NULL) {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005182 Load(property->obj());
5183 Load(property->key());
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00005184 frame_->InvokeBuiltin(Builtins::DELETE, CALL_JS, 2);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005185 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005186
mads.s.ager31e71382008-08-13 09:32:07 +00005187 } else if (variable != NULL) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005188 Slot* slot = variable->slot();
5189 if (variable->is_global()) {
5190 LoadGlobal();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005191 frame_->EmitPush(Operand(variable->name()));
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00005192 frame_->InvokeBuiltin(Builtins::DELETE, CALL_JS, 2);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005193 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005194
5195 } else if (slot != NULL && slot->type() == Slot::LOOKUP) {
5196 // lookup the context holding the named variable
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005197 frame_->EmitPush(cp);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005198 frame_->EmitPush(Operand(variable->name()));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005199 frame_->CallRuntime(Runtime::kLookupContext, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005200 // r0: context
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005201 frame_->EmitPush(r0);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005202 frame_->EmitPush(Operand(variable->name()));
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00005203 frame_->InvokeBuiltin(Builtins::DELETE, CALL_JS, 2);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005204 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005205
mads.s.ager31e71382008-08-13 09:32:07 +00005206 } else {
5207 // Default: Result of deleting non-global, not dynamically
5208 // introduced variables is false.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005209 frame_->EmitPushRoot(Heap::kFalseValueRootIndex);
mads.s.ager31e71382008-08-13 09:32:07 +00005210 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005211
5212 } else {
5213 // Default: Result of deleting expressions is true.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005214 Load(node->expression()); // may have side-effects
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005215 frame_->Drop();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005216 frame_->EmitPushRoot(Heap::kTrueValueRootIndex);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005217 }
5218
5219 } else if (op == Token::TYPEOF) {
5220 // Special case for loading the typeof expression; see comment on
5221 // LoadTypeofExpression().
5222 LoadTypeofExpression(node->expression());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005223 frame_->CallRuntime(Runtime::kTypeof, 1);
5224 frame_->EmitPush(r0); // r0 has result
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005225
5226 } else {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005227 bool overwrite =
5228 (node->expression()->AsBinaryOperation() != NULL &&
5229 node->expression()->AsBinaryOperation()->ResultOverwriteAllowed());
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005230 Load(node->expression());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005231 switch (op) {
5232 case Token::NOT:
5233 case Token::DELETE:
5234 case Token::TYPEOF:
5235 UNREACHABLE(); // handled above
5236 break;
5237
5238 case Token::SUB: {
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00005239 frame_->PopToR0();
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005240 GenericUnaryOpStub stub(Token::SUB, overwrite);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005241 frame_->CallStub(&stub, 0);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005242 frame_->EmitPush(r0); // r0 has result
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005243 break;
5244 }
5245
5246 case Token::BIT_NOT: {
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00005247 Register tos = frame_->PopToRegister();
5248 JumpTarget not_smi_label;
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005249 JumpTarget continue_label;
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00005250 // Smi check.
5251 __ tst(tos, Operand(kSmiTagMask));
5252 not_smi_label.Branch(ne);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005253
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00005254 __ mvn(tos, Operand(tos));
5255 __ bic(tos, tos, Operand(kSmiTagMask)); // Bit-clear inverted smi-tag.
5256 frame_->EmitPush(tos);
5257 // The fast case is the first to jump to the continue label, so it gets
5258 // to decide the virtual frame layout.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005259 continue_label.Jump();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005260
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00005261 not_smi_label.Bind();
5262 frame_->SpillAll();
5263 __ Move(r0, tos);
5264 GenericUnaryOpStub stub(Token::BIT_NOT, overwrite);
5265 frame_->CallStub(&stub, 0);
5266 frame_->EmitPush(r0);
5267
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005268 continue_label.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005269 break;
5270 }
5271
5272 case Token::VOID:
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005273 frame_->Drop();
5274 frame_->EmitPushRoot(Heap::kUndefinedValueRootIndex);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005275 break;
5276
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005277 case Token::ADD: {
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00005278 Register tos = frame_->Peek();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005279 // Smi check.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005280 JumpTarget continue_label;
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00005281 __ tst(tos, Operand(kSmiTagMask));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005282 continue_label.Branch(eq);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00005283
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00005284 frame_->InvokeBuiltin(Builtins::TO_NUMBER, CALL_JS, 1);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00005285 frame_->EmitPush(r0);
5286
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005287 continue_label.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005288 break;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005289 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005290 default:
5291 UNREACHABLE();
5292 }
5293 }
kasperl@chromium.orge959c182009-07-27 08:59:04 +00005294 ASSERT(!has_valid_frame() ||
5295 (has_cc() && frame_->height() == original_height) ||
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005296 (!has_cc() && frame_->height() == original_height + 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005297}
5298
5299
ager@chromium.org7c537e22008-10-16 08:43:32 +00005300void CodeGenerator::VisitCountOperation(CountOperation* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005301#ifdef DEBUG
5302 int original_height = frame_->height();
5303#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005304 Comment cmnt(masm_, "[ CountOperation");
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00005305 VirtualFrame::RegisterAllocationScope scope(this);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005306
5307 bool is_postfix = node->is_postfix();
5308 bool is_increment = node->op() == Token::INC;
5309
5310 Variable* var = node->expression()->AsVariableProxy()->AsVariable();
5311 bool is_const = (var != NULL && var->mode() == Variable::CONST);
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00005312 bool is_slot = (var != NULL && var->mode() == Variable::VAR);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005313
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00005314 if (!is_const && is_slot && type_info(var->slot()).IsSmi()) {
5315 // The type info declares that this variable is always a Smi. That
5316 // means it is a Smi both before and after the increment/decrement.
5317 // Lets make use of that to make a very minimal count.
5318 Reference target(this, node->expression(), !is_const);
5319 ASSERT(!target.is_illegal());
5320 target.GetValue(); // Pushes the value.
5321 Register value = frame_->PopToRegister();
5322 if (is_postfix) frame_->EmitPush(value);
5323 if (is_increment) {
5324 __ add(value, value, Operand(Smi::FromInt(1)));
5325 } else {
5326 __ sub(value, value, Operand(Smi::FromInt(1)));
5327 }
5328 frame_->EmitPush(value);
5329 target.SetValue(NOT_CONST_INIT);
5330 if (is_postfix) frame_->Pop();
5331 ASSERT_EQ(original_height + 1, frame_->height());
5332 return;
5333 }
5334
5335 // If it's a postfix expression and its result is not ignored and the
5336 // reference is non-trivial, then push a placeholder on the stack now
5337 // to hold the result of the expression.
5338 bool placeholder_pushed = false;
5339 if (!is_slot && is_postfix) {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005340 frame_->EmitPush(Operand(Smi::FromInt(0)));
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00005341 placeholder_pushed = true;
mads.s.ager31e71382008-08-13 09:32:07 +00005342 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005343
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005344 // A constant reference is not saved to, so a constant reference is not a
5345 // compound assignment reference.
5346 { Reference target(this, node->expression(), !is_const);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005347 if (target.is_illegal()) {
5348 // Spoof the virtual frame to have the expected height (one higher
5349 // than on entry).
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00005350 if (!placeholder_pushed) frame_->EmitPush(Operand(Smi::FromInt(0)));
ager@chromium.orgac091b72010-05-05 07:34:42 +00005351 ASSERT_EQ(original_height + 1, frame_->height());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005352 return;
5353 }
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00005354
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005355 // This pushes 0, 1 or 2 words on the object to be used later when updating
5356 // the target. It also pushes the current value of the target.
ager@chromium.org357bf652010-04-12 11:30:10 +00005357 target.GetValue();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005358
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005359 JumpTarget slow;
5360 JumpTarget exit;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005361
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005362 Register value = frame_->PopToRegister();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005363
5364 // Postfix: Store the old value as the result.
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00005365 if (placeholder_pushed) {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005366 frame_->SetElementAt(value, target.size());
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00005367 } else if (is_postfix) {
5368 frame_->EmitPush(value);
5369 __ mov(VirtualFrame::scratch0(), value);
5370 value = VirtualFrame::scratch0();
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005371 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005372
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00005373 // Check for smi operand.
5374 __ tst(value, Operand(kSmiTagMask));
5375 slow.Branch(ne);
5376
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005377 // Perform optimistic increment/decrement.
5378 if (is_increment) {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005379 __ add(value, value, Operand(Smi::FromInt(1)), SetCC);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005380 } else {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005381 __ sub(value, value, Operand(Smi::FromInt(1)), SetCC);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005382 }
5383
5384 // If the increment/decrement didn't overflow, we're done.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005385 exit.Branch(vc);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005386
5387 // Revert optimistic increment/decrement.
5388 if (is_increment) {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005389 __ sub(value, value, Operand(Smi::FromInt(1)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005390 } else {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005391 __ add(value, value, Operand(Smi::FromInt(1)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005392 }
5393
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005394 // Slow case: Convert to number. At this point the
5395 // value to be incremented is in the value register..
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005396 slow.Bind();
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005397
5398 // Convert the operand to a number.
5399 frame_->EmitPush(value);
5400
kasperl@chromium.org8ccb0be2009-04-07 07:21:39 +00005401 {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005402 VirtualFrame::SpilledScope spilled(frame_);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00005403 frame_->InvokeBuiltin(Builtins::TO_NUMBER, CALL_JS, 1);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005404
5405 if (is_postfix) {
5406 // Postfix: store to result (on the stack).
5407 __ str(r0, frame_->ElementAt(target.size()));
5408 }
5409
5410 // Compute the new value.
5411 frame_->EmitPush(r0);
5412 frame_->EmitPush(Operand(Smi::FromInt(1)));
5413 if (is_increment) {
5414 frame_->CallRuntime(Runtime::kNumberAdd, 2);
5415 } else {
5416 frame_->CallRuntime(Runtime::kNumberSub, 2);
5417 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005418 }
5419
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005420 __ Move(value, r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005421 // Store the new value in the target if not const.
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005422 // At this point the answer is in the value register.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005423 exit.Bind();
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005424 frame_->EmitPush(value);
5425 // Set the target with the result, leaving the result on
5426 // top of the stack. Removes the target from the stack if
5427 // it has a non-zero size.
ager@chromium.org7c537e22008-10-16 08:43:32 +00005428 if (!is_const) target.SetValue(NOT_CONST_INIT);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005429 }
5430
5431 // Postfix: Discard the new value and use the old.
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005432 if (is_postfix) frame_->Pop();
ager@chromium.orgac091b72010-05-05 07:34:42 +00005433 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005434}
5435
5436
ager@chromium.orgb26c50a2010-03-26 09:27:16 +00005437void CodeGenerator::GenerateLogicalBooleanOperation(BinaryOperation* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005438 // According to ECMA-262 section 11.11, page 58, the binary logical
5439 // operators must yield the result of one of the two expressions
5440 // before any ToBoolean() conversions. This means that the value
5441 // produced by a && or || operator is not necessarily a boolean.
5442
5443 // NOTE: If the left hand side produces a materialized value (not in
5444 // the CC register), we force the right hand side to do the
5445 // same. This is necessary because we may have to branch to the exit
5446 // after evaluating the left hand side (due to the shortcut
5447 // semantics), but the compiler must (statically) know if the result
5448 // of compiling the binary operation is materialized or not.
ager@chromium.orgb26c50a2010-03-26 09:27:16 +00005449 if (node->op() == Token::AND) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005450 JumpTarget is_true;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005451 LoadCondition(node->left(), &is_true, false_target(), false);
kasperl@chromium.orge959c182009-07-27 08:59:04 +00005452 if (has_valid_frame() && !has_cc()) {
5453 // The left-hand side result is on top of the virtual frame.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005454 JumpTarget pop_and_continue;
5455 JumpTarget exit;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005456
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00005457 frame_->Dup();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005458 // Avoid popping the result if it converts to 'false' using the
5459 // standard ToBoolean() conversion as described in ECMA-262,
5460 // section 9.2, page 30.
mads.s.ager31e71382008-08-13 09:32:07 +00005461 ToBoolean(&pop_and_continue, &exit);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005462 Branch(false, &exit);
5463
5464 // Pop the result of evaluating the first part.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005465 pop_and_continue.Bind();
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00005466 frame_->Pop();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005467
5468 // Evaluate right side expression.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005469 is_true.Bind();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005470 Load(node->right());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005471
5472 // Exit (always with a materialized value).
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005473 exit.Bind();
kasperl@chromium.orge959c182009-07-27 08:59:04 +00005474 } else if (has_cc() || is_true.is_linked()) {
5475 // The left-hand side is either (a) partially compiled to
5476 // control flow with a final branch left to emit or (b) fully
5477 // compiled to control flow and possibly true.
5478 if (has_cc()) {
5479 Branch(false, false_target());
5480 }
5481 is_true.Bind();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005482 LoadCondition(node->right(), true_target(), false_target(), false);
kasperl@chromium.orge959c182009-07-27 08:59:04 +00005483 } else {
5484 // Nothing to do.
5485 ASSERT(!has_valid_frame() && !has_cc() && !is_true.is_linked());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005486 }
5487
ager@chromium.orgb26c50a2010-03-26 09:27:16 +00005488 } else {
5489 ASSERT(node->op() == Token::OR);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005490 JumpTarget is_false;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005491 LoadCondition(node->left(), true_target(), &is_false, false);
kasperl@chromium.orge959c182009-07-27 08:59:04 +00005492 if (has_valid_frame() && !has_cc()) {
5493 // The left-hand side result is on top of the virtual frame.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005494 JumpTarget pop_and_continue;
5495 JumpTarget exit;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005496
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00005497 frame_->Dup();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005498 // Avoid popping the result if it converts to 'true' using the
5499 // standard ToBoolean() conversion as described in ECMA-262,
5500 // section 9.2, page 30.
mads.s.ager31e71382008-08-13 09:32:07 +00005501 ToBoolean(&exit, &pop_and_continue);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005502 Branch(true, &exit);
5503
5504 // Pop the result of evaluating the first part.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005505 pop_and_continue.Bind();
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00005506 frame_->Pop();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005507
5508 // Evaluate right side expression.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005509 is_false.Bind();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005510 Load(node->right());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005511
5512 // Exit (always with a materialized value).
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005513 exit.Bind();
kasperl@chromium.orge959c182009-07-27 08:59:04 +00005514 } else if (has_cc() || is_false.is_linked()) {
5515 // The left-hand side is either (a) partially compiled to
5516 // control flow with a final branch left to emit or (b) fully
5517 // compiled to control flow and possibly false.
5518 if (has_cc()) {
5519 Branch(true, true_target());
5520 }
5521 is_false.Bind();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005522 LoadCondition(node->right(), true_target(), false_target(), false);
kasperl@chromium.orge959c182009-07-27 08:59:04 +00005523 } else {
5524 // Nothing to do.
5525 ASSERT(!has_valid_frame() && !has_cc() && !is_false.is_linked());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005526 }
ager@chromium.orgb26c50a2010-03-26 09:27:16 +00005527 }
5528}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005529
ager@chromium.orgb26c50a2010-03-26 09:27:16 +00005530
5531void CodeGenerator::VisitBinaryOperation(BinaryOperation* node) {
5532#ifdef DEBUG
5533 int original_height = frame_->height();
5534#endif
ager@chromium.orgb26c50a2010-03-26 09:27:16 +00005535 Comment cmnt(masm_, "[ BinaryOperation");
5536
5537 if (node->op() == Token::AND || node->op() == Token::OR) {
5538 GenerateLogicalBooleanOperation(node);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005539 } else {
5540 // Optimize for the case where (at least) one of the expressions
5541 // is a literal small integer.
5542 Literal* lliteral = node->left()->AsLiteral();
5543 Literal* rliteral = node->right()->AsLiteral();
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005544 // NOTE: The code below assumes that the slow cases (calls to runtime)
5545 // never return a constant/immutable object.
5546 bool overwrite_left =
5547 (node->left()->AsBinaryOperation() != NULL &&
5548 node->left()->AsBinaryOperation()->ResultOverwriteAllowed());
5549 bool overwrite_right =
5550 (node->right()->AsBinaryOperation() != NULL &&
5551 node->right()->AsBinaryOperation()->ResultOverwriteAllowed());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005552
5553 if (rliteral != NULL && rliteral->handle()->IsSmi()) {
ager@chromium.org357bf652010-04-12 11:30:10 +00005554 VirtualFrame::RegisterAllocationScope scope(this);
5555 Load(node->left());
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00005556 if (frame_->KnownSmiAt(0)) overwrite_left = false;
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00005557 SmiOperation(node->op(),
5558 rliteral->handle(),
5559 false,
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00005560 overwrite_left ? OVERWRITE_LEFT : NO_OVERWRITE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005561 } else if (lliteral != NULL && lliteral->handle()->IsSmi()) {
ager@chromium.org357bf652010-04-12 11:30:10 +00005562 VirtualFrame::RegisterAllocationScope scope(this);
5563 Load(node->right());
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00005564 if (frame_->KnownSmiAt(0)) overwrite_right = false;
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00005565 SmiOperation(node->op(),
5566 lliteral->handle(),
5567 true,
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00005568 overwrite_right ? OVERWRITE_RIGHT : NO_OVERWRITE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005569 } else {
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00005570 GenerateInlineSmi inline_smi =
5571 loop_nesting() > 0 ? GENERATE_INLINE_SMI : DONT_GENERATE_INLINE_SMI;
5572 if (lliteral != NULL) {
5573 ASSERT(!lliteral->handle()->IsSmi());
5574 inline_smi = DONT_GENERATE_INLINE_SMI;
5575 }
5576 if (rliteral != NULL) {
5577 ASSERT(!rliteral->handle()->IsSmi());
5578 inline_smi = DONT_GENERATE_INLINE_SMI;
5579 }
ager@chromium.org357bf652010-04-12 11:30:10 +00005580 VirtualFrame::RegisterAllocationScope scope(this);
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005581 OverwriteMode overwrite_mode = NO_OVERWRITE;
5582 if (overwrite_left) {
5583 overwrite_mode = OVERWRITE_LEFT;
5584 } else if (overwrite_right) {
5585 overwrite_mode = OVERWRITE_RIGHT;
5586 }
ager@chromium.org357bf652010-04-12 11:30:10 +00005587 Load(node->left());
5588 Load(node->right());
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00005589 GenericBinaryOperation(node->op(), overwrite_mode, inline_smi);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005590 }
5591 }
kasperl@chromium.orge959c182009-07-27 08:59:04 +00005592 ASSERT(!has_valid_frame() ||
5593 (has_cc() && frame_->height() == original_height) ||
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005594 (!has_cc() && frame_->height() == original_height + 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005595}
5596
5597
ager@chromium.org7c537e22008-10-16 08:43:32 +00005598void CodeGenerator::VisitThisFunction(ThisFunction* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005599#ifdef DEBUG
5600 int original_height = frame_->height();
5601#endif
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00005602 frame_->EmitPush(MemOperand(frame_->Function()));
ager@chromium.orgac091b72010-05-05 07:34:42 +00005603 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005604}
5605
5606
ager@chromium.org7c537e22008-10-16 08:43:32 +00005607void CodeGenerator::VisitCompareOperation(CompareOperation* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005608#ifdef DEBUG
5609 int original_height = frame_->height();
5610#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005611 Comment cmnt(masm_, "[ CompareOperation");
5612
ager@chromium.org357bf652010-04-12 11:30:10 +00005613 VirtualFrame::RegisterAllocationScope nonspilled_scope(this);
5614
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005615 // Get the expressions from the node.
5616 Expression* left = node->left();
5617 Expression* right = node->right();
5618 Token::Value op = node->op();
5619
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005620 // To make null checks efficient, we check if either left or right is the
5621 // literal 'null'. If so, we optimize the code by inlining a null check
5622 // instead of calling the (very) general runtime routine for checking
5623 // equality.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005624 if (op == Token::EQ || op == Token::EQ_STRICT) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005625 bool left_is_null =
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005626 left->AsLiteral() != NULL && left->AsLiteral()->IsNull();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005627 bool right_is_null =
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005628 right->AsLiteral() != NULL && right->AsLiteral()->IsNull();
5629 // The 'null' value can only be equal to 'null' or 'undefined'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005630 if (left_is_null || right_is_null) {
ager@chromium.org357bf652010-04-12 11:30:10 +00005631 Load(left_is_null ? right : left);
5632 Register tos = frame_->PopToRegister();
ager@chromium.orgab99eea2009-08-25 07:05:41 +00005633 __ LoadRoot(ip, Heap::kNullValueRootIndex);
ager@chromium.org357bf652010-04-12 11:30:10 +00005634 __ cmp(tos, ip);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005635
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005636 // The 'null' value is only equal to 'undefined' if using non-strict
5637 // comparisons.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005638 if (op != Token::EQ_STRICT) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005639 true_target()->Branch(eq);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005640
ager@chromium.orgab99eea2009-08-25 07:05:41 +00005641 __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
ager@chromium.org357bf652010-04-12 11:30:10 +00005642 __ cmp(tos, Operand(ip));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005643 true_target()->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005644
ager@chromium.org357bf652010-04-12 11:30:10 +00005645 __ tst(tos, Operand(kSmiTagMask));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005646 false_target()->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005647
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005648 // It can be an undetectable object.
ager@chromium.org357bf652010-04-12 11:30:10 +00005649 __ ldr(tos, FieldMemOperand(tos, HeapObject::kMapOffset));
5650 __ ldrb(tos, FieldMemOperand(tos, Map::kBitFieldOffset));
5651 __ and_(tos, tos, Operand(1 << Map::kIsUndetectable));
5652 __ cmp(tos, Operand(1 << Map::kIsUndetectable));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005653 }
5654
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005655 cc_reg_ = eq;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005656 ASSERT(has_cc() && frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005657 return;
5658 }
5659 }
5660
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005661 // To make typeof testing for natives implemented in JavaScript really
5662 // efficient, we generate special code for expressions of the form:
5663 // 'typeof <expression> == <string>'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005664 UnaryOperation* operation = left->AsUnaryOperation();
5665 if ((op == Token::EQ || op == Token::EQ_STRICT) &&
5666 (operation != NULL && operation->op() == Token::TYPEOF) &&
5667 (right->AsLiteral() != NULL &&
5668 right->AsLiteral()->handle()->IsString())) {
5669 Handle<String> check(String::cast(*right->AsLiteral()->handle()));
5670
ager@chromium.org357bf652010-04-12 11:30:10 +00005671 // Load the operand, move it to a register.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005672 LoadTypeofExpression(operation->expression());
ager@chromium.org357bf652010-04-12 11:30:10 +00005673 Register tos = frame_->PopToRegister();
5674
ager@chromium.org357bf652010-04-12 11:30:10 +00005675 Register scratch = VirtualFrame::scratch0();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005676
5677 if (check->Equals(Heap::number_symbol())) {
ager@chromium.org357bf652010-04-12 11:30:10 +00005678 __ tst(tos, Operand(kSmiTagMask));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005679 true_target()->Branch(eq);
ager@chromium.org357bf652010-04-12 11:30:10 +00005680 __ ldr(tos, FieldMemOperand(tos, HeapObject::kMapOffset));
ager@chromium.orgab99eea2009-08-25 07:05:41 +00005681 __ LoadRoot(ip, Heap::kHeapNumberMapRootIndex);
ager@chromium.org357bf652010-04-12 11:30:10 +00005682 __ cmp(tos, ip);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005683 cc_reg_ = eq;
5684
5685 } else if (check->Equals(Heap::string_symbol())) {
ager@chromium.org357bf652010-04-12 11:30:10 +00005686 __ tst(tos, Operand(kSmiTagMask));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005687 false_target()->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005688
ager@chromium.org357bf652010-04-12 11:30:10 +00005689 __ ldr(tos, FieldMemOperand(tos, HeapObject::kMapOffset));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005690
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005691 // It can be an undetectable string object.
ager@chromium.org357bf652010-04-12 11:30:10 +00005692 __ ldrb(scratch, FieldMemOperand(tos, Map::kBitFieldOffset));
5693 __ and_(scratch, scratch, Operand(1 << Map::kIsUndetectable));
5694 __ cmp(scratch, Operand(1 << Map::kIsUndetectable));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005695 false_target()->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005696
ager@chromium.org357bf652010-04-12 11:30:10 +00005697 __ ldrb(scratch, FieldMemOperand(tos, Map::kInstanceTypeOffset));
5698 __ cmp(scratch, Operand(FIRST_NONSTRING_TYPE));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005699 cc_reg_ = lt;
5700
5701 } else if (check->Equals(Heap::boolean_symbol())) {
ager@chromium.orgab99eea2009-08-25 07:05:41 +00005702 __ LoadRoot(ip, Heap::kTrueValueRootIndex);
ager@chromium.org357bf652010-04-12 11:30:10 +00005703 __ cmp(tos, ip);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005704 true_target()->Branch(eq);
ager@chromium.orgab99eea2009-08-25 07:05:41 +00005705 __ LoadRoot(ip, Heap::kFalseValueRootIndex);
ager@chromium.org357bf652010-04-12 11:30:10 +00005706 __ cmp(tos, ip);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005707 cc_reg_ = eq;
5708
5709 } else if (check->Equals(Heap::undefined_symbol())) {
ager@chromium.orgab99eea2009-08-25 07:05:41 +00005710 __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
ager@chromium.org357bf652010-04-12 11:30:10 +00005711 __ cmp(tos, ip);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005712 true_target()->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005713
ager@chromium.org357bf652010-04-12 11:30:10 +00005714 __ tst(tos, Operand(kSmiTagMask));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005715 false_target()->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005716
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005717 // It can be an undetectable object.
ager@chromium.org357bf652010-04-12 11:30:10 +00005718 __ ldr(tos, FieldMemOperand(tos, HeapObject::kMapOffset));
5719 __ ldrb(scratch, FieldMemOperand(tos, Map::kBitFieldOffset));
5720 __ and_(scratch, scratch, Operand(1 << Map::kIsUndetectable));
5721 __ cmp(scratch, Operand(1 << Map::kIsUndetectable));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005722
5723 cc_reg_ = eq;
5724
5725 } else if (check->Equals(Heap::function_symbol())) {
ager@chromium.org357bf652010-04-12 11:30:10 +00005726 __ tst(tos, Operand(kSmiTagMask));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005727 false_target()->Branch(eq);
ager@chromium.org357bf652010-04-12 11:30:10 +00005728 Register map_reg = scratch;
5729 __ CompareObjectType(tos, map_reg, tos, JS_FUNCTION_TYPE);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005730 true_target()->Branch(eq);
5731 // Regular expressions are callable so typeof == 'function'.
ager@chromium.org357bf652010-04-12 11:30:10 +00005732 __ CompareInstanceType(map_reg, tos, JS_REGEXP_TYPE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005733 cc_reg_ = eq;
5734
5735 } else if (check->Equals(Heap::object_symbol())) {
ager@chromium.org357bf652010-04-12 11:30:10 +00005736 __ tst(tos, Operand(kSmiTagMask));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005737 false_target()->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005738
ager@chromium.orgab99eea2009-08-25 07:05:41 +00005739 __ LoadRoot(ip, Heap::kNullValueRootIndex);
ager@chromium.org357bf652010-04-12 11:30:10 +00005740 __ cmp(tos, ip);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005741 true_target()->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005742
ager@chromium.org357bf652010-04-12 11:30:10 +00005743 Register map_reg = scratch;
5744 __ CompareObjectType(tos, map_reg, tos, JS_REGEXP_TYPE);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005745 false_target()->Branch(eq);
5746
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005747 // It can be an undetectable object.
ager@chromium.org357bf652010-04-12 11:30:10 +00005748 __ ldrb(tos, FieldMemOperand(map_reg, Map::kBitFieldOffset));
5749 __ and_(tos, tos, Operand(1 << Map::kIsUndetectable));
5750 __ cmp(tos, Operand(1 << Map::kIsUndetectable));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005751 false_target()->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005752
ager@chromium.org357bf652010-04-12 11:30:10 +00005753 __ ldrb(tos, FieldMemOperand(map_reg, Map::kInstanceTypeOffset));
5754 __ cmp(tos, Operand(FIRST_JS_OBJECT_TYPE));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005755 false_target()->Branch(lt);
ager@chromium.org357bf652010-04-12 11:30:10 +00005756 __ cmp(tos, Operand(LAST_JS_OBJECT_TYPE));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005757 cc_reg_ = le;
5758
5759 } else {
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005760 // Uncommon case: typeof testing against a string literal that is
5761 // never returned from the typeof operator.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005762 false_target()->Jump();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005763 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005764 ASSERT(!has_valid_frame() ||
5765 (has_cc() && frame_->height() == original_height));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005766 return;
5767 }
5768
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005769 switch (op) {
5770 case Token::EQ:
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005771 Comparison(eq, left, right, false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005772 break;
5773
5774 case Token::LT:
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005775 Comparison(lt, left, right);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005776 break;
5777
5778 case Token::GT:
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005779 Comparison(gt, left, right);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005780 break;
5781
5782 case Token::LTE:
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005783 Comparison(le, left, right);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005784 break;
5785
5786 case Token::GTE:
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005787 Comparison(ge, left, right);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005788 break;
5789
5790 case Token::EQ_STRICT:
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005791 Comparison(eq, left, right, true);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005792 break;
5793
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005794 case Token::IN: {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005795 Load(left);
5796 Load(right);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00005797 frame_->InvokeBuiltin(Builtins::IN, CALL_JS, 2);
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00005798 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005799 break;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005800 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005801
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005802 case Token::INSTANCEOF: {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005803 Load(left);
5804 Load(right);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00005805 InstanceofStub stub;
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00005806 frame_->CallStub(&stub, 2);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00005807 // At this point if instanceof succeeded then r0 == 0.
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00005808 __ tst(r0, Operand(r0));
ager@chromium.org7c537e22008-10-16 08:43:32 +00005809 cc_reg_ = eq;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005810 break;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005811 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005812
5813 default:
5814 UNREACHABLE();
5815 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005816 ASSERT((has_cc() && frame_->height() == original_height) ||
5817 (!has_cc() && frame_->height() == original_height + 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005818}
5819
5820
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005821class DeferredReferenceGetNamedValue: public DeferredCode {
5822 public:
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005823 explicit DeferredReferenceGetNamedValue(Register receiver,
5824 Handle<String> name)
5825 : receiver_(receiver), name_(name) {
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005826 set_comment("[ DeferredReferenceGetNamedValue");
5827 }
5828
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005829 virtual void Generate();
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005830
5831 private:
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005832 Register receiver_;
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005833 Handle<String> name_;
5834};
5835
5836
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005837// Convention for this is that on entry the receiver is in a register that
5838// is not used by the stack. On exit the answer is found in that same
5839// register and the stack has the same height.
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005840void DeferredReferenceGetNamedValue::Generate() {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005841#ifdef DEBUG
5842 int expected_height = frame_state()->frame()->height();
5843#endif
5844 VirtualFrame copied_frame(*frame_state()->frame());
5845 copied_frame.SpillAll();
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005846
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005847 Register scratch1 = VirtualFrame::scratch0();
5848 Register scratch2 = VirtualFrame::scratch1();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005849 ASSERT(!receiver_.is(scratch1) && !receiver_.is(scratch2));
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005850 __ DecrementCounter(&Counters::named_load_inline, 1, scratch1, scratch2);
5851 __ IncrementCounter(&Counters::named_load_inline_miss, 1, scratch1, scratch2);
5852
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005853 // Ensure receiver in r0 and name in r2 to match load ic calling convention.
5854 __ Move(r0, receiver_);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005855 __ mov(r2, Operand(name_));
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005856
5857 // The rest of the instructions in the deferred code must be together.
5858 { Assembler::BlockConstPoolScope block_const_pool(masm_);
5859 Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize));
5860 __ Call(ic, RelocInfo::CODE_TARGET);
5861 // The call must be followed by a nop(1) instruction to indicate that the
5862 // in-object has been inlined.
5863 __ nop(PROPERTY_ACCESS_INLINED);
5864
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005865 // At this point the answer is in r0. We move it to the expected register
5866 // if necessary.
5867 __ Move(receiver_, r0);
5868
5869 // Now go back to the frame that we entered with. This will not overwrite
5870 // the receiver register since that register was not in use when we came
5871 // in. The instructions emitted by this merge are skipped over by the
5872 // inline load patching mechanism when looking for the branch instruction
5873 // that tells it where the code to patch is.
5874 copied_frame.MergeTo(frame_state()->frame());
5875
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005876 // Block the constant pool for one more instruction after leaving this
5877 // constant pool block scope to include the branch instruction ending the
5878 // deferred code.
5879 __ BlockConstPoolFor(1);
5880 }
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005881 ASSERT_EQ(expected_height, frame_state()->frame()->height());
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005882}
5883
5884
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005885class DeferredReferenceGetKeyedValue: public DeferredCode {
5886 public:
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005887 DeferredReferenceGetKeyedValue(Register key, Register receiver)
5888 : key_(key), receiver_(receiver) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005889 set_comment("[ DeferredReferenceGetKeyedValue");
5890 }
5891
5892 virtual void Generate();
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005893
5894 private:
5895 Register key_;
5896 Register receiver_;
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005897};
5898
5899
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00005900// Takes key and register in r0 and r1 or vice versa. Returns result
5901// in r0.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005902void DeferredReferenceGetKeyedValue::Generate() {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005903 ASSERT((key_.is(r0) && receiver_.is(r1)) ||
5904 (key_.is(r1) && receiver_.is(r0)));
5905
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00005906 VirtualFrame copied_frame(*frame_state()->frame());
5907 copied_frame.SpillAll();
5908
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005909 Register scratch1 = VirtualFrame::scratch0();
5910 Register scratch2 = VirtualFrame::scratch1();
5911 __ DecrementCounter(&Counters::keyed_load_inline, 1, scratch1, scratch2);
5912 __ IncrementCounter(&Counters::keyed_load_inline_miss, 1, scratch1, scratch2);
5913
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005914 // Ensure key in r0 and receiver in r1 to match keyed load ic calling
5915 // convention.
5916 if (key_.is(r1)) {
5917 __ Swap(r0, r1, ip);
5918 }
5919
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005920 // The rest of the instructions in the deferred code must be together.
5921 { Assembler::BlockConstPoolScope block_const_pool(masm_);
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00005922 // Call keyed load IC. It has the arguments key and receiver in r0 and r1.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005923 Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
5924 __ Call(ic, RelocInfo::CODE_TARGET);
5925 // The call must be followed by a nop instruction to indicate that the
5926 // keyed load has been inlined.
5927 __ nop(PROPERTY_ACCESS_INLINED);
5928
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00005929 // Now go back to the frame that we entered with. This will not overwrite
5930 // the receiver or key registers since they were not in use when we came
5931 // in. The instructions emitted by this merge are skipped over by the
5932 // inline load patching mechanism when looking for the branch instruction
5933 // that tells it where the code to patch is.
5934 copied_frame.MergeTo(frame_state()->frame());
5935
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005936 // Block the constant pool for one more instruction after leaving this
5937 // constant pool block scope to include the branch instruction ending the
5938 // deferred code.
5939 __ BlockConstPoolFor(1);
5940 }
5941}
5942
5943
5944class DeferredReferenceSetKeyedValue: public DeferredCode {
5945 public:
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005946 DeferredReferenceSetKeyedValue(Register value,
5947 Register key,
5948 Register receiver)
5949 : value_(value), key_(key), receiver_(receiver) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005950 set_comment("[ DeferredReferenceSetKeyedValue");
5951 }
5952
5953 virtual void Generate();
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005954
5955 private:
5956 Register value_;
5957 Register key_;
5958 Register receiver_;
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005959};
5960
5961
5962void DeferredReferenceSetKeyedValue::Generate() {
5963 Register scratch1 = VirtualFrame::scratch0();
5964 Register scratch2 = VirtualFrame::scratch1();
5965 __ DecrementCounter(&Counters::keyed_store_inline, 1, scratch1, scratch2);
5966 __ IncrementCounter(
5967 &Counters::keyed_store_inline_miss, 1, scratch1, scratch2);
5968
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005969 // Ensure value in r0, key in r1 and receiver in r2 to match keyed store ic
5970 // calling convention.
5971 if (value_.is(r1)) {
5972 __ Swap(r0, r1, ip);
5973 }
5974 ASSERT(receiver_.is(r2));
5975
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005976 // The rest of the instructions in the deferred code must be together.
5977 { Assembler::BlockConstPoolScope block_const_pool(masm_);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005978 // Call keyed store IC. It has the arguments value, key and receiver in r0,
5979 // r1 and r2.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005980 Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize));
5981 __ Call(ic, RelocInfo::CODE_TARGET);
5982 // The call must be followed by a nop instruction to indicate that the
5983 // keyed store has been inlined.
5984 __ nop(PROPERTY_ACCESS_INLINED);
5985
5986 // Block the constant pool for one more instruction after leaving this
5987 // constant pool block scope to include the branch instruction ending the
5988 // deferred code.
5989 __ BlockConstPoolFor(1);
5990 }
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005991}
5992
5993
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005994// Consumes the top of stack (the receiver) and pushes the result instead.
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005995void CodeGenerator::EmitNamedLoad(Handle<String> name, bool is_contextual) {
5996 if (is_contextual || scope()->is_global_scope() || loop_nesting() == 0) {
5997 Comment cmnt(masm(), "[ Load from named Property");
5998 // Setup the name register and call load IC.
ager@chromium.orgac091b72010-05-05 07:34:42 +00005999 frame_->CallLoadIC(name,
6000 is_contextual
6001 ? RelocInfo::CODE_TARGET_CONTEXT
6002 : RelocInfo::CODE_TARGET);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006003 frame_->EmitPush(r0); // Push answer.
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006004 } else {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006005 // Inline the in-object property case.
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006006 Comment cmnt(masm(), "[ Inlined named property load");
6007
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006008 // Counter will be decremented in the deferred code. Placed here to avoid
6009 // having it in the instruction stream below where patching will occur.
6010 __ IncrementCounter(&Counters::named_load_inline, 1,
6011 frame_->scratch0(), frame_->scratch1());
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006012
6013 // The following instructions are the inlined load of an in-object property.
6014 // Parts of this code is patched, so the exact instructions generated needs
6015 // to be fixed. Therefore the instruction pool is blocked when generating
6016 // this code
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006017
6018 // Load the receiver from the stack.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006019 Register receiver = frame_->PopToRegister();
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006020
6021 DeferredReferenceGetNamedValue* deferred =
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006022 new DeferredReferenceGetNamedValue(receiver, name);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006023
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006024#ifdef DEBUG
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006025 int kInlinedNamedLoadInstructions = 7;
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006026 Label check_inlined_codesize;
6027 masm_->bind(&check_inlined_codesize);
6028#endif
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006029
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006030 { Assembler::BlockConstPoolScope block_const_pool(masm_);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006031 // Check that the receiver is a heap object.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006032 __ tst(receiver, Operand(kSmiTagMask));
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006033 deferred->Branch(eq);
6034
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006035 Register scratch = VirtualFrame::scratch0();
6036 Register scratch2 = VirtualFrame::scratch1();
6037
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006038 // Check the map. The null map used below is patched by the inline cache
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006039 // code. Therefore we can't use a LoadRoot call.
6040 __ ldr(scratch, FieldMemOperand(receiver, HeapObject::kMapOffset));
6041 __ mov(scratch2, Operand(Factory::null_value()));
6042 __ cmp(scratch, scratch2);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006043 deferred->Branch(ne);
6044
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006045 // Initially use an invalid index. The index will be patched by the
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006046 // inline cache code.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006047 __ ldr(receiver, MemOperand(receiver, 0));
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006048
6049 // Make sure that the expected number of instructions are generated.
6050 ASSERT_EQ(kInlinedNamedLoadInstructions,
6051 masm_->InstructionsGeneratedSince(&check_inlined_codesize));
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006052 }
6053
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006054 deferred->BindExit();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006055 // At this point the receiver register has the result, either from the
6056 // deferred code or from the inlined code.
6057 frame_->EmitPush(receiver);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006058 }
6059}
6060
6061
ager@chromium.orgac091b72010-05-05 07:34:42 +00006062void CodeGenerator::EmitNamedStore(Handle<String> name, bool is_contextual) {
6063#ifdef DEBUG
6064 int expected_height = frame_->height() - (is_contextual ? 1 : 2);
6065#endif
6066 frame_->CallStoreIC(name, is_contextual);
6067
6068 ASSERT_EQ(expected_height, frame_->height());
6069}
6070
6071
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006072void CodeGenerator::EmitKeyedLoad() {
6073 if (loop_nesting() == 0) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006074 Comment cmnt(masm_, "[ Load from keyed property");
6075 frame_->CallKeyedLoadIC();
6076 } else {
6077 // Inline the keyed load.
6078 Comment cmnt(masm_, "[ Inlined load from keyed property");
6079
6080 // Counter will be decremented in the deferred code. Placed here to avoid
6081 // having it in the instruction stream below where patching will occur.
6082 __ IncrementCounter(&Counters::keyed_load_inline, 1,
6083 frame_->scratch0(), frame_->scratch1());
6084
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00006085 // Load the key and receiver from the stack.
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00006086 bool key_is_known_smi = frame_->KnownSmiAt(0);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00006087 Register key = frame_->PopToRegister();
6088 Register receiver = frame_->PopToRegister(key);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006089
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00006090 // The deferred code expects key and receiver in registers.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006091 DeferredReferenceGetKeyedValue* deferred =
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00006092 new DeferredReferenceGetKeyedValue(key, receiver);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006093
6094 // Check that the receiver is a heap object.
6095 __ tst(receiver, Operand(kSmiTagMask));
6096 deferred->Branch(eq);
6097
6098 // The following instructions are the part of the inlined load keyed
6099 // property code which can be patched. Therefore the exact number of
6100 // instructions generated need to be fixed, so the constant pool is blocked
6101 // while generating this code.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006102 { Assembler::BlockConstPoolScope block_const_pool(masm_);
6103 Register scratch1 = VirtualFrame::scratch0();
6104 Register scratch2 = VirtualFrame::scratch1();
6105 // Check the map. The null map used below is patched by the inline cache
6106 // code.
6107 __ ldr(scratch1, FieldMemOperand(receiver, HeapObject::kMapOffset));
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00006108
6109 // Check that the key is a smi.
6110 if (!key_is_known_smi) {
6111 __ tst(key, Operand(kSmiTagMask));
6112 deferred->Branch(ne);
6113 }
6114
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00006115#ifdef DEBUG
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00006116 Label check_inlined_codesize;
6117 masm_->bind(&check_inlined_codesize);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00006118#endif
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006119 __ mov(scratch2, Operand(Factory::null_value()));
6120 __ cmp(scratch1, scratch2);
6121 deferred->Branch(ne);
6122
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006123 // Get the elements array from the receiver and check that it
6124 // is not a dictionary.
6125 __ ldr(scratch1, FieldMemOperand(receiver, JSObject::kElementsOffset));
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00006126 if (FLAG_debug_code) {
6127 __ ldr(scratch2, FieldMemOperand(scratch1, JSObject::kMapOffset));
6128 __ LoadRoot(ip, Heap::kFixedArrayMapRootIndex);
6129 __ cmp(scratch2, ip);
6130 __ Assert(eq, "JSObject with fast elements map has slow elements");
6131 }
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006132
6133 // Check that key is within bounds. Use unsigned comparison to handle
6134 // negative keys.
6135 __ ldr(scratch2, FieldMemOperand(scratch1, FixedArray::kLengthOffset));
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006136 __ cmp(scratch2, key);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006137 deferred->Branch(ls); // Unsigned less equal.
6138
6139 // Load and check that the result is not the hole (key is a smi).
6140 __ LoadRoot(scratch2, Heap::kTheHoleValueRootIndex);
6141 __ add(scratch1,
6142 scratch1,
6143 Operand(FixedArray::kHeaderSize - kHeapObjectTag));
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00006144 __ ldr(scratch1,
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006145 MemOperand(scratch1, key, LSL,
6146 kPointerSizeLog2 - (kSmiTagSize + kSmiShiftSize)));
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00006147 __ cmp(scratch1, scratch2);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006148 deferred->Branch(eq);
6149
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00006150 __ mov(r0, scratch1);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006151 // Make sure that the expected number of instructions are generated.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00006152 ASSERT_EQ(GetInlinedKeyedLoadInstructionsAfterPatch(),
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006153 masm_->InstructionsGeneratedSince(&check_inlined_codesize));
6154 }
6155
6156 deferred->BindExit();
6157 }
6158}
6159
6160
6161void CodeGenerator::EmitKeyedStore(StaticType* key_type) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006162 // Generate inlined version of the keyed store if the code is in a loop
6163 // and the key is likely to be a smi.
6164 if (loop_nesting() > 0 && key_type->IsLikelySmi()) {
6165 // Inline the keyed store.
6166 Comment cmnt(masm_, "[ Inlined store to keyed property");
6167
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006168 Register scratch1 = VirtualFrame::scratch0();
6169 Register scratch2 = VirtualFrame::scratch1();
6170 Register scratch3 = r3;
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006171
6172 // Counter will be decremented in the deferred code. Placed here to avoid
6173 // having it in the instruction stream below where patching will occur.
6174 __ IncrementCounter(&Counters::keyed_store_inline, 1,
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006175 scratch1, scratch2);
6176
6177 // Load the value, key and receiver from the stack.
6178 Register value = frame_->PopToRegister();
6179 Register key = frame_->PopToRegister(value);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00006180 VirtualFrame::SpilledScope spilled(frame_);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006181 Register receiver = r2;
6182 frame_->EmitPop(receiver);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006183
6184 // The deferred code expects value, key and receiver in registers.
6185 DeferredReferenceSetKeyedValue* deferred =
6186 new DeferredReferenceSetKeyedValue(value, key, receiver);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006187
6188 // Check that the value is a smi. As this inlined code does not set the
6189 // write barrier it is only possible to store smi values.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006190 __ tst(value, Operand(kSmiTagMask));
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006191 deferred->Branch(ne);
6192
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006193 // Check that the key is a smi.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006194 __ tst(key, Operand(kSmiTagMask));
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006195 deferred->Branch(ne);
6196
6197 // Check that the receiver is a heap object.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006198 __ tst(receiver, Operand(kSmiTagMask));
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006199 deferred->Branch(eq);
6200
6201 // Check that the receiver is a JSArray.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006202 __ CompareObjectType(receiver, scratch1, scratch1, JS_ARRAY_TYPE);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006203 deferred->Branch(ne);
6204
6205 // Check that the key is within bounds. Both the key and the length of
6206 // the JSArray are smis. Use unsigned comparison to handle negative keys.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006207 __ ldr(scratch1, FieldMemOperand(receiver, JSArray::kLengthOffset));
6208 __ cmp(scratch1, key);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006209 deferred->Branch(ls); // Unsigned less equal.
6210
6211 // The following instructions are the part of the inlined store keyed
6212 // property code which can be patched. Therefore the exact number of
6213 // instructions generated need to be fixed, so the constant pool is blocked
6214 // while generating this code.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006215 { Assembler::BlockConstPoolScope block_const_pool(masm_);
6216 // Get the elements array from the receiver and check that it
6217 // is not a dictionary.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006218 __ ldr(scratch1, FieldMemOperand(receiver, JSObject::kElementsOffset));
6219 __ ldr(scratch2, FieldMemOperand(scratch1, JSObject::kMapOffset));
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006220 // Read the fixed array map from the constant pool (not from the root
6221 // array) so that the value can be patched. When debugging, we patch this
6222 // comparison to always fail so that we will hit the IC call in the
6223 // deferred code which will allow the debugger to break for fast case
6224 // stores.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006225#ifdef DEBUG
6226 Label check_inlined_codesize;
6227 masm_->bind(&check_inlined_codesize);
6228#endif
6229 __ mov(scratch3, Operand(Factory::fixed_array_map()));
6230 __ cmp(scratch2, scratch3);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006231 deferred->Branch(ne);
6232
6233 // Store the value.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006234 __ add(scratch1, scratch1,
6235 Operand(FixedArray::kHeaderSize - kHeapObjectTag));
6236 __ str(value,
6237 MemOperand(scratch1, key, LSL,
6238 kPointerSizeLog2 - (kSmiTagSize + kSmiShiftSize)));
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006239
6240 // Make sure that the expected number of instructions are generated.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006241 ASSERT_EQ(kInlinedKeyedStoreInstructionsAfterPatch,
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006242 masm_->InstructionsGeneratedSince(&check_inlined_codesize));
6243 }
6244
6245 deferred->BindExit();
6246 } else {
6247 frame()->CallKeyedStoreIC();
6248 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006249}
6250
6251
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006252#ifdef DEBUG
6253bool CodeGenerator::HasValidEntryRegisters() { return true; }
6254#endif
6255
6256
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006257#undef __
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006258#define __ ACCESS_MASM(masm)
6259
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006260
ager@chromium.org7c537e22008-10-16 08:43:32 +00006261Handle<String> Reference::GetName() {
6262 ASSERT(type_ == NAMED);
6263 Property* property = expression_->AsProperty();
6264 if (property == NULL) {
6265 // Global variable reference treated as a named property reference.
6266 VariableProxy* proxy = expression_->AsVariableProxy();
6267 ASSERT(proxy->AsVariable() != NULL);
6268 ASSERT(proxy->AsVariable()->is_global());
6269 return proxy->name();
6270 } else {
6271 Literal* raw_name = property->key()->AsLiteral();
6272 ASSERT(raw_name != NULL);
6273 return Handle<String>(String::cast(*raw_name->handle()));
6274 }
6275}
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006276
ager@chromium.org7c537e22008-10-16 08:43:32 +00006277
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006278void Reference::DupIfPersist() {
6279 if (persist_after_get_) {
6280 switch (type_) {
6281 case KEYED:
6282 cgen_->frame()->Dup2();
6283 break;
6284 case NAMED:
6285 cgen_->frame()->Dup();
6286 // Fall through.
6287 case UNLOADED:
6288 case ILLEGAL:
6289 case SLOT:
6290 // Do nothing.
6291 ;
6292 }
6293 } else {
6294 set_unloaded();
6295 }
6296}
6297
6298
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006299void Reference::GetValue() {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006300 ASSERT(cgen_->HasValidEntryRegisters());
ager@chromium.org7c537e22008-10-16 08:43:32 +00006301 ASSERT(!is_illegal());
6302 ASSERT(!cgen_->has_cc());
6303 MacroAssembler* masm = cgen_->masm();
6304 Property* property = expression_->AsProperty();
6305 if (property != NULL) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006306 cgen_->CodeForSourcePosition(property->position());
ager@chromium.org7c537e22008-10-16 08:43:32 +00006307 }
6308
6309 switch (type_) {
6310 case SLOT: {
6311 Comment cmnt(masm, "[ Load from Slot");
6312 Slot* slot = expression_->AsVariableProxy()->AsVariable()->slot();
6313 ASSERT(slot != NULL);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006314 DupIfPersist();
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006315 cgen_->LoadFromSlotCheckForArguments(slot, NOT_INSIDE_TYPEOF);
ager@chromium.org7c537e22008-10-16 08:43:32 +00006316 break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006317 }
6318
ager@chromium.org7c537e22008-10-16 08:43:32 +00006319 case NAMED: {
ager@chromium.org7c537e22008-10-16 08:43:32 +00006320 Variable* var = expression_->AsVariableProxy()->AsVariable();
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006321 bool is_global = var != NULL;
6322 ASSERT(!is_global || var->is_global());
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006323 Handle<String> name = GetName();
6324 DupIfPersist();
6325 cgen_->EmitNamedLoad(name, is_global);
ager@chromium.org7c537e22008-10-16 08:43:32 +00006326 break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006327 }
6328
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006329 case KEYED: {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006330 ASSERT(property != NULL);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006331 DupIfPersist();
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006332 cgen_->EmitKeyedLoad();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006333 cgen_->frame()->EmitPush(r0);
6334 break;
6335 }
6336
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006337 default:
6338 UNREACHABLE();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006339 }
6340}
6341
6342
ager@chromium.org7c537e22008-10-16 08:43:32 +00006343void Reference::SetValue(InitState init_state) {
6344 ASSERT(!is_illegal());
6345 ASSERT(!cgen_->has_cc());
6346 MacroAssembler* masm = cgen_->masm();
ager@chromium.org3bf7b912008-11-17 09:09:45 +00006347 VirtualFrame* frame = cgen_->frame();
ager@chromium.org7c537e22008-10-16 08:43:32 +00006348 Property* property = expression_->AsProperty();
6349 if (property != NULL) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006350 cgen_->CodeForSourcePosition(property->position());
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006351 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006352
ager@chromium.org7c537e22008-10-16 08:43:32 +00006353 switch (type_) {
6354 case SLOT: {
6355 Comment cmnt(masm, "[ Store to Slot");
6356 Slot* slot = expression_->AsVariableProxy()->AsVariable()->slot();
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006357 cgen_->StoreToSlot(slot, init_state);
ager@chromium.orgac091b72010-05-05 07:34:42 +00006358 set_unloaded();
ager@chromium.org7c537e22008-10-16 08:43:32 +00006359 break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006360 }
6361
ager@chromium.org7c537e22008-10-16 08:43:32 +00006362 case NAMED: {
6363 Comment cmnt(masm, "[ Store to named Property");
ager@chromium.orgac091b72010-05-05 07:34:42 +00006364 cgen_->EmitNamedStore(GetName(), false);
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00006365 frame->EmitPush(r0);
ager@chromium.org5c838252010-02-19 08:53:10 +00006366 set_unloaded();
ager@chromium.org7c537e22008-10-16 08:43:32 +00006367 break;
6368 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006369
ager@chromium.org7c537e22008-10-16 08:43:32 +00006370 case KEYED: {
6371 Comment cmnt(masm, "[ Store to keyed Property");
6372 Property* property = expression_->AsProperty();
6373 ASSERT(property != NULL);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006374 cgen_->CodeForSourcePosition(property->position());
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006375 cgen_->EmitKeyedStore(property->key()->type());
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00006376 frame->EmitPush(r0);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006377 set_unloaded();
ager@chromium.org7c537e22008-10-16 08:43:32 +00006378 break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006379 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00006380
6381 default:
6382 UNREACHABLE();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006383 }
6384}
6385
6386
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006387void FastNewClosureStub::Generate(MacroAssembler* masm) {
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006388 // Create a new closure from the given function info in new
6389 // space. Set the context to the current context in cp.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006390 Label gc;
6391
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006392 // Pop the function info from the stack.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006393 __ pop(r3);
6394
6395 // Attempt to allocate new JSFunction in new space.
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00006396 __ AllocateInNewSpace(JSFunction::kSize,
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006397 r0,
6398 r1,
6399 r2,
6400 &gc,
6401 TAG_OBJECT);
6402
6403 // Compute the function map in the current global context and set that
6404 // as the map of the allocated object.
6405 __ ldr(r2, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_INDEX)));
6406 __ ldr(r2, FieldMemOperand(r2, GlobalObject::kGlobalContextOffset));
6407 __ ldr(r2, MemOperand(r2, Context::SlotOffset(Context::FUNCTION_MAP_INDEX)));
6408 __ str(r2, FieldMemOperand(r0, HeapObject::kMapOffset));
6409
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006410 // Initialize the rest of the function. We don't have to update the
6411 // write barrier because the allocated object is in new space.
6412 __ LoadRoot(r1, Heap::kEmptyFixedArrayRootIndex);
6413 __ LoadRoot(r2, Heap::kTheHoleValueRootIndex);
6414 __ str(r1, FieldMemOperand(r0, JSObject::kPropertiesOffset));
6415 __ str(r1, FieldMemOperand(r0, JSObject::kElementsOffset));
6416 __ str(r2, FieldMemOperand(r0, JSFunction::kPrototypeOrInitialMapOffset));
6417 __ str(r3, FieldMemOperand(r0, JSFunction::kSharedFunctionInfoOffset));
6418 __ str(cp, FieldMemOperand(r0, JSFunction::kContextOffset));
6419 __ str(r1, FieldMemOperand(r0, JSFunction::kLiteralsOffset));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006420
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006421 // Return result. The argument function info has been popped already.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006422 __ Ret();
6423
6424 // Create a new closure through the slower runtime call.
6425 __ bind(&gc);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006426 __ Push(cp, r3);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006427 __ TailCallRuntime(Runtime::kNewClosure, 2, 1);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006428}
6429
6430
6431void FastNewContextStub::Generate(MacroAssembler* masm) {
6432 // Try to allocate the context in new space.
6433 Label gc;
6434 int length = slots_ + Context::MIN_CONTEXT_SLOTS;
6435
6436 // Attempt to allocate the context in new space.
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00006437 __ AllocateInNewSpace(FixedArray::SizeFor(length),
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006438 r0,
6439 r1,
6440 r2,
6441 &gc,
6442 TAG_OBJECT);
6443
6444 // Load the function from the stack.
ager@chromium.org5c838252010-02-19 08:53:10 +00006445 __ ldr(r3, MemOperand(sp, 0));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006446
6447 // Setup the object header.
6448 __ LoadRoot(r2, Heap::kContextMapRootIndex);
6449 __ str(r2, FieldMemOperand(r0, HeapObject::kMapOffset));
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006450 __ mov(r2, Operand(Smi::FromInt(length)));
6451 __ str(r2, FieldMemOperand(r0, FixedArray::kLengthOffset));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006452
6453 // Setup the fixed slots.
6454 __ mov(r1, Operand(Smi::FromInt(0)));
6455 __ str(r3, MemOperand(r0, Context::SlotOffset(Context::CLOSURE_INDEX)));
6456 __ str(r0, MemOperand(r0, Context::SlotOffset(Context::FCONTEXT_INDEX)));
6457 __ str(r1, MemOperand(r0, Context::SlotOffset(Context::PREVIOUS_INDEX)));
6458 __ str(r1, MemOperand(r0, Context::SlotOffset(Context::EXTENSION_INDEX)));
6459
6460 // Copy the global object from the surrounding context.
6461 __ ldr(r1, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_INDEX)));
6462 __ str(r1, MemOperand(r0, Context::SlotOffset(Context::GLOBAL_INDEX)));
6463
6464 // Initialize the rest of the slots to undefined.
6465 __ LoadRoot(r1, Heap::kUndefinedValueRootIndex);
6466 for (int i = Context::MIN_CONTEXT_SLOTS; i < length; i++) {
6467 __ str(r1, MemOperand(r0, Context::SlotOffset(i)));
6468 }
6469
6470 // Remove the on-stack argument and return.
6471 __ mov(cp, r0);
6472 __ pop();
6473 __ Ret();
6474
6475 // Need to collect. Call into runtime system.
6476 __ bind(&gc);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006477 __ TailCallRuntime(Runtime::kNewContext, 1, 1);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006478}
6479
6480
ager@chromium.org5c838252010-02-19 08:53:10 +00006481void FastCloneShallowArrayStub::Generate(MacroAssembler* masm) {
6482 // Stack layout on entry:
6483 //
6484 // [sp]: constant elements.
6485 // [sp + kPointerSize]: literal index.
6486 // [sp + (2 * kPointerSize)]: literals array.
6487
6488 // All sizes here are multiples of kPointerSize.
6489 int elements_size = (length_ > 0) ? FixedArray::SizeFor(length_) : 0;
6490 int size = JSArray::kSize + elements_size;
6491
6492 // Load boilerplate object into r3 and check if we need to create a
6493 // boilerplate.
6494 Label slow_case;
6495 __ ldr(r3, MemOperand(sp, 2 * kPointerSize));
6496 __ ldr(r0, MemOperand(sp, 1 * kPointerSize));
6497 __ add(r3, r3, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
6498 __ ldr(r3, MemOperand(r3, r0, LSL, kPointerSizeLog2 - kSmiTagSize));
6499 __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
6500 __ cmp(r3, ip);
6501 __ b(eq, &slow_case);
6502
6503 // Allocate both the JS array and the elements array in one big
6504 // allocation. This avoids multiple limit checks.
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00006505 __ AllocateInNewSpace(size,
ager@chromium.org5c838252010-02-19 08:53:10 +00006506 r0,
6507 r1,
6508 r2,
6509 &slow_case,
6510 TAG_OBJECT);
6511
6512 // Copy the JS array part.
6513 for (int i = 0; i < JSArray::kSize; i += kPointerSize) {
6514 if ((i != JSArray::kElementsOffset) || (length_ == 0)) {
6515 __ ldr(r1, FieldMemOperand(r3, i));
6516 __ str(r1, FieldMemOperand(r0, i));
6517 }
6518 }
6519
6520 if (length_ > 0) {
6521 // Get hold of the elements array of the boilerplate and setup the
6522 // elements pointer in the resulting object.
6523 __ ldr(r3, FieldMemOperand(r3, JSArray::kElementsOffset));
6524 __ add(r2, r0, Operand(JSArray::kSize));
6525 __ str(r2, FieldMemOperand(r0, JSArray::kElementsOffset));
6526
6527 // Copy the elements array.
6528 for (int i = 0; i < elements_size; i += kPointerSize) {
6529 __ ldr(r1, FieldMemOperand(r3, i));
6530 __ str(r1, FieldMemOperand(r2, i));
6531 }
6532 }
6533
6534 // Return and remove the on-stack parameters.
6535 __ add(sp, sp, Operand(3 * kPointerSize));
6536 __ Ret();
6537
6538 __ bind(&slow_case);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006539 __ TailCallRuntime(Runtime::kCreateArrayLiteralShallow, 3, 1);
ager@chromium.org5c838252010-02-19 08:53:10 +00006540}
6541
6542
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006543// Takes a Smi and converts to an IEEE 64 bit floating point value in two
6544// registers. The format is 1 sign bit, 11 exponent bits (biased 1023) and
6545// 52 fraction bits (20 in the first word, 32 in the second). Zeros is a
6546// scratch register. Destroys the source register. No GC occurs during this
6547// stub so you don't have to set up the frame.
6548class ConvertToDoubleStub : public CodeStub {
6549 public:
6550 ConvertToDoubleStub(Register result_reg_1,
6551 Register result_reg_2,
6552 Register source_reg,
6553 Register scratch_reg)
6554 : result1_(result_reg_1),
6555 result2_(result_reg_2),
6556 source_(source_reg),
6557 zeros_(scratch_reg) { }
6558
6559 private:
6560 Register result1_;
6561 Register result2_;
6562 Register source_;
6563 Register zeros_;
6564
6565 // Minor key encoding in 16 bits.
6566 class ModeBits: public BitField<OverwriteMode, 0, 2> {};
6567 class OpBits: public BitField<Token::Value, 2, 14> {};
6568
6569 Major MajorKey() { return ConvertToDouble; }
6570 int MinorKey() {
6571 // Encode the parameters in a unique 16 bit value.
6572 return result1_.code() +
6573 (result2_.code() << 4) +
6574 (source_.code() << 8) +
6575 (zeros_.code() << 12);
6576 }
6577
6578 void Generate(MacroAssembler* masm);
6579
6580 const char* GetName() { return "ConvertToDoubleStub"; }
6581
6582#ifdef DEBUG
6583 void Print() { PrintF("ConvertToDoubleStub\n"); }
6584#endif
6585};
6586
6587
6588void ConvertToDoubleStub::Generate(MacroAssembler* masm) {
6589#ifndef BIG_ENDIAN_FLOATING_POINT
6590 Register exponent = result1_;
6591 Register mantissa = result2_;
6592#else
6593 Register exponent = result2_;
6594 Register mantissa = result1_;
6595#endif
6596 Label not_special;
6597 // Convert from Smi to integer.
6598 __ mov(source_, Operand(source_, ASR, kSmiTagSize));
6599 // Move sign bit from source to destination. This works because the sign bit
6600 // in the exponent word of the double has the same position and polarity as
6601 // the 2's complement sign bit in a Smi.
6602 ASSERT(HeapNumber::kSignMask == 0x80000000u);
6603 __ and_(exponent, source_, Operand(HeapNumber::kSignMask), SetCC);
6604 // Subtract from 0 if source was negative.
6605 __ rsb(source_, source_, Operand(0), LeaveCC, ne);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006606
6607 // We have -1, 0 or 1, which we treat specially. Register source_ contains
6608 // absolute value: it is either equal to 1 (special case of -1 and 1),
6609 // greater than 1 (not a special case) or less than 1 (special case of 0).
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006610 __ cmp(source_, Operand(1));
6611 __ b(gt, &not_special);
6612
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006613 // For 1 or -1 we need to or in the 0 exponent (biased to 1023).
6614 static const uint32_t exponent_word_for_1 =
6615 HeapNumber::kExponentBias << HeapNumber::kExponentShift;
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006616 __ orr(exponent, exponent, Operand(exponent_word_for_1), LeaveCC, eq);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006617 // 1, 0 and -1 all have 0 for the second word.
6618 __ mov(mantissa, Operand(0));
6619 __ Ret();
6620
6621 __ bind(&not_special);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006622 // Count leading zeros. Uses mantissa for a scratch register on pre-ARM5.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006623 // Gets the wrong answer for 0, but we already checked for that case above.
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006624 __ CountLeadingZeros(source_, mantissa, zeros_);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006625 // Compute exponent and or it into the exponent register.
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00006626 // We use mantissa as a scratch register here. Use a fudge factor to
6627 // divide the constant 31 + HeapNumber::kExponentBias, 0x41d, into two parts
6628 // that fit in the ARM's constant field.
6629 int fudge = 0x400;
6630 __ rsb(mantissa, zeros_, Operand(31 + HeapNumber::kExponentBias - fudge));
6631 __ add(mantissa, mantissa, Operand(fudge));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006632 __ orr(exponent,
6633 exponent,
6634 Operand(mantissa, LSL, HeapNumber::kExponentShift));
6635 // Shift up the source chopping the top bit off.
6636 __ add(zeros_, zeros_, Operand(1));
6637 // This wouldn't work for 1.0 or -1.0 as the shift would be 32 which means 0.
6638 __ mov(source_, Operand(source_, LSL, zeros_));
6639 // Compute lower part of fraction (last 12 bits).
6640 __ mov(mantissa, Operand(source_, LSL, HeapNumber::kMantissaBitsInTopWord));
6641 // And the top (top 20 bits).
6642 __ orr(exponent,
6643 exponent,
6644 Operand(source_, LSR, 32 - HeapNumber::kMantissaBitsInTopWord));
6645 __ Ret();
6646}
6647
6648
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006649// See comment for class.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006650void WriteInt32ToHeapNumberStub::Generate(MacroAssembler* masm) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006651 Label max_negative_int;
6652 // the_int_ has the answer which is a signed int32 but not a Smi.
6653 // We test for the special value that has a different exponent. This test
6654 // has the neat side effect of setting the flags according to the sign.
6655 ASSERT(HeapNumber::kSignMask == 0x80000000u);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00006656 __ cmp(the_int_, Operand(0x80000000u));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006657 __ b(eq, &max_negative_int);
6658 // Set up the correct exponent in scratch_. All non-Smi int32s have the same.
6659 // A non-Smi integer is 1.xxx * 2^30 so the exponent is 30 (biased).
6660 uint32_t non_smi_exponent =
6661 (HeapNumber::kExponentBias + 30) << HeapNumber::kExponentShift;
6662 __ mov(scratch_, Operand(non_smi_exponent));
6663 // Set the sign bit in scratch_ if the value was negative.
6664 __ orr(scratch_, scratch_, Operand(HeapNumber::kSignMask), LeaveCC, cs);
6665 // Subtract from 0 if the value was negative.
6666 __ rsb(the_int_, the_int_, Operand(0), LeaveCC, cs);
6667 // We should be masking the implict first digit of the mantissa away here,
6668 // but it just ends up combining harmlessly with the last digit of the
6669 // exponent that happens to be 1. The sign bit is 0 so we shift 10 to get
6670 // the most significant 1 to hit the last bit of the 12 bit sign and exponent.
6671 ASSERT(((1 << HeapNumber::kExponentShift) & non_smi_exponent) != 0);
6672 const int shift_distance = HeapNumber::kNonMantissaBitsInTopWord - 2;
6673 __ orr(scratch_, scratch_, Operand(the_int_, LSR, shift_distance));
6674 __ str(scratch_, FieldMemOperand(the_heap_number_,
6675 HeapNumber::kExponentOffset));
6676 __ mov(scratch_, Operand(the_int_, LSL, 32 - shift_distance));
6677 __ str(scratch_, FieldMemOperand(the_heap_number_,
6678 HeapNumber::kMantissaOffset));
6679 __ Ret();
6680
6681 __ bind(&max_negative_int);
6682 // The max negative int32 is stored as a positive number in the mantissa of
6683 // a double because it uses a sign bit instead of using two's complement.
6684 // The actual mantissa bits stored are all 0 because the implicit most
6685 // significant 1 bit is not stored.
6686 non_smi_exponent += 1 << HeapNumber::kExponentShift;
6687 __ mov(ip, Operand(HeapNumber::kSignMask | non_smi_exponent));
6688 __ str(ip, FieldMemOperand(the_heap_number_, HeapNumber::kExponentOffset));
6689 __ mov(ip, Operand(0));
6690 __ str(ip, FieldMemOperand(the_heap_number_, HeapNumber::kMantissaOffset));
6691 __ Ret();
6692}
6693
6694
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006695// Handle the case where the lhs and rhs are the same object.
6696// Equality is almost reflexive (everything but NaN), so this is a test
6697// for "identity and not NaN".
6698static void EmitIdenticalObjectComparison(MacroAssembler* masm,
6699 Label* slow,
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006700 Condition cc,
6701 bool never_nan_nan) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006702 Label not_identical;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006703 Label heap_number, return_equal;
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006704 __ cmp(r0, r1);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006705 __ b(ne, &not_identical);
6706
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006707 // The two objects are identical. If we know that one of them isn't NaN then
6708 // we now know they test equal.
6709 if (cc != eq || !never_nan_nan) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006710 // Test for NaN. Sadly, we can't just compare to Factory::nan_value(),
6711 // so we do the second best thing - test it ourselves.
6712 // They are both equal and they are not both Smis so both of them are not
6713 // Smis. If it's not a heap number, then return equal.
6714 if (cc == lt || cc == gt) {
6715 __ CompareObjectType(r0, r4, r4, FIRST_JS_OBJECT_TYPE);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006716 __ b(ge, slow);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006717 } else {
6718 __ CompareObjectType(r0, r4, r4, HEAP_NUMBER_TYPE);
6719 __ b(eq, &heap_number);
6720 // Comparing JS objects with <=, >= is complicated.
6721 if (cc != eq) {
6722 __ cmp(r4, Operand(FIRST_JS_OBJECT_TYPE));
6723 __ b(ge, slow);
6724 // Normally here we fall through to return_equal, but undefined is
6725 // special: (undefined == undefined) == true, but
6726 // (undefined <= undefined) == false! See ECMAScript 11.8.5.
6727 if (cc == le || cc == ge) {
6728 __ cmp(r4, Operand(ODDBALL_TYPE));
6729 __ b(ne, &return_equal);
6730 __ LoadRoot(r2, Heap::kUndefinedValueRootIndex);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006731 __ cmp(r0, r2);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006732 __ b(ne, &return_equal);
6733 if (cc == le) {
6734 // undefined <= undefined should fail.
6735 __ mov(r0, Operand(GREATER));
6736 } else {
6737 // undefined >= undefined should fail.
6738 __ mov(r0, Operand(LESS));
6739 }
6740 __ mov(pc, Operand(lr)); // Return.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006741 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006742 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006743 }
6744 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006745
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006746 __ bind(&return_equal);
6747 if (cc == lt) {
6748 __ mov(r0, Operand(GREATER)); // Things aren't less than themselves.
6749 } else if (cc == gt) {
6750 __ mov(r0, Operand(LESS)); // Things aren't greater than themselves.
6751 } else {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006752 __ mov(r0, Operand(EQUAL)); // Things are <=, >=, ==, === themselves.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006753 }
6754 __ mov(pc, Operand(lr)); // Return.
6755
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006756 if (cc != eq || !never_nan_nan) {
6757 // For less and greater we don't have to check for NaN since the result of
6758 // x < x is false regardless. For the others here is some code to check
6759 // for NaN.
6760 if (cc != lt && cc != gt) {
6761 __ bind(&heap_number);
6762 // It is a heap number, so return non-equal if it's NaN and equal if it's
6763 // not NaN.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006764
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006765 // The representation of NaN values has all exponent bits (52..62) set,
6766 // and not all mantissa bits (0..51) clear.
6767 // Read top bits of double representation (second word of value).
6768 __ ldr(r2, FieldMemOperand(r0, HeapNumber::kExponentOffset));
6769 // Test that exponent bits are all set.
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00006770 __ Sbfx(r3, r2, HeapNumber::kExponentShift, HeapNumber::kExponentBits);
6771 // NaNs have all-one exponents so they sign extend to -1.
6772 __ cmp(r3, Operand(-1));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006773 __ b(ne, &return_equal);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006774
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006775 // Shift out flag and all exponent bits, retaining only mantissa.
6776 __ mov(r2, Operand(r2, LSL, HeapNumber::kNonMantissaBitsInTopWord));
6777 // Or with all low-bits of mantissa.
6778 __ ldr(r3, FieldMemOperand(r0, HeapNumber::kMantissaOffset));
6779 __ orr(r0, r3, Operand(r2), SetCC);
6780 // For equal we already have the right value in r0: Return zero (equal)
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006781 // if all bits in mantissa are zero (it's an Infinity) and non-zero if
6782 // not (it's a NaN). For <= and >= we need to load r0 with the failing
6783 // value if it's a NaN.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006784 if (cc != eq) {
6785 // All-zero means Infinity means equal.
6786 __ mov(pc, Operand(lr), LeaveCC, eq); // Return equal
6787 if (cc == le) {
6788 __ mov(r0, Operand(GREATER)); // NaN <= NaN should fail.
6789 } else {
6790 __ mov(r0, Operand(LESS)); // NaN >= NaN should fail.
6791 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006792 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006793 __ mov(pc, Operand(lr)); // Return.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006794 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006795 // No fall through here.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006796 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006797
6798 __ bind(&not_identical);
6799}
6800
6801
6802// See comment at call site.
6803static void EmitSmiNonsmiComparison(MacroAssembler* masm,
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006804 Label* lhs_not_nan,
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006805 Label* slow,
6806 bool strict) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006807 Label rhs_is_smi;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006808 __ tst(r0, Operand(kSmiTagMask));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006809 __ b(eq, &rhs_is_smi);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006810
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006811 // Lhs is a Smi. Check whether the rhs is a heap number.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006812 __ CompareObjectType(r0, r4, r4, HEAP_NUMBER_TYPE);
6813 if (strict) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006814 // If rhs is not a number and lhs is a Smi then strict equality cannot
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006815 // succeed. Return non-equal (r0 is already not zero)
6816 __ mov(pc, Operand(lr), LeaveCC, ne); // Return.
6817 } else {
6818 // Smi compared non-strictly with a non-Smi non-heap-number. Call
6819 // the runtime.
6820 __ b(ne, slow);
6821 }
6822
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006823 // Lhs (r1) is a smi, rhs (r0) is a number.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006824 if (CpuFeatures::IsSupported(VFP3)) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006825 // Convert lhs to a double in d7 .
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006826 CpuFeatures::Scope scope(VFP3);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006827 __ mov(r7, Operand(r1, ASR, kSmiTagSize));
6828 __ vmov(s15, r7);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006829 __ vcvt_f64_s32(d7, s15);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006830 // Load the double from rhs, tagged HeapNumber r0, to d6.
6831 __ sub(r7, r0, Operand(kHeapObjectTag));
6832 __ vldr(d6, r7, HeapNumber::kValueOffset);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006833 } else {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006834 __ push(lr);
6835 // Convert lhs to a double in r2, r3.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006836 __ mov(r7, Operand(r1));
6837 ConvertToDoubleStub stub1(r3, r2, r7, r6);
6838 __ Call(stub1.GetCode(), RelocInfo::CODE_TARGET);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006839 // Load rhs to a double in r0, r1.
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00006840 __ Ldrd(r0, r1, FieldMemOperand(r0, HeapNumber::kValueOffset));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006841 __ pop(lr);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006842 }
6843
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006844 // We now have both loaded as doubles but we can skip the lhs nan check
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006845 // since it's a smi.
6846 __ jmp(lhs_not_nan);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006847
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006848 __ bind(&rhs_is_smi);
6849 // Rhs is a smi. Check whether the non-smi lhs is a heap number.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006850 __ CompareObjectType(r1, r4, r4, HEAP_NUMBER_TYPE);
6851 if (strict) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006852 // If lhs is not a number and rhs is a smi then strict equality cannot
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006853 // succeed. Return non-equal.
6854 __ mov(r0, Operand(1), LeaveCC, ne); // Non-zero indicates not equal.
6855 __ mov(pc, Operand(lr), LeaveCC, ne); // Return.
6856 } else {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006857 // Smi compared non-strictly with a non-smi non-heap-number. Call
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006858 // the runtime.
6859 __ b(ne, slow);
6860 }
6861
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006862 // Rhs (r0) is a smi, lhs (r1) is a heap number.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006863 if (CpuFeatures::IsSupported(VFP3)) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006864 // Convert rhs to a double in d6 .
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006865 CpuFeatures::Scope scope(VFP3);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006866 // Load the double from lhs, tagged HeapNumber r1, to d7.
6867 __ sub(r7, r1, Operand(kHeapObjectTag));
6868 __ vldr(d7, r7, HeapNumber::kValueOffset);
6869 __ mov(r7, Operand(r0, ASR, kSmiTagSize));
6870 __ vmov(s13, r7);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006871 __ vcvt_f64_s32(d6, s13);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006872 } else {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006873 __ push(lr);
6874 // Load lhs to a double in r2, r3.
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00006875 __ Ldrd(r2, r3, FieldMemOperand(r1, HeapNumber::kValueOffset));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006876 // Convert rhs to a double in r0, r1.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006877 __ mov(r7, Operand(r0));
6878 ConvertToDoubleStub stub2(r1, r0, r7, r6);
6879 __ Call(stub2.GetCode(), RelocInfo::CODE_TARGET);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006880 __ pop(lr);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006881 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006882 // Fall through to both_loaded_as_doubles.
6883}
6884
6885
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006886void EmitNanCheck(MacroAssembler* masm, Label* lhs_not_nan, Condition cc) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006887 bool exp_first = (HeapNumber::kExponentOffset == HeapNumber::kValueOffset);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006888 Register rhs_exponent = exp_first ? r0 : r1;
6889 Register lhs_exponent = exp_first ? r2 : r3;
6890 Register rhs_mantissa = exp_first ? r1 : r0;
6891 Register lhs_mantissa = exp_first ? r3 : r2;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006892 Label one_is_nan, neither_is_nan;
6893
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00006894 __ Sbfx(r4,
6895 lhs_exponent,
6896 HeapNumber::kExponentShift,
6897 HeapNumber::kExponentBits);
6898 // NaNs have all-one exponents so they sign extend to -1.
6899 __ cmp(r4, Operand(-1));
6900 __ b(ne, lhs_not_nan);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006901 __ mov(r4,
6902 Operand(lhs_exponent, LSL, HeapNumber::kNonMantissaBitsInTopWord),
6903 SetCC);
6904 __ b(ne, &one_is_nan);
6905 __ cmp(lhs_mantissa, Operand(0));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006906 __ b(ne, &one_is_nan);
6907
6908 __ bind(lhs_not_nan);
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00006909 __ Sbfx(r4,
6910 rhs_exponent,
6911 HeapNumber::kExponentShift,
6912 HeapNumber::kExponentBits);
6913 // NaNs have all-one exponents so they sign extend to -1.
6914 __ cmp(r4, Operand(-1));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006915 __ b(ne, &neither_is_nan);
6916 __ mov(r4,
6917 Operand(rhs_exponent, LSL, HeapNumber::kNonMantissaBitsInTopWord),
6918 SetCC);
6919 __ b(ne, &one_is_nan);
6920 __ cmp(rhs_mantissa, Operand(0));
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006921 __ b(eq, &neither_is_nan);
6922
6923 __ bind(&one_is_nan);
6924 // NaN comparisons always fail.
6925 // Load whatever we need in r0 to make the comparison fail.
6926 if (cc == lt || cc == le) {
6927 __ mov(r0, Operand(GREATER));
6928 } else {
6929 __ mov(r0, Operand(LESS));
6930 }
6931 __ mov(pc, Operand(lr)); // Return.
6932
6933 __ bind(&neither_is_nan);
6934}
6935
6936
6937// See comment at call site.
6938static void EmitTwoNonNanDoubleComparison(MacroAssembler* masm, Condition cc) {
6939 bool exp_first = (HeapNumber::kExponentOffset == HeapNumber::kValueOffset);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006940 Register rhs_exponent = exp_first ? r0 : r1;
6941 Register lhs_exponent = exp_first ? r2 : r3;
6942 Register rhs_mantissa = exp_first ? r1 : r0;
6943 Register lhs_mantissa = exp_first ? r3 : r2;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006944
6945 // r0, r1, r2, r3 have the two doubles. Neither is a NaN.
6946 if (cc == eq) {
6947 // Doubles are not equal unless they have the same bit pattern.
6948 // Exception: 0 and -0.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006949 __ cmp(rhs_mantissa, Operand(lhs_mantissa));
6950 __ orr(r0, rhs_mantissa, Operand(lhs_mantissa), LeaveCC, ne);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006951 // Return non-zero if the numbers are unequal.
6952 __ mov(pc, Operand(lr), LeaveCC, ne);
6953
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006954 __ sub(r0, rhs_exponent, Operand(lhs_exponent), SetCC);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006955 // If exponents are equal then return 0.
6956 __ mov(pc, Operand(lr), LeaveCC, eq);
6957
6958 // Exponents are unequal. The only way we can return that the numbers
6959 // are equal is if one is -0 and the other is 0. We already dealt
6960 // with the case where both are -0 or both are 0.
6961 // We start by seeing if the mantissas (that are equal) or the bottom
6962 // 31 bits of the rhs exponent are non-zero. If so we return not
6963 // equal.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006964 __ orr(r4, lhs_mantissa, Operand(lhs_exponent, LSL, kSmiTagSize), SetCC);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006965 __ mov(r0, Operand(r4), LeaveCC, ne);
6966 __ mov(pc, Operand(lr), LeaveCC, ne); // Return conditionally.
6967 // Now they are equal if and only if the lhs exponent is zero in its
6968 // low 31 bits.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006969 __ mov(r0, Operand(rhs_exponent, LSL, kSmiTagSize));
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006970 __ mov(pc, Operand(lr));
6971 } else {
6972 // Call a native function to do a comparison between two non-NaNs.
6973 // Call C routine that may not cause GC or other trouble.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00006974 __ push(lr);
6975 __ PrepareCallCFunction(4, r5); // Two doubles count as 4 arguments.
6976 __ CallCFunction(ExternalReference::compare_doubles(), 4);
6977 __ pop(pc); // Return.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006978 }
6979}
6980
6981
6982// See comment at call site.
6983static void EmitStrictTwoHeapObjectCompare(MacroAssembler* masm) {
6984 // If either operand is a JSObject or an oddball value, then they are
6985 // not equal since their pointers are different.
6986 // There is no test for undetectability in strict equality.
6987 ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
6988 Label first_non_object;
6989 // Get the type of the first operand into r2 and compare it with
6990 // FIRST_JS_OBJECT_TYPE.
6991 __ CompareObjectType(r0, r2, r2, FIRST_JS_OBJECT_TYPE);
6992 __ b(lt, &first_non_object);
6993
6994 // Return non-zero (r0 is not zero)
6995 Label return_not_equal;
6996 __ bind(&return_not_equal);
6997 __ mov(pc, Operand(lr)); // Return.
6998
6999 __ bind(&first_non_object);
7000 // Check for oddballs: true, false, null, undefined.
7001 __ cmp(r2, Operand(ODDBALL_TYPE));
7002 __ b(eq, &return_not_equal);
7003
7004 __ CompareObjectType(r1, r3, r3, FIRST_JS_OBJECT_TYPE);
7005 __ b(ge, &return_not_equal);
7006
7007 // Check for oddballs: true, false, null, undefined.
7008 __ cmp(r3, Operand(ODDBALL_TYPE));
7009 __ b(eq, &return_not_equal);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007010
7011 // Now that we have the types we might as well check for symbol-symbol.
7012 // Ensure that no non-strings have the symbol bit set.
7013 ASSERT(kNotStringTag + kIsSymbolMask > LAST_TYPE);
7014 ASSERT(kSymbolTag != 0);
7015 __ and_(r2, r2, Operand(r3));
7016 __ tst(r2, Operand(kIsSymbolMask));
7017 __ b(ne, &return_not_equal);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007018}
7019
7020
7021// See comment at call site.
7022static void EmitCheckForTwoHeapNumbers(MacroAssembler* masm,
7023 Label* both_loaded_as_doubles,
7024 Label* not_heap_numbers,
7025 Label* slow) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007026 __ CompareObjectType(r0, r3, r2, HEAP_NUMBER_TYPE);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007027 __ b(ne, not_heap_numbers);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007028 __ ldr(r2, FieldMemOperand(r1, HeapObject::kMapOffset));
7029 __ cmp(r2, r3);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007030 __ b(ne, slow); // First was a heap number, second wasn't. Go slow case.
7031
7032 // Both are heap numbers. Load them up then jump to the code we have
7033 // for that.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007034 if (CpuFeatures::IsSupported(VFP3)) {
7035 CpuFeatures::Scope scope(VFP3);
7036 __ sub(r7, r0, Operand(kHeapObjectTag));
7037 __ vldr(d6, r7, HeapNumber::kValueOffset);
7038 __ sub(r7, r1, Operand(kHeapObjectTag));
7039 __ vldr(d7, r7, HeapNumber::kValueOffset);
7040 } else {
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00007041 __ Ldrd(r2, r3, FieldMemOperand(r1, HeapNumber::kValueOffset));
7042 __ Ldrd(r0, r1, FieldMemOperand(r0, HeapNumber::kValueOffset));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007043 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007044 __ jmp(both_loaded_as_doubles);
7045}
7046
7047
7048// Fast negative check for symbol-to-symbol equality.
7049static void EmitCheckForSymbols(MacroAssembler* masm, Label* slow) {
7050 // r2 is object type of r0.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007051 // Ensure that no non-strings have the symbol bit set.
7052 ASSERT(kNotStringTag + kIsSymbolMask > LAST_TYPE);
7053 ASSERT(kSymbolTag != 0);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007054 __ tst(r2, Operand(kIsSymbolMask));
7055 __ b(eq, slow);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007056 __ ldr(r3, FieldMemOperand(r1, HeapObject::kMapOffset));
7057 __ ldrb(r3, FieldMemOperand(r3, Map::kInstanceTypeOffset));
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007058 __ tst(r3, Operand(kIsSymbolMask));
7059 __ b(eq, slow);
7060
7061 // Both are symbols. We already checked they weren't the same pointer
7062 // so they are not equal.
7063 __ mov(r0, Operand(1)); // Non-zero indicates not equal.
7064 __ mov(pc, Operand(lr)); // Return.
7065}
7066
7067
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007068void NumberToStringStub::GenerateLookupNumberStringCache(MacroAssembler* masm,
7069 Register object,
7070 Register result,
7071 Register scratch1,
7072 Register scratch2,
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00007073 Register scratch3,
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007074 bool object_is_smi,
7075 Label* not_found) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007076 // Use of registers. Register result is used as a temporary.
7077 Register number_string_cache = result;
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00007078 Register mask = scratch3;
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007079
7080 // Load the number string cache.
7081 __ LoadRoot(number_string_cache, Heap::kNumberStringCacheRootIndex);
7082
7083 // Make the hash mask from the length of the number string cache. It
7084 // contains two elements (number and string) for each cache entry.
7085 __ ldr(mask, FieldMemOperand(number_string_cache, FixedArray::kLengthOffset));
ricow@chromium.org30ce4112010-05-31 10:38:25 +00007086 // Divide length by two (length is a smi).
7087 __ mov(mask, Operand(mask, ASR, kSmiTagSize + 1));
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007088 __ sub(mask, mask, Operand(1)); // Make mask.
7089
7090 // Calculate the entry in the number string cache. The hash value in the
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00007091 // number string cache for smis is just the smi value, and the hash for
7092 // doubles is the xor of the upper and lower words. See
7093 // Heap::GetNumberStringCache.
7094 Label is_smi;
7095 Label load_result_from_cache;
7096 if (!object_is_smi) {
7097 __ BranchOnSmi(object, &is_smi);
7098 if (CpuFeatures::IsSupported(VFP3)) {
7099 CpuFeatures::Scope scope(VFP3);
7100 __ CheckMap(object,
7101 scratch1,
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00007102 Heap::kHeapNumberMapRootIndex,
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00007103 not_found,
7104 true);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007105
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00007106 ASSERT_EQ(8, kDoubleSize);
7107 __ add(scratch1,
7108 object,
7109 Operand(HeapNumber::kValueOffset - kHeapObjectTag));
7110 __ ldm(ia, scratch1, scratch1.bit() | scratch2.bit());
7111 __ eor(scratch1, scratch1, Operand(scratch2));
7112 __ and_(scratch1, scratch1, Operand(mask));
7113
7114 // Calculate address of entry in string cache: each entry consists
7115 // of two pointer sized fields.
7116 __ add(scratch1,
7117 number_string_cache,
7118 Operand(scratch1, LSL, kPointerSizeLog2 + 1));
7119
7120 Register probe = mask;
7121 __ ldr(probe,
7122 FieldMemOperand(scratch1, FixedArray::kHeaderSize));
7123 __ BranchOnSmi(probe, not_found);
7124 __ sub(scratch2, object, Operand(kHeapObjectTag));
7125 __ vldr(d0, scratch2, HeapNumber::kValueOffset);
7126 __ sub(probe, probe, Operand(kHeapObjectTag));
7127 __ vldr(d1, probe, HeapNumber::kValueOffset);
7128 __ vcmp(d0, d1);
7129 __ vmrs(pc);
7130 __ b(ne, not_found); // The cache did not contain this value.
7131 __ b(&load_result_from_cache);
7132 } else {
7133 __ b(not_found);
7134 }
7135 }
7136
7137 __ bind(&is_smi);
7138 Register scratch = scratch1;
7139 __ and_(scratch, mask, Operand(object, ASR, 1));
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007140 // Calculate address of entry in string cache: each entry consists
7141 // of two pointer sized fields.
7142 __ add(scratch,
7143 number_string_cache,
7144 Operand(scratch, LSL, kPointerSizeLog2 + 1));
7145
7146 // Check if the entry is the smi we are looking for.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00007147 Register probe = mask;
7148 __ ldr(probe, FieldMemOperand(scratch, FixedArray::kHeaderSize));
7149 __ cmp(object, probe);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007150 __ b(ne, not_found);
7151
7152 // Get the result from the cache.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00007153 __ bind(&load_result_from_cache);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007154 __ ldr(result,
7155 FieldMemOperand(scratch, FixedArray::kHeaderSize + kPointerSize));
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007156 __ IncrementCounter(&Counters::number_to_string_native,
7157 1,
7158 scratch1,
7159 scratch2);
7160}
7161
7162
7163void NumberToStringStub::Generate(MacroAssembler* masm) {
7164 Label runtime;
7165
7166 __ ldr(r1, MemOperand(sp, 0));
7167
7168 // Generate code to lookup number in the number string cache.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00007169 GenerateLookupNumberStringCache(masm, r1, r0, r2, r3, r4, false, &runtime);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007170 __ add(sp, sp, Operand(1 * kPointerSize));
7171 __ Ret();
7172
7173 __ bind(&runtime);
7174 // Handle number to string in the runtime system if not found in the cache.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00007175 __ TailCallRuntime(Runtime::kNumberToStringSkipCache, 1, 1);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007176}
7177
7178
ager@chromium.orgac091b72010-05-05 07:34:42 +00007179void RecordWriteStub::Generate(MacroAssembler* masm) {
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00007180 __ RecordWriteHelper(object_, Operand(offset_), offset_, scratch_);
ager@chromium.orgac091b72010-05-05 07:34:42 +00007181 __ Ret();
7182}
7183
7184
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007185// On entry r0 (rhs) and r1 (lhs) are the values to be compared.
7186// On exit r0 is 0, positive or negative to indicate the result of
7187// the comparison.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007188void CompareStub::Generate(MacroAssembler* masm) {
7189 Label slow; // Call builtin.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007190 Label not_smis, both_loaded_as_doubles, lhs_not_nan;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007191
7192 // NOTICE! This code is only reached after a smi-fast-case check, so
7193 // it is certain that at least one operand isn't a smi.
7194
7195 // Handle the case where the objects are identical. Either returns the answer
7196 // or goes to slow. Only falls through if the objects were not identical.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007197 EmitIdenticalObjectComparison(masm, &slow, cc_, never_nan_nan_);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007198
7199 // If either is a Smi (we know that not both are), then they can only
7200 // be strictly equal if the other is a HeapNumber.
7201 ASSERT_EQ(0, kSmiTag);
7202 ASSERT_EQ(0, Smi::FromInt(0));
7203 __ and_(r2, r0, Operand(r1));
7204 __ tst(r2, Operand(kSmiTagMask));
7205 __ b(ne, &not_smis);
7206 // One operand is a smi. EmitSmiNonsmiComparison generates code that can:
7207 // 1) Return the answer.
7208 // 2) Go to slow.
7209 // 3) Fall through to both_loaded_as_doubles.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007210 // 4) Jump to lhs_not_nan.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007211 // In cases 3 and 4 we have found out we were dealing with a number-number
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007212 // comparison. If VFP3 is supported the double values of the numbers have
7213 // been loaded into d7 and d6. Otherwise, the double values have been loaded
7214 // into r0, r1, r2, and r3.
7215 EmitSmiNonsmiComparison(masm, &lhs_not_nan, &slow, strict_);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007216
7217 __ bind(&both_loaded_as_doubles);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007218 // The arguments have been converted to doubles and stored in d6 and d7, if
7219 // VFP3 is supported, or in r0, r1, r2, and r3.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007220 if (CpuFeatures::IsSupported(VFP3)) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007221 __ bind(&lhs_not_nan);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007222 CpuFeatures::Scope scope(VFP3);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007223 Label no_nan;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007224 // ARMv7 VFP3 instructions to implement double precision comparison.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007225 __ vcmp(d7, d6);
7226 __ vmrs(pc); // Move vector status bits to normal status bits.
7227 Label nan;
7228 __ b(vs, &nan);
7229 __ mov(r0, Operand(EQUAL), LeaveCC, eq);
7230 __ mov(r0, Operand(LESS), LeaveCC, lt);
7231 __ mov(r0, Operand(GREATER), LeaveCC, gt);
7232 __ mov(pc, Operand(lr));
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007233
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007234 __ bind(&nan);
7235 // If one of the sides was a NaN then the v flag is set. Load r0 with
7236 // whatever it takes to make the comparison fail, since comparisons with NaN
7237 // always fail.
7238 if (cc_ == lt || cc_ == le) {
7239 __ mov(r0, Operand(GREATER));
7240 } else {
7241 __ mov(r0, Operand(LESS));
7242 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007243 __ mov(pc, Operand(lr));
7244 } else {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007245 // Checks for NaN in the doubles we have loaded. Can return the answer or
7246 // fall through if neither is a NaN. Also binds lhs_not_nan.
7247 EmitNanCheck(masm, &lhs_not_nan, cc_);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007248 // Compares two doubles in r0, r1, r2, r3 that are not NaNs. Returns the
7249 // answer. Never falls through.
7250 EmitTwoNonNanDoubleComparison(masm, cc_);
7251 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007252
7253 __ bind(&not_smis);
7254 // At this point we know we are dealing with two different objects,
7255 // and neither of them is a Smi. The objects are in r0 and r1.
7256 if (strict_) {
7257 // This returns non-equal for some object types, or falls through if it
7258 // was not lucky.
7259 EmitStrictTwoHeapObjectCompare(masm);
7260 }
7261
7262 Label check_for_symbols;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007263 Label flat_string_check;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007264 // Check for heap-number-heap-number comparison. Can jump to slow case,
7265 // or load both doubles into r0, r1, r2, r3 and jump to the code that handles
7266 // that case. If the inputs are not doubles then jumps to check_for_symbols.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007267 // In this case r2 will contain the type of r0. Never falls through.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007268 EmitCheckForTwoHeapNumbers(masm,
7269 &both_loaded_as_doubles,
7270 &check_for_symbols,
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007271 &flat_string_check);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007272
7273 __ bind(&check_for_symbols);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007274 // In the strict case the EmitStrictTwoHeapObjectCompare already took care of
7275 // symbols.
7276 if (cc_ == eq && !strict_) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007277 // Either jumps to slow or returns the answer. Assumes that r2 is the type
7278 // of r0 on entry.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007279 EmitCheckForSymbols(masm, &flat_string_check);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007280 }
7281
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007282 // Check for both being sequential ASCII strings, and inline if that is the
7283 // case.
7284 __ bind(&flat_string_check);
7285
7286 __ JumpIfNonSmisNotBothSequentialAsciiStrings(r0, r1, r2, r3, &slow);
7287
7288 __ IncrementCounter(&Counters::string_compare_native, 1, r2, r3);
7289 StringCompareStub::GenerateCompareFlatAsciiStrings(masm,
7290 r1,
7291 r0,
7292 r2,
7293 r3,
7294 r4,
7295 r5);
7296 // Never falls through to here.
7297
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007298 __ bind(&slow);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007299
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00007300 __ Push(r1, r0);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007301 // Figure out which native to call and setup the arguments.
7302 Builtins::JavaScript native;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007303 if (cc_ == eq) {
7304 native = strict_ ? Builtins::STRICT_EQUALS : Builtins::EQUALS;
7305 } else {
7306 native = Builtins::COMPARE;
7307 int ncr; // NaN compare result
7308 if (cc_ == lt || cc_ == le) {
7309 ncr = GREATER;
7310 } else {
7311 ASSERT(cc_ == gt || cc_ == ge); // remaining cases
7312 ncr = LESS;
7313 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007314 __ mov(r0, Operand(Smi::FromInt(ncr)));
7315 __ push(r0);
7316 }
7317
7318 // Call the native; it returns -1 (less), 0 (equal), or 1 (greater)
7319 // tagged as a small integer.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007320 __ InvokeBuiltin(native, JUMP_JS);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007321}
7322
7323
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00007324// We fall into this code if the operands were Smis, but the result was
7325// not (eg. overflow). We branch into this code (to the not_smi label) if
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007326// the operands were not both Smi. The operands are in r0 and r1. In order
7327// to call the C-implemented binary fp operation routines we need to end up
7328// with the double precision floating point operands in r0 and r1 (for the
7329// value in r1) and r2 and r3 (for the value in r0).
ager@chromium.org357bf652010-04-12 11:30:10 +00007330void GenericBinaryOpStub::HandleBinaryOpSlowCases(
7331 MacroAssembler* masm,
7332 Label* not_smi,
7333 Register lhs,
7334 Register rhs,
7335 const Builtins::JavaScript& builtin) {
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00007336 Label slow, slow_reverse, do_the_call;
ager@chromium.org357bf652010-04-12 11:30:10 +00007337 bool use_fp_registers = CpuFeatures::IsSupported(VFP3) && Token::MOD != op_;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007338
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00007339 ASSERT((lhs.is(r0) && rhs.is(r1)) || (lhs.is(r1) && rhs.is(r0)));
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00007340 Register heap_number_map = r6;
ager@chromium.org357bf652010-04-12 11:30:10 +00007341
7342 if (ShouldGenerateSmiCode()) {
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00007343 __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
7344
ager@chromium.org357bf652010-04-12 11:30:10 +00007345 // Smi-smi case (overflow).
7346 // Since both are Smis there is no heap number to overwrite, so allocate.
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00007347 // The new heap number is in r5. r3 and r7 are scratch.
7348 __ AllocateHeapNumber(
7349 r5, r3, r7, heap_number_map, lhs.is(r0) ? &slow_reverse : &slow);
ager@chromium.org357bf652010-04-12 11:30:10 +00007350
7351 // If we have floating point hardware, inline ADD, SUB, MUL, and DIV,
7352 // using registers d7 and d6 for the double values.
7353 if (use_fp_registers) {
7354 CpuFeatures::Scope scope(VFP3);
7355 __ mov(r7, Operand(rhs, ASR, kSmiTagSize));
7356 __ vmov(s15, r7);
7357 __ vcvt_f64_s32(d7, s15);
7358 __ mov(r7, Operand(lhs, ASR, kSmiTagSize));
7359 __ vmov(s13, r7);
7360 __ vcvt_f64_s32(d6, s13);
7361 } else {
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00007362 // Write Smi from rhs to r3 and r2 in double format. r3 is scratch.
ager@chromium.org357bf652010-04-12 11:30:10 +00007363 __ mov(r7, Operand(rhs));
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00007364 ConvertToDoubleStub stub1(r3, r2, r7, r9);
ager@chromium.org357bf652010-04-12 11:30:10 +00007365 __ push(lr);
7366 __ Call(stub1.GetCode(), RelocInfo::CODE_TARGET);
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00007367 // Write Smi from lhs to r1 and r0 in double format. r9 is scratch.
ager@chromium.org357bf652010-04-12 11:30:10 +00007368 __ mov(r7, Operand(lhs));
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00007369 ConvertToDoubleStub stub2(r1, r0, r7, r9);
ager@chromium.org357bf652010-04-12 11:30:10 +00007370 __ Call(stub2.GetCode(), RelocInfo::CODE_TARGET);
7371 __ pop(lr);
7372 }
7373 __ jmp(&do_the_call); // Tail call. No return.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007374 }
7375
ager@chromium.org357bf652010-04-12 11:30:10 +00007376 // We branch here if at least one of r0 and r1 is not a Smi.
7377 __ bind(not_smi);
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00007378 __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007379
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00007380 // After this point we have the left hand side in r1 and the right hand side
7381 // in r0.
ager@chromium.org357bf652010-04-12 11:30:10 +00007382 if (lhs.is(r0)) {
7383 __ Swap(r0, r1, ip);
7384 }
7385
7386 if (ShouldGenerateFPCode()) {
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00007387 Label r0_is_smi, r1_is_smi, finished_loading_r0, finished_loading_r1;
7388
ager@chromium.org357bf652010-04-12 11:30:10 +00007389 if (runtime_operands_type_ == BinaryOpIC::DEFAULT) {
7390 switch (op_) {
7391 case Token::ADD:
7392 case Token::SUB:
7393 case Token::MUL:
7394 case Token::DIV:
7395 GenerateTypeTransition(masm);
7396 break;
7397
7398 default:
7399 break;
7400 }
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00007401 // Restore heap number map register.
7402 __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
ager@chromium.org357bf652010-04-12 11:30:10 +00007403 }
7404
7405 if (mode_ == NO_OVERWRITE) {
7406 // In the case where there is no chance of an overwritable float we may as
7407 // well do the allocation immediately while r0 and r1 are untouched.
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00007408 __ AllocateHeapNumber(r5, r3, r7, heap_number_map, &slow);
ager@chromium.org357bf652010-04-12 11:30:10 +00007409 }
7410
7411 // Move r0 to a double in r2-r3.
7412 __ tst(r0, Operand(kSmiTagMask));
7413 __ b(eq, &r0_is_smi); // It's a Smi so don't check it's a heap number.
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00007414 __ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset));
7415 __ AssertRegisterIsRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
7416 __ cmp(r4, heap_number_map);
ager@chromium.org357bf652010-04-12 11:30:10 +00007417 __ b(ne, &slow);
7418 if (mode_ == OVERWRITE_RIGHT) {
7419 __ mov(r5, Operand(r0)); // Overwrite this heap number.
7420 }
7421 if (use_fp_registers) {
7422 CpuFeatures::Scope scope(VFP3);
7423 // Load the double from tagged HeapNumber r0 to d7.
7424 __ sub(r7, r0, Operand(kHeapObjectTag));
7425 __ vldr(d7, r7, HeapNumber::kValueOffset);
7426 } else {
7427 // Calling convention says that second double is in r2 and r3.
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00007428 __ Ldrd(r2, r3, FieldMemOperand(r0, HeapNumber::kValueOffset));
ager@chromium.org357bf652010-04-12 11:30:10 +00007429 }
7430 __ jmp(&finished_loading_r0);
7431 __ bind(&r0_is_smi);
7432 if (mode_ == OVERWRITE_RIGHT) {
7433 // We can't overwrite a Smi so get address of new heap number into r5.
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00007434 __ AllocateHeapNumber(r5, r4, r7, heap_number_map, &slow);
ager@chromium.org357bf652010-04-12 11:30:10 +00007435 }
7436
7437 if (use_fp_registers) {
7438 CpuFeatures::Scope scope(VFP3);
7439 // Convert smi in r0 to double in d7.
7440 __ mov(r7, Operand(r0, ASR, kSmiTagSize));
7441 __ vmov(s15, r7);
7442 __ vcvt_f64_s32(d7, s15);
7443 } else {
7444 // Write Smi from r0 to r3 and r2 in double format.
7445 __ mov(r7, Operand(r0));
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00007446 ConvertToDoubleStub stub3(r3, r2, r7, r4);
ager@chromium.org357bf652010-04-12 11:30:10 +00007447 __ push(lr);
7448 __ Call(stub3.GetCode(), RelocInfo::CODE_TARGET);
7449 __ pop(lr);
7450 }
7451
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00007452 // HEAP_NUMBERS stub is slower than GENERIC on a pair of smis.
7453 // r0 is known to be a smi. If r1 is also a smi then switch to GENERIC.
7454 Label r1_is_not_smi;
7455 if (runtime_operands_type_ == BinaryOpIC::HEAP_NUMBERS) {
7456 __ tst(r1, Operand(kSmiTagMask));
7457 __ b(ne, &r1_is_not_smi);
7458 GenerateTypeTransition(masm);
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00007459 // Restore heap number map register.
7460 __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00007461 __ jmp(&r1_is_smi);
7462 }
7463
ager@chromium.org357bf652010-04-12 11:30:10 +00007464 __ bind(&finished_loading_r0);
7465
7466 // Move r1 to a double in r0-r1.
7467 __ tst(r1, Operand(kSmiTagMask));
7468 __ 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 +00007469 __ bind(&r1_is_not_smi);
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00007470 __ ldr(r4, FieldMemOperand(r1, HeapNumber::kMapOffset));
7471 __ AssertRegisterIsRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
7472 __ cmp(r4, heap_number_map);
ager@chromium.org357bf652010-04-12 11:30:10 +00007473 __ b(ne, &slow);
7474 if (mode_ == OVERWRITE_LEFT) {
7475 __ mov(r5, Operand(r1)); // Overwrite this heap number.
7476 }
7477 if (use_fp_registers) {
7478 CpuFeatures::Scope scope(VFP3);
7479 // Load the double from tagged HeapNumber r1 to d6.
7480 __ sub(r7, r1, Operand(kHeapObjectTag));
7481 __ vldr(d6, r7, HeapNumber::kValueOffset);
7482 } else {
7483 // Calling convention says that first double is in r0 and r1.
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00007484 __ Ldrd(r0, r1, FieldMemOperand(r1, HeapNumber::kValueOffset));
ager@chromium.org357bf652010-04-12 11:30:10 +00007485 }
7486 __ jmp(&finished_loading_r1);
7487 __ bind(&r1_is_smi);
7488 if (mode_ == OVERWRITE_LEFT) {
7489 // We can't overwrite a Smi so get address of new heap number into r5.
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00007490 __ AllocateHeapNumber(r5, r4, r7, heap_number_map, &slow);
ager@chromium.org357bf652010-04-12 11:30:10 +00007491 }
7492
7493 if (use_fp_registers) {
7494 CpuFeatures::Scope scope(VFP3);
7495 // Convert smi in r1 to double in d6.
7496 __ mov(r7, Operand(r1, ASR, kSmiTagSize));
7497 __ vmov(s13, r7);
7498 __ vcvt_f64_s32(d6, s13);
7499 } else {
7500 // Write Smi from r1 to r1 and r0 in double format.
7501 __ mov(r7, Operand(r1));
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00007502 ConvertToDoubleStub stub4(r1, r0, r7, r9);
ager@chromium.org357bf652010-04-12 11:30:10 +00007503 __ push(lr);
7504 __ Call(stub4.GetCode(), RelocInfo::CODE_TARGET);
7505 __ pop(lr);
7506 }
7507
7508 __ bind(&finished_loading_r1);
7509
7510 __ bind(&do_the_call);
7511 // If we are inlining the operation using VFP3 instructions for
7512 // add, subtract, multiply, or divide, the arguments are in d6 and d7.
7513 if (use_fp_registers) {
7514 CpuFeatures::Scope scope(VFP3);
7515 // ARMv7 VFP3 instructions to implement
7516 // double precision, add, subtract, multiply, divide.
7517
7518 if (Token::MUL == op_) {
7519 __ vmul(d5, d6, d7);
7520 } else if (Token::DIV == op_) {
7521 __ vdiv(d5, d6, d7);
7522 } else if (Token::ADD == op_) {
7523 __ vadd(d5, d6, d7);
7524 } else if (Token::SUB == op_) {
7525 __ vsub(d5, d6, d7);
7526 } else {
7527 UNREACHABLE();
7528 }
7529 __ sub(r0, r5, Operand(kHeapObjectTag));
7530 __ vstr(d5, r0, HeapNumber::kValueOffset);
7531 __ add(r0, r0, Operand(kHeapObjectTag));
7532 __ mov(pc, lr);
7533 } else {
7534 // If we did not inline the operation, then the arguments are in:
7535 // r0: Left value (least significant part of mantissa).
7536 // r1: Left value (sign, exponent, top of mantissa).
7537 // r2: Right value (least significant part of mantissa).
7538 // r3: Right value (sign, exponent, top of mantissa).
7539 // r5: Address of heap number for result.
7540
7541 __ push(lr); // For later.
7542 __ PrepareCallCFunction(4, r4); // Two doubles count as 4 arguments.
7543 // Call C routine that may not cause GC or other trouble. r5 is callee
7544 // save.
7545 __ CallCFunction(ExternalReference::double_fp_operation(op_), 4);
7546 // Store answer in the overwritable heap number.
7547 #if !defined(USE_ARM_EABI)
7548 // Double returned in fp coprocessor register 0 and 1, encoded as register
7549 // cr8. Offsets must be divisible by 4 for coprocessor so we need to
7550 // substract the tag from r5.
7551 __ sub(r4, r5, Operand(kHeapObjectTag));
7552 __ stc(p1, cr8, MemOperand(r4, HeapNumber::kValueOffset));
7553 #else
7554 // Double returned in registers 0 and 1.
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00007555 __ Strd(r0, r1, FieldMemOperand(r5, HeapNumber::kValueOffset));
ager@chromium.org357bf652010-04-12 11:30:10 +00007556 #endif
7557 __ mov(r0, Operand(r5));
7558 // And we are done.
7559 __ pop(pc);
7560 }
7561 }
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00007562
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00007563 if (lhs.is(r0)) {
7564 __ b(&slow);
7565 __ bind(&slow_reverse);
7566 __ Swap(r0, r1, ip);
7567 }
7568
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00007569 heap_number_map = no_reg; // Don't use this any more from here on.
7570
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007571 // We jump to here if something goes wrong (one param is not a number of any
7572 // sort or new-space allocation fails).
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007573 __ bind(&slow);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007574
7575 // Push arguments to the stack
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00007576 __ Push(r1, r0);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007577
ager@chromium.org357bf652010-04-12 11:30:10 +00007578 if (Token::ADD == op_) {
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007579 // Test for string arguments before calling runtime.
7580 // r1 : first argument
7581 // r0 : second argument
7582 // sp[0] : second argument
ager@chromium.org5c838252010-02-19 08:53:10 +00007583 // sp[4] : first argument
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007584
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007585 Label not_strings, not_string1, string1, string1_smi2;
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007586 __ tst(r1, Operand(kSmiTagMask));
7587 __ b(eq, &not_string1);
7588 __ CompareObjectType(r1, r2, r2, FIRST_NONSTRING_TYPE);
7589 __ b(ge, &not_string1);
7590
7591 // First argument is a a string, test second.
7592 __ tst(r0, Operand(kSmiTagMask));
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007593 __ b(eq, &string1_smi2);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007594 __ CompareObjectType(r0, r2, r2, FIRST_NONSTRING_TYPE);
7595 __ b(ge, &string1);
7596
7597 // First and second argument are strings.
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007598 StringAddStub string_add_stub(NO_STRING_CHECK_IN_STUB);
7599 __ TailCallStub(&string_add_stub);
7600
7601 __ bind(&string1_smi2);
7602 // First argument is a string, second is a smi. Try to lookup the number
7603 // string for the smi in the number string cache.
7604 NumberToStringStub::GenerateLookupNumberStringCache(
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00007605 masm, r0, r2, r4, r5, r6, true, &string1);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007606
7607 // Replace second argument on stack and tailcall string add stub to make
7608 // the result.
7609 __ str(r2, MemOperand(sp, 0));
7610 __ TailCallStub(&string_add_stub);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007611
7612 // Only first argument is a string.
7613 __ bind(&string1);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007614 __ InvokeBuiltin(Builtins::STRING_ADD_LEFT, JUMP_JS);
7615
7616 // First argument was not a string, test second.
7617 __ bind(&not_string1);
7618 __ tst(r0, Operand(kSmiTagMask));
7619 __ b(eq, &not_strings);
7620 __ CompareObjectType(r0, r2, r2, FIRST_NONSTRING_TYPE);
7621 __ b(ge, &not_strings);
7622
7623 // Only second argument is a string.
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007624 __ InvokeBuiltin(Builtins::STRING_ADD_RIGHT, JUMP_JS);
7625
7626 __ bind(&not_strings);
7627 }
7628
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007629 __ InvokeBuiltin(builtin, JUMP_JS); // Tail call. No return.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007630}
7631
7632
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007633// Tries to get a signed int32 out of a double precision floating point heap
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007634// number. Rounds towards 0. Fastest for doubles that are in the ranges
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007635// -0x7fffffff to -0x40000000 or 0x40000000 to 0x7fffffff. This corresponds
7636// almost to the range of signed int32 values that are not Smis. Jumps to the
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007637// label 'slow' if the double isn't in the range -0x80000000.0 to 0x80000000.0
7638// (excluding the endpoints).
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007639static void GetInt32(MacroAssembler* masm,
7640 Register source,
7641 Register dest,
7642 Register scratch,
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007643 Register scratch2,
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007644 Label* slow) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007645 Label right_exponent, done;
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007646 // Get exponent word.
7647 __ ldr(scratch, FieldMemOperand(source, HeapNumber::kExponentOffset));
7648 // Get exponent alone in scratch2.
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00007649 __ Ubfx(scratch2,
7650 scratch,
7651 HeapNumber::kExponentShift,
7652 HeapNumber::kExponentBits);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007653 // Load dest with zero. We use this either for the final shift or
7654 // for the answer.
7655 __ mov(dest, Operand(0));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007656 // Check whether the exponent matches a 32 bit signed int that is not a Smi.
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007657 // A non-Smi integer is 1.xxx * 2^30 so the exponent is 30 (biased). This is
7658 // the exponent that we are fastest at and also the highest exponent we can
7659 // handle here.
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00007660 const uint32_t non_smi_exponent = HeapNumber::kExponentBias + 30;
7661 // The non_smi_exponent, 0x41d, is too big for ARM's immediate field so we
7662 // split it up to avoid a constant pool entry. You can't do that in general
7663 // for cmp because of the overflow flag, but we know the exponent is in the
7664 // range 0-2047 so there is no overflow.
7665 int fudge_factor = 0x400;
7666 __ sub(scratch2, scratch2, Operand(fudge_factor));
7667 __ cmp(scratch2, Operand(non_smi_exponent - fudge_factor));
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007668 // If we have a match of the int32-but-not-Smi exponent then skip some logic.
7669 __ b(eq, &right_exponent);
7670 // If the exponent is higher than that then go to slow case. This catches
7671 // numbers that don't fit in a signed int32, infinities and NaNs.
7672 __ b(gt, slow);
7673
7674 // We know the exponent is smaller than 30 (biased). If it is less than
7675 // 0 (biased) then the number is smaller in magnitude than 1.0 * 2^0, ie
7676 // it rounds to zero.
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00007677 const uint32_t zero_exponent = HeapNumber::kExponentBias + 0;
7678 __ sub(scratch2, scratch2, Operand(zero_exponent - fudge_factor), SetCC);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007679 // Dest already has a Smi zero.
7680 __ b(lt, &done);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007681 if (!CpuFeatures::IsSupported(VFP3)) {
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00007682 // We have an exponent between 0 and 30 in scratch2. Subtract from 30 to
7683 // get how much to shift down.
7684 __ rsb(dest, scratch2, Operand(30));
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007685 }
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007686 __ bind(&right_exponent);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007687 if (CpuFeatures::IsSupported(VFP3)) {
7688 CpuFeatures::Scope scope(VFP3);
7689 // ARMv7 VFP3 instructions implementing double precision to integer
7690 // conversion using round to zero.
7691 __ ldr(scratch2, FieldMemOperand(source, HeapNumber::kMantissaOffset));
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00007692 __ vmov(d7, scratch2, scratch);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007693 __ vcvt_s32_f64(s15, d7);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00007694 __ vmov(dest, s15);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007695 } else {
7696 // Get the top bits of the mantissa.
7697 __ and_(scratch2, scratch, Operand(HeapNumber::kMantissaMask));
7698 // Put back the implicit 1.
7699 __ orr(scratch2, scratch2, Operand(1 << HeapNumber::kExponentShift));
7700 // Shift up the mantissa bits to take up the space the exponent used to
7701 // take. We just orred in the implicit bit so that took care of one and
7702 // we want to leave the sign bit 0 so we subtract 2 bits from the shift
7703 // distance.
7704 const int shift_distance = HeapNumber::kNonMantissaBitsInTopWord - 2;
7705 __ mov(scratch2, Operand(scratch2, LSL, shift_distance));
7706 // Put sign in zero flag.
7707 __ tst(scratch, Operand(HeapNumber::kSignMask));
7708 // Get the second half of the double. For some exponents we don't
7709 // actually need this because the bits get shifted out again, but
7710 // it's probably slower to test than just to do it.
7711 __ ldr(scratch, FieldMemOperand(source, HeapNumber::kMantissaOffset));
7712 // Shift down 22 bits to get the last 10 bits.
7713 __ orr(scratch, scratch2, Operand(scratch, LSR, 32 - shift_distance));
7714 // Move down according to the exponent.
7715 __ mov(dest, Operand(scratch, LSR, dest));
7716 // Fix sign if sign bit was set.
7717 __ rsb(dest, dest, Operand(0), LeaveCC, ne);
7718 }
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007719 __ bind(&done);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007720}
7721
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007722// For bitwise ops where the inputs are not both Smis we here try to determine
7723// whether both inputs are either Smis or at least heap numbers that can be
7724// represented by a 32 bit signed value. We truncate towards zero as required
7725// by the ES spec. If this is the case we do the bitwise op and see if the
7726// result is a Smi. If so, great, otherwise we try to find a heap number to
7727// write the answer into (either by allocating or by overwriting).
ager@chromium.org357bf652010-04-12 11:30:10 +00007728// On entry the operands are in lhs and rhs. On exit the answer is in r0.
7729void GenericBinaryOpStub::HandleNonSmiBitwiseOp(MacroAssembler* masm,
7730 Register lhs,
7731 Register rhs) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007732 Label slow, result_not_a_smi;
ager@chromium.org357bf652010-04-12 11:30:10 +00007733 Label rhs_is_smi, lhs_is_smi;
7734 Label done_checking_rhs, done_checking_lhs;
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007735
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00007736 Register heap_number_map = r6;
7737 __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
7738
ager@chromium.org357bf652010-04-12 11:30:10 +00007739 __ tst(lhs, Operand(kSmiTagMask));
7740 __ b(eq, &lhs_is_smi); // It's a Smi so don't check it's a heap number.
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00007741 __ ldr(r4, FieldMemOperand(lhs, HeapNumber::kMapOffset));
7742 __ cmp(r4, heap_number_map);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007743 __ b(ne, &slow);
ager@chromium.org357bf652010-04-12 11:30:10 +00007744 GetInt32(masm, lhs, r3, r5, r4, &slow);
7745 __ jmp(&done_checking_lhs);
7746 __ bind(&lhs_is_smi);
7747 __ mov(r3, Operand(lhs, ASR, 1));
7748 __ bind(&done_checking_lhs);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007749
ager@chromium.org357bf652010-04-12 11:30:10 +00007750 __ tst(rhs, Operand(kSmiTagMask));
7751 __ b(eq, &rhs_is_smi); // It's a Smi so don't check it's a heap number.
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00007752 __ ldr(r4, FieldMemOperand(rhs, HeapNumber::kMapOffset));
7753 __ cmp(r4, heap_number_map);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007754 __ b(ne, &slow);
ager@chromium.org357bf652010-04-12 11:30:10 +00007755 GetInt32(masm, rhs, r2, r5, r4, &slow);
7756 __ jmp(&done_checking_rhs);
7757 __ bind(&rhs_is_smi);
7758 __ mov(r2, Operand(rhs, ASR, 1));
7759 __ bind(&done_checking_rhs);
7760
7761 ASSERT(((lhs.is(r0) && rhs.is(r1)) || (lhs.is(r1) && rhs.is(r0))));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007762
7763 // r0 and r1: Original operands (Smi or heap numbers).
7764 // r2 and r3: Signed int32 operands.
7765 switch (op_) {
7766 case Token::BIT_OR: __ orr(r2, r2, Operand(r3)); break;
7767 case Token::BIT_XOR: __ eor(r2, r2, Operand(r3)); break;
7768 case Token::BIT_AND: __ and_(r2, r2, Operand(r3)); break;
7769 case Token::SAR:
7770 // Use only the 5 least significant bits of the shift count.
7771 __ and_(r2, r2, Operand(0x1f));
7772 __ mov(r2, Operand(r3, ASR, r2));
7773 break;
7774 case Token::SHR:
7775 // Use only the 5 least significant bits of the shift count.
7776 __ and_(r2, r2, Operand(0x1f));
7777 __ mov(r2, Operand(r3, LSR, r2), SetCC);
7778 // SHR is special because it is required to produce a positive answer.
7779 // The code below for writing into heap numbers isn't capable of writing
7780 // the register as an unsigned int so we go to slow case if we hit this
7781 // case.
7782 __ b(mi, &slow);
7783 break;
7784 case Token::SHL:
7785 // Use only the 5 least significant bits of the shift count.
7786 __ and_(r2, r2, Operand(0x1f));
7787 __ mov(r2, Operand(r3, LSL, r2));
7788 break;
7789 default: UNREACHABLE();
7790 }
7791 // check that the *signed* result fits in a smi
7792 __ add(r3, r2, Operand(0x40000000), SetCC);
7793 __ b(mi, &result_not_a_smi);
7794 __ mov(r0, Operand(r2, LSL, kSmiTagSize));
7795 __ Ret();
7796
7797 Label have_to_allocate, got_a_heap_number;
7798 __ bind(&result_not_a_smi);
7799 switch (mode_) {
7800 case OVERWRITE_RIGHT: {
ager@chromium.org357bf652010-04-12 11:30:10 +00007801 __ tst(rhs, Operand(kSmiTagMask));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007802 __ b(eq, &have_to_allocate);
ager@chromium.org357bf652010-04-12 11:30:10 +00007803 __ mov(r5, Operand(rhs));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007804 break;
7805 }
7806 case OVERWRITE_LEFT: {
ager@chromium.org357bf652010-04-12 11:30:10 +00007807 __ tst(lhs, Operand(kSmiTagMask));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007808 __ b(eq, &have_to_allocate);
ager@chromium.org357bf652010-04-12 11:30:10 +00007809 __ mov(r5, Operand(lhs));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007810 break;
7811 }
7812 case NO_OVERWRITE: {
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00007813 // Get a new heap number in r5. r4 and r7 are scratch.
7814 __ AllocateHeapNumber(r5, r4, r7, heap_number_map, &slow);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007815 }
7816 default: break;
7817 }
7818 __ bind(&got_a_heap_number);
7819 // r2: Answer as signed int32.
7820 // r5: Heap number to write answer into.
7821
7822 // Nothing can go wrong now, so move the heap number to r0, which is the
7823 // result.
7824 __ mov(r0, Operand(r5));
7825
7826 // Tail call that writes the int32 in r2 to the heap number in r0, using
7827 // r3 as scratch. r0 is preserved and returned.
7828 WriteInt32ToHeapNumberStub stub(r2, r0, r3);
7829 __ Jump(stub.GetCode(), RelocInfo::CODE_TARGET);
7830
7831 if (mode_ != NO_OVERWRITE) {
7832 __ bind(&have_to_allocate);
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00007833 // Get a new heap number in r5. r4 and r7 are scratch.
7834 __ AllocateHeapNumber(r5, r4, r7, heap_number_map, &slow);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007835 __ jmp(&got_a_heap_number);
7836 }
7837
7838 // If all else failed then we go to the runtime system.
7839 __ bind(&slow);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00007840 __ Push(lhs, rhs); // Restore stack.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007841 switch (op_) {
7842 case Token::BIT_OR:
7843 __ InvokeBuiltin(Builtins::BIT_OR, JUMP_JS);
7844 break;
7845 case Token::BIT_AND:
7846 __ InvokeBuiltin(Builtins::BIT_AND, JUMP_JS);
7847 break;
7848 case Token::BIT_XOR:
7849 __ InvokeBuiltin(Builtins::BIT_XOR, JUMP_JS);
7850 break;
7851 case Token::SAR:
7852 __ InvokeBuiltin(Builtins::SAR, JUMP_JS);
7853 break;
7854 case Token::SHR:
7855 __ InvokeBuiltin(Builtins::SHR, JUMP_JS);
7856 break;
7857 case Token::SHL:
7858 __ InvokeBuiltin(Builtins::SHL, JUMP_JS);
7859 break;
7860 default:
7861 UNREACHABLE();
7862 }
7863}
7864
7865
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007866// Can we multiply by x with max two shifts and an add.
7867// This answers yes to all integers from 2 to 10.
7868static bool IsEasyToMultiplyBy(int x) {
7869 if (x < 2) return false; // Avoid special cases.
7870 if (x > (Smi::kMaxValue + 1) >> 2) return false; // Almost always overflows.
7871 if (IsPowerOf2(x)) return true; // Simple shift.
7872 if (PopCountLessThanEqual2(x)) return true; // Shift and add and shift.
7873 if (IsPowerOf2(x + 1)) return true; // Patterns like 11111.
7874 return false;
7875}
7876
7877
7878// Can multiply by anything that IsEasyToMultiplyBy returns true for.
7879// Source and destination may be the same register. This routine does
7880// not set carry and overflow the way a mul instruction would.
7881static void MultiplyByKnownInt(MacroAssembler* masm,
7882 Register source,
7883 Register destination,
7884 int known_int) {
7885 if (IsPowerOf2(known_int)) {
7886 __ mov(destination, Operand(source, LSL, BitPosition(known_int)));
7887 } else if (PopCountLessThanEqual2(known_int)) {
7888 int first_bit = BitPosition(known_int);
7889 int second_bit = BitPosition(known_int ^ (1 << first_bit));
7890 __ add(destination, source, Operand(source, LSL, second_bit - first_bit));
7891 if (first_bit != 0) {
7892 __ mov(destination, Operand(destination, LSL, first_bit));
7893 }
7894 } else {
7895 ASSERT(IsPowerOf2(known_int + 1)); // Patterns like 1111.
7896 int the_bit = BitPosition(known_int + 1);
7897 __ rsb(destination, source, Operand(source, LSL, the_bit));
7898 }
7899}
7900
7901
7902// This function (as opposed to MultiplyByKnownInt) takes the known int in a
7903// a register for the cases where it doesn't know a good trick, and may deliver
7904// a result that needs shifting.
7905static void MultiplyByKnownInt2(
7906 MacroAssembler* masm,
7907 Register result,
7908 Register source,
7909 Register known_int_register, // Smi tagged.
7910 int known_int,
7911 int* required_shift) { // Including Smi tag shift
7912 switch (known_int) {
7913 case 3:
7914 __ add(result, source, Operand(source, LSL, 1));
7915 *required_shift = 1;
7916 break;
7917 case 5:
7918 __ add(result, source, Operand(source, LSL, 2));
7919 *required_shift = 1;
7920 break;
7921 case 6:
7922 __ add(result, source, Operand(source, LSL, 1));
7923 *required_shift = 2;
7924 break;
7925 case 7:
7926 __ rsb(result, source, Operand(source, LSL, 3));
7927 *required_shift = 1;
7928 break;
7929 case 9:
7930 __ add(result, source, Operand(source, LSL, 3));
7931 *required_shift = 1;
7932 break;
7933 case 10:
7934 __ add(result, source, Operand(source, LSL, 2));
7935 *required_shift = 2;
7936 break;
7937 default:
7938 ASSERT(!IsPowerOf2(known_int)); // That would be very inefficient.
7939 __ mul(result, source, known_int_register);
7940 *required_shift = 0;
7941 }
7942}
7943
7944
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00007945const char* GenericBinaryOpStub::GetName() {
7946 if (name_ != NULL) return name_;
7947 const int len = 100;
7948 name_ = Bootstrapper::AllocateAutoDeletedArray(len);
7949 if (name_ == NULL) return "OOM";
7950 const char* op_name = Token::Name(op_);
7951 const char* overwrite_name;
7952 switch (mode_) {
7953 case NO_OVERWRITE: overwrite_name = "Alloc"; break;
7954 case OVERWRITE_RIGHT: overwrite_name = "OverwriteRight"; break;
7955 case OVERWRITE_LEFT: overwrite_name = "OverwriteLeft"; break;
7956 default: overwrite_name = "UnknownOverwrite"; break;
7957 }
7958
7959 OS::SNPrintF(Vector<char>(name_, len),
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00007960 "GenericBinaryOpStub_%s_%s%s_%s",
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00007961 op_name,
7962 overwrite_name,
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00007963 specialized_on_rhs_ ? "_ConstantRhs" : "",
7964 BinaryOpIC::GetName(runtime_operands_type_));
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00007965 return name_;
7966}
7967
7968
ager@chromium.org5c838252010-02-19 08:53:10 +00007969
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007970void GenericBinaryOpStub::Generate(MacroAssembler* masm) {
ager@chromium.org357bf652010-04-12 11:30:10 +00007971 // lhs_ : x
7972 // rhs_ : y
7973 // r0 : result
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007974
ager@chromium.org357bf652010-04-12 11:30:10 +00007975 Register result = r0;
7976 Register lhs = lhs_;
7977 Register rhs = rhs_;
7978
7979 // This code can't cope with other register allocations yet.
7980 ASSERT(result.is(r0) &&
7981 ((lhs.is(r0) && rhs.is(r1)) ||
7982 (lhs.is(r1) && rhs.is(r0))));
7983
7984 Register smi_test_reg = VirtualFrame::scratch0();
7985 Register scratch = VirtualFrame::scratch1();
7986
7987 // All ops need to know whether we are dealing with two Smis. Set up
7988 // smi_test_reg to tell us that.
7989 if (ShouldGenerateSmiCode()) {
7990 __ orr(smi_test_reg, lhs, Operand(rhs));
7991 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007992
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007993 switch (op_) {
7994 case Token::ADD: {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007995 Label not_smi;
7996 // Fast path.
ager@chromium.org357bf652010-04-12 11:30:10 +00007997 if (ShouldGenerateSmiCode()) {
7998 ASSERT(kSmiTag == 0); // Adjust code below.
7999 __ tst(smi_test_reg, Operand(kSmiTagMask));
8000 __ b(ne, &not_smi);
8001 __ add(r0, r1, Operand(r0), SetCC); // Add y optimistically.
8002 // Return if no overflow.
8003 __ Ret(vc);
8004 __ sub(r0, r0, Operand(r1)); // Revert optimistic add.
8005 }
8006 HandleBinaryOpSlowCases(masm, &not_smi, lhs, rhs, Builtins::ADD);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008007 break;
8008 }
8009
8010 case Token::SUB: {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008011 Label not_smi;
8012 // Fast path.
ager@chromium.org357bf652010-04-12 11:30:10 +00008013 if (ShouldGenerateSmiCode()) {
8014 ASSERT(kSmiTag == 0); // Adjust code below.
8015 __ tst(smi_test_reg, Operand(kSmiTagMask));
8016 __ b(ne, &not_smi);
8017 if (lhs.is(r1)) {
8018 __ sub(r0, r1, Operand(r0), SetCC); // Subtract y optimistically.
8019 // Return if no overflow.
8020 __ Ret(vc);
8021 __ sub(r0, r1, Operand(r0)); // Revert optimistic subtract.
8022 } else {
8023 __ sub(r0, r0, Operand(r1), SetCC); // Subtract y optimistically.
8024 // Return if no overflow.
8025 __ Ret(vc);
8026 __ add(r0, r0, Operand(r1)); // Revert optimistic subtract.
8027 }
8028 }
8029 HandleBinaryOpSlowCases(masm, &not_smi, lhs, rhs, Builtins::SUB);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008030 break;
8031 }
8032
8033 case Token::MUL: {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008034 Label not_smi, slow;
ager@chromium.org357bf652010-04-12 11:30:10 +00008035 if (ShouldGenerateSmiCode()) {
8036 ASSERT(kSmiTag == 0); // adjust code below
8037 __ tst(smi_test_reg, Operand(kSmiTagMask));
8038 Register scratch2 = smi_test_reg;
8039 smi_test_reg = no_reg;
8040 __ b(ne, &not_smi);
8041 // Remove tag from one operand (but keep sign), so that result is Smi.
8042 __ mov(ip, Operand(rhs, ASR, kSmiTagSize));
8043 // Do multiplication
8044 // scratch = lower 32 bits of ip * lhs.
8045 __ smull(scratch, scratch2, lhs, ip);
8046 // Go slow on overflows (overflow bit is not set).
8047 __ mov(ip, Operand(scratch, ASR, 31));
8048 // No overflow if higher 33 bits are identical.
8049 __ cmp(ip, Operand(scratch2));
8050 __ b(ne, &slow);
8051 // Go slow on zero result to handle -0.
8052 __ tst(scratch, Operand(scratch));
8053 __ mov(result, Operand(scratch), LeaveCC, ne);
8054 __ Ret(ne);
8055 // We need -0 if we were multiplying a negative number with 0 to get 0.
8056 // We know one of them was zero.
8057 __ add(scratch2, rhs, Operand(lhs), SetCC);
8058 __ mov(result, Operand(Smi::FromInt(0)), LeaveCC, pl);
8059 __ Ret(pl); // Return Smi 0 if the non-zero one was positive.
8060 // Slow case. We fall through here if we multiplied a negative number
8061 // with 0, because that would mean we should produce -0.
8062 __ bind(&slow);
8063 }
8064 HandleBinaryOpSlowCases(masm, &not_smi, lhs, rhs, Builtins::MUL);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008065 break;
8066 }
8067
8068 case Token::DIV:
8069 case Token::MOD: {
8070 Label not_smi;
ager@chromium.org357bf652010-04-12 11:30:10 +00008071 if (ShouldGenerateSmiCode() && specialized_on_rhs_) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008072 Label smi_is_unsuitable;
ager@chromium.org357bf652010-04-12 11:30:10 +00008073 __ BranchOnNotSmi(lhs, &not_smi);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008074 if (IsPowerOf2(constant_rhs_)) {
8075 if (op_ == Token::MOD) {
ager@chromium.org357bf652010-04-12 11:30:10 +00008076 __ and_(rhs,
8077 lhs,
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008078 Operand(0x80000000u | ((constant_rhs_ << kSmiTagSize) - 1)),
8079 SetCC);
8080 // We now have the answer, but if the input was negative we also
8081 // have the sign bit. Our work is done if the result is
8082 // positive or zero:
ager@chromium.org357bf652010-04-12 11:30:10 +00008083 if (!rhs.is(r0)) {
8084 __ mov(r0, rhs, LeaveCC, pl);
8085 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008086 __ Ret(pl);
8087 // A mod of a negative left hand side must return a negative number.
8088 // Unfortunately if the answer is 0 then we must return -0. And we
ager@chromium.org357bf652010-04-12 11:30:10 +00008089 // already optimistically trashed rhs so we may need to restore it.
8090 __ eor(rhs, rhs, Operand(0x80000000u), SetCC);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008091 // Next two instructions are conditional on the answer being -0.
ager@chromium.org357bf652010-04-12 11:30:10 +00008092 __ mov(rhs, Operand(Smi::FromInt(constant_rhs_)), LeaveCC, eq);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008093 __ b(eq, &smi_is_unsuitable);
8094 // We need to subtract the dividend. Eg. -3 % 4 == -3.
ager@chromium.org357bf652010-04-12 11:30:10 +00008095 __ sub(result, rhs, Operand(Smi::FromInt(constant_rhs_)));
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008096 } else {
8097 ASSERT(op_ == Token::DIV);
ager@chromium.org357bf652010-04-12 11:30:10 +00008098 __ tst(lhs,
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008099 Operand(0x80000000u | ((constant_rhs_ << kSmiTagSize) - 1)));
8100 __ b(ne, &smi_is_unsuitable); // Go slow on negative or remainder.
8101 int shift = 0;
8102 int d = constant_rhs_;
8103 while ((d & 1) == 0) {
8104 d >>= 1;
8105 shift++;
8106 }
ager@chromium.org357bf652010-04-12 11:30:10 +00008107 __ mov(r0, Operand(lhs, LSR, shift));
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008108 __ bic(r0, r0, Operand(kSmiTagMask));
8109 }
8110 } else {
8111 // Not a power of 2.
ager@chromium.org357bf652010-04-12 11:30:10 +00008112 __ tst(lhs, Operand(0x80000000u));
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008113 __ b(ne, &smi_is_unsuitable);
8114 // Find a fixed point reciprocal of the divisor so we can divide by
8115 // multiplying.
8116 double divisor = 1.0 / constant_rhs_;
8117 int shift = 32;
8118 double scale = 4294967296.0; // 1 << 32.
8119 uint32_t mul;
8120 // Maximise the precision of the fixed point reciprocal.
8121 while (true) {
8122 mul = static_cast<uint32_t>(scale * divisor);
8123 if (mul >= 0x7fffffff) break;
8124 scale *= 2.0;
8125 shift++;
8126 }
8127 mul++;
ager@chromium.org357bf652010-04-12 11:30:10 +00008128 Register scratch2 = smi_test_reg;
8129 smi_test_reg = no_reg;
8130 __ mov(scratch2, Operand(mul));
8131 __ umull(scratch, scratch2, scratch2, lhs);
8132 __ mov(scratch2, Operand(scratch2, LSR, shift - 31));
8133 // scratch2 is lhs / rhs. scratch2 is not Smi tagged.
8134 // rhs is still the known rhs. rhs is Smi tagged.
8135 // lhs is still the unkown lhs. lhs is Smi tagged.
8136 int required_scratch_shift = 0; // Including the Smi tag shift of 1.
8137 // scratch = scratch2 * rhs.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008138 MultiplyByKnownInt2(masm,
ager@chromium.org357bf652010-04-12 11:30:10 +00008139 scratch,
8140 scratch2,
8141 rhs,
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008142 constant_rhs_,
ager@chromium.org357bf652010-04-12 11:30:10 +00008143 &required_scratch_shift);
8144 // scratch << required_scratch_shift is now the Smi tagged rhs *
8145 // (lhs / rhs) where / indicates integer division.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008146 if (op_ == Token::DIV) {
ager@chromium.org357bf652010-04-12 11:30:10 +00008147 __ cmp(lhs, Operand(scratch, LSL, required_scratch_shift));
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008148 __ b(ne, &smi_is_unsuitable); // There was a remainder.
ager@chromium.org357bf652010-04-12 11:30:10 +00008149 __ mov(result, Operand(scratch2, LSL, kSmiTagSize));
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008150 } else {
8151 ASSERT(op_ == Token::MOD);
ager@chromium.org357bf652010-04-12 11:30:10 +00008152 __ sub(result, lhs, Operand(scratch, LSL, required_scratch_shift));
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008153 }
8154 }
8155 __ Ret();
8156 __ bind(&smi_is_unsuitable);
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00008157 } else if (op_ == Token::MOD &&
8158 runtime_operands_type_ != BinaryOpIC::HEAP_NUMBERS &&
8159 runtime_operands_type_ != BinaryOpIC::STRINGS) {
8160 // Do generate a bit of smi code for modulus even though the default for
8161 // modulus is not to do it, but as the ARM processor has no coprocessor
8162 // support for modulus checking for smis makes sense.
8163 Label slow;
8164 ASSERT(!ShouldGenerateSmiCode());
8165 ASSERT(kSmiTag == 0); // Adjust code below.
8166 // Check for two positive smis.
8167 __ orr(smi_test_reg, lhs, Operand(rhs));
8168 __ tst(smi_test_reg, Operand(0x80000000u | kSmiTagMask));
8169 __ b(ne, &slow);
8170 // Check that rhs is a power of two and not zero.
8171 __ sub(scratch, rhs, Operand(1), SetCC);
8172 __ b(mi, &slow);
8173 __ tst(rhs, scratch);
8174 __ b(ne, &slow);
8175 // Calculate power of two modulus.
8176 __ and_(result, lhs, Operand(scratch));
8177 __ Ret();
8178 __ bind(&slow);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008179 }
ager@chromium.org357bf652010-04-12 11:30:10 +00008180 HandleBinaryOpSlowCases(
8181 masm,
8182 &not_smi,
8183 lhs,
8184 rhs,
8185 op_ == Token::MOD ? Builtins::MOD : Builtins::DIV);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008186 break;
8187 }
8188
8189 case Token::BIT_OR:
8190 case Token::BIT_AND:
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008191 case Token::BIT_XOR:
8192 case Token::SAR:
8193 case Token::SHR:
8194 case Token::SHL: {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008195 Label slow;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008196 ASSERT(kSmiTag == 0); // adjust code below
ager@chromium.org357bf652010-04-12 11:30:10 +00008197 __ tst(smi_test_reg, Operand(kSmiTagMask));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008198 __ b(ne, &slow);
ager@chromium.org357bf652010-04-12 11:30:10 +00008199 Register scratch2 = smi_test_reg;
8200 smi_test_reg = no_reg;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008201 switch (op_) {
ager@chromium.org357bf652010-04-12 11:30:10 +00008202 case Token::BIT_OR: __ orr(result, rhs, Operand(lhs)); break;
8203 case Token::BIT_AND: __ and_(result, rhs, Operand(lhs)); break;
8204 case Token::BIT_XOR: __ eor(result, rhs, Operand(lhs)); break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008205 case Token::SAR:
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008206 // Remove tags from right operand.
ager@chromium.org357bf652010-04-12 11:30:10 +00008207 __ GetLeastBitsFromSmi(scratch2, rhs, 5);
8208 __ mov(result, Operand(lhs, ASR, scratch2));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008209 // Smi tag result.
ager@chromium.org357bf652010-04-12 11:30:10 +00008210 __ bic(result, result, Operand(kSmiTagMask));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008211 break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008212 case Token::SHR:
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008213 // Remove tags from operands. We can't do this on a 31 bit number
8214 // because then the 0s get shifted into bit 30 instead of bit 31.
ager@chromium.org357bf652010-04-12 11:30:10 +00008215 __ mov(scratch, Operand(lhs, ASR, kSmiTagSize)); // x
8216 __ GetLeastBitsFromSmi(scratch2, rhs, 5);
8217 __ mov(scratch, Operand(scratch, LSR, scratch2));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008218 // Unsigned shift is not allowed to produce a negative number, so
8219 // check the sign bit and the sign bit after Smi tagging.
ager@chromium.org357bf652010-04-12 11:30:10 +00008220 __ tst(scratch, Operand(0xc0000000));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008221 __ b(ne, &slow);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008222 // Smi tag result.
ager@chromium.org357bf652010-04-12 11:30:10 +00008223 __ mov(result, Operand(scratch, LSL, kSmiTagSize));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008224 break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008225 case Token::SHL:
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008226 // Remove tags from operands.
ager@chromium.org357bf652010-04-12 11:30:10 +00008227 __ mov(scratch, Operand(lhs, ASR, kSmiTagSize)); // x
8228 __ GetLeastBitsFromSmi(scratch2, rhs, 5);
8229 __ mov(scratch, Operand(scratch, LSL, scratch2));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008230 // Check that the signed result fits in a Smi.
ager@chromium.org357bf652010-04-12 11:30:10 +00008231 __ add(scratch2, scratch, Operand(0x40000000), SetCC);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008232 __ b(mi, &slow);
ager@chromium.org357bf652010-04-12 11:30:10 +00008233 __ mov(result, Operand(scratch, LSL, kSmiTagSize));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008234 break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008235 default: UNREACHABLE();
8236 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008237 __ Ret();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008238 __ bind(&slow);
ager@chromium.org357bf652010-04-12 11:30:10 +00008239 HandleNonSmiBitwiseOp(masm, lhs, rhs);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008240 break;
8241 }
8242
8243 default: UNREACHABLE();
8244 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008245 // This code should be unreachable.
8246 __ stop("Unreachable");
ager@chromium.org357bf652010-04-12 11:30:10 +00008247
8248 // Generate an unreachable reference to the DEFAULT stub so that it can be
8249 // found at the end of this stub when clearing ICs at GC.
8250 // TODO(kaznacheev): Check performance impact and get rid of this.
8251 if (runtime_operands_type_ != BinaryOpIC::DEFAULT) {
8252 GenericBinaryOpStub uninit(MinorKey(), BinaryOpIC::DEFAULT);
8253 __ CallStub(&uninit);
8254 }
8255}
8256
8257
8258void GenericBinaryOpStub::GenerateTypeTransition(MacroAssembler* masm) {
8259 Label get_result;
8260
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00008261 __ Push(r1, r0);
ager@chromium.org357bf652010-04-12 11:30:10 +00008262
8263 // Internal frame is necessary to handle exceptions properly.
8264 __ EnterInternalFrame();
8265 // Call the stub proper to get the result in r0.
8266 __ Call(&get_result);
8267 __ LeaveInternalFrame();
8268
8269 __ push(r0);
8270
8271 __ mov(r0, Operand(Smi::FromInt(MinorKey())));
8272 __ push(r0);
8273 __ mov(r0, Operand(Smi::FromInt(op_)));
8274 __ push(r0);
8275 __ mov(r0, Operand(Smi::FromInt(runtime_operands_type_)));
8276 __ push(r0);
8277
8278 __ TailCallExternalReference(
8279 ExternalReference(IC_Utility(IC::kBinaryOp_Patch)),
8280 6,
8281 1);
8282
8283 // The entry point for the result calculation is assumed to be immediately
8284 // after this sequence.
8285 __ bind(&get_result);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008286}
8287
8288
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00008289Handle<Code> GetBinaryOpStub(int key, BinaryOpIC::TypeInfo type_info) {
ager@chromium.org357bf652010-04-12 11:30:10 +00008290 GenericBinaryOpStub stub(key, type_info);
8291 return stub.GetCode();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00008292}
8293
8294
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008295void TranscendentalCacheStub::Generate(MacroAssembler* masm) {
8296 // Argument is a number and is on stack and in r0.
8297 Label runtime_call;
8298 Label input_not_smi;
8299 Label loaded;
8300
8301 if (CpuFeatures::IsSupported(VFP3)) {
8302 // Load argument and check if it is a smi.
8303 __ BranchOnNotSmi(r0, &input_not_smi);
8304
8305 CpuFeatures::Scope scope(VFP3);
8306 // Input is a smi. Convert to double and load the low and high words
8307 // of the double into r2, r3.
8308 __ IntegerToDoubleConversionWithVFP3(r0, r3, r2);
8309 __ b(&loaded);
8310
8311 __ bind(&input_not_smi);
8312 // Check if input is a HeapNumber.
8313 __ CheckMap(r0,
8314 r1,
8315 Heap::kHeapNumberMapRootIndex,
8316 &runtime_call,
8317 true);
8318 // Input is a HeapNumber. Load it to a double register and store the
8319 // low and high words into r2, r3.
8320 __ Ldrd(r2, r3, FieldMemOperand(r0, HeapNumber::kValueOffset));
8321
8322 __ bind(&loaded);
8323 // r2 = low 32 bits of double value
8324 // r3 = high 32 bits of double value
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00008325 // Compute hash (the shifts are arithmetic):
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008326 // h = (low ^ high); h ^= h >> 16; h ^= h >> 8; h = h & (cacheSize - 1);
8327 __ eor(r1, r2, Operand(r3));
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00008328 __ eor(r1, r1, Operand(r1, ASR, 16));
8329 __ eor(r1, r1, Operand(r1, ASR, 8));
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008330 ASSERT(IsPowerOf2(TranscendentalCache::kCacheSize));
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00008331 __ And(r1, r1, Operand(TranscendentalCache::kCacheSize - 1));
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008332
8333 // r2 = low 32 bits of double value.
8334 // r3 = high 32 bits of double value.
8335 // r1 = TranscendentalCache::hash(double value).
8336 __ mov(r0,
8337 Operand(ExternalReference::transcendental_cache_array_address()));
8338 // r0 points to cache array.
8339 __ ldr(r0, MemOperand(r0, type_ * sizeof(TranscendentalCache::caches_[0])));
8340 // r0 points to the cache for the type type_.
8341 // If NULL, the cache hasn't been initialized yet, so go through runtime.
8342 __ cmp(r0, Operand(0));
8343 __ b(eq, &runtime_call);
8344
8345#ifdef DEBUG
8346 // Check that the layout of cache elements match expectations.
8347 { TranscendentalCache::Element test_elem[2];
8348 char* elem_start = reinterpret_cast<char*>(&test_elem[0]);
8349 char* elem2_start = reinterpret_cast<char*>(&test_elem[1]);
8350 char* elem_in0 = reinterpret_cast<char*>(&(test_elem[0].in[0]));
8351 char* elem_in1 = reinterpret_cast<char*>(&(test_elem[0].in[1]));
8352 char* elem_out = reinterpret_cast<char*>(&(test_elem[0].output));
8353 CHECK_EQ(12, elem2_start - elem_start); // Two uint_32's and a pointer.
8354 CHECK_EQ(0, elem_in0 - elem_start);
8355 CHECK_EQ(kIntSize, elem_in1 - elem_start);
8356 CHECK_EQ(2 * kIntSize, elem_out - elem_start);
8357 }
8358#endif
8359
8360 // Find the address of the r1'st entry in the cache, i.e., &r0[r1*12].
8361 __ add(r1, r1, Operand(r1, LSL, 1));
8362 __ add(r0, r0, Operand(r1, LSL, 2));
8363 // Check if cache matches: Double value is stored in uint32_t[2] array.
8364 __ ldm(ia, r0, r4.bit()| r5.bit() | r6.bit());
8365 __ cmp(r2, r4);
8366 __ b(ne, &runtime_call);
8367 __ cmp(r3, r5);
8368 __ b(ne, &runtime_call);
8369 // Cache hit. Load result, pop argument and return.
8370 __ mov(r0, Operand(r6));
8371 __ pop();
8372 __ Ret();
8373 }
8374
8375 __ bind(&runtime_call);
8376 __ TailCallExternalReference(ExternalReference(RuntimeFunction()), 1, 1);
8377}
8378
8379
8380Runtime::FunctionId TranscendentalCacheStub::RuntimeFunction() {
8381 switch (type_) {
8382 // Add more cases when necessary.
8383 case TranscendentalCache::SIN: return Runtime::kMath_sin;
8384 case TranscendentalCache::COS: return Runtime::kMath_cos;
8385 default:
8386 UNIMPLEMENTED();
8387 return Runtime::kAbort;
8388 }
8389}
8390
8391
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008392void StackCheckStub::Generate(MacroAssembler* masm) {
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00008393 // Do tail-call to runtime routine. Runtime routines expect at least one
8394 // argument, so give it a Smi.
8395 __ mov(r0, Operand(Smi::FromInt(0)));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008396 __ push(r0);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00008397 __ TailCallRuntime(Runtime::kStackGuard, 1, 1);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008398
8399 __ StubReturn(1);
8400}
8401
8402
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00008403void GenericUnaryOpStub::Generate(MacroAssembler* masm) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008404 Label slow, done;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00008405
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00008406 Register heap_number_map = r6;
8407 __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
8408
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008409 if (op_ == Token::SUB) {
8410 // Check whether the value is a smi.
8411 Label try_float;
8412 __ tst(r0, Operand(kSmiTagMask));
8413 __ b(ne, &try_float);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008414
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008415 // Go slow case if the value of the expression is zero
8416 // to make sure that we switch between 0 and -0.
8417 __ cmp(r0, Operand(0));
8418 __ b(eq, &slow);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008419
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008420 // The value of the expression is a smi that is not zero. Try
8421 // optimistic subtraction '0 - value'.
8422 __ rsb(r1, r0, Operand(0), SetCC);
8423 __ b(vs, &slow);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008424
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008425 __ mov(r0, Operand(r1)); // Set r0 to result.
8426 __ b(&done);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008427
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008428 __ bind(&try_float);
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00008429 __ ldr(r1, FieldMemOperand(r0, HeapObject::kMapOffset));
8430 __ AssertRegisterIsRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
8431 __ cmp(r1, heap_number_map);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008432 __ b(ne, &slow);
8433 // r0 is a heap number. Get a new heap number in r1.
8434 if (overwrite_) {
8435 __ ldr(r2, FieldMemOperand(r0, HeapNumber::kExponentOffset));
8436 __ eor(r2, r2, Operand(HeapNumber::kSignMask)); // Flip sign.
8437 __ str(r2, FieldMemOperand(r0, HeapNumber::kExponentOffset));
8438 } else {
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00008439 __ AllocateHeapNumber(r1, r2, r3, r6, &slow);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008440 __ ldr(r3, FieldMemOperand(r0, HeapNumber::kMantissaOffset));
8441 __ ldr(r2, FieldMemOperand(r0, HeapNumber::kExponentOffset));
8442 __ str(r3, FieldMemOperand(r1, HeapNumber::kMantissaOffset));
8443 __ eor(r2, r2, Operand(HeapNumber::kSignMask)); // Flip sign.
8444 __ str(r2, FieldMemOperand(r1, HeapNumber::kExponentOffset));
8445 __ mov(r0, Operand(r1));
8446 }
8447 } else if (op_ == Token::BIT_NOT) {
8448 // Check if the operand is a heap number.
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00008449 __ ldr(r1, FieldMemOperand(r0, HeapObject::kMapOffset));
8450 __ AssertRegisterIsRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
8451 __ cmp(r1, heap_number_map);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008452 __ b(ne, &slow);
8453
8454 // Convert the heap number is r0 to an untagged integer in r1.
8455 GetInt32(masm, r0, r1, r2, r3, &slow);
8456
8457 // Do the bitwise operation (move negated) and check if the result
8458 // fits in a smi.
8459 Label try_float;
8460 __ mvn(r1, Operand(r1));
8461 __ add(r2, r1, Operand(0x40000000), SetCC);
8462 __ b(mi, &try_float);
8463 __ mov(r0, Operand(r1, LSL, kSmiTagSize));
8464 __ b(&done);
8465
8466 __ bind(&try_float);
8467 if (!overwrite_) {
8468 // Allocate a fresh heap number, but don't overwrite r0 until
8469 // we're sure we can do it without going through the slow case
8470 // that needs the value in r0.
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00008471 __ AllocateHeapNumber(r2, r3, r4, r6, &slow);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008472 __ mov(r0, Operand(r2));
8473 }
8474
8475 // WriteInt32ToHeapNumberStub does not trigger GC, so we do not
8476 // have to set up a frame.
8477 WriteInt32ToHeapNumberStub stub(r1, r0, r2);
8478 __ push(lr);
8479 __ Call(stub.GetCode(), RelocInfo::CODE_TARGET);
8480 __ pop(lr);
8481 } else {
8482 UNIMPLEMENTED();
8483 }
8484
8485 __ bind(&done);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008486 __ StubReturn(1);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008487
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008488 // Handle the slow case by jumping to the JavaScript builtin.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008489 __ bind(&slow);
8490 __ push(r0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008491 switch (op_) {
8492 case Token::SUB:
8493 __ InvokeBuiltin(Builtins::UNARY_MINUS, JUMP_JS);
8494 break;
8495 case Token::BIT_NOT:
8496 __ InvokeBuiltin(Builtins::BIT_NOT, JUMP_JS);
8497 break;
8498 default:
8499 UNREACHABLE();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008500 }
ager@chromium.orga1645e22009-09-09 19:27:10 +00008501}
8502
8503
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008504void CEntryStub::GenerateThrowTOS(MacroAssembler* masm) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008505 // r0 holds the exception.
8506
8507 // Adjust this code if not the case.
8508 ASSERT(StackHandlerConstants::kSize == 4 * kPointerSize);
8509
8510 // Drop the sp to the top of the handler.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008511 __ mov(r3, Operand(ExternalReference(Top::k_handler_address)));
8512 __ ldr(sp, MemOperand(r3));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008513
8514 // Restore the next handler and frame pointer, discard handler state.
8515 ASSERT(StackHandlerConstants::kNextOffset == 0);
8516 __ pop(r2);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008517 __ str(r2, MemOperand(r3));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008518 ASSERT(StackHandlerConstants::kFPOffset == 2 * kPointerSize);
8519 __ ldm(ia_w, sp, r3.bit() | fp.bit()); // r3: discarded state.
8520
8521 // Before returning we restore the context from the frame pointer if
8522 // not NULL. The frame pointer is NULL in the exception handler of a
8523 // JS entry frame.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008524 __ cmp(fp, Operand(0));
8525 // Set cp to NULL if fp is NULL.
8526 __ mov(cp, Operand(0), LeaveCC, eq);
8527 // Restore cp otherwise.
8528 __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset), ne);
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008529#ifdef DEBUG
8530 if (FLAG_debug_code) {
8531 __ mov(lr, Operand(pc));
8532 }
8533#endif
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008534 ASSERT(StackHandlerConstants::kPCOffset == 3 * kPointerSize);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008535 __ pop(pc);
8536}
8537
8538
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00008539void CEntryStub::GenerateThrowUncatchable(MacroAssembler* masm,
8540 UncatchableExceptionType type) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008541 // Adjust this code if not the case.
8542 ASSERT(StackHandlerConstants::kSize == 4 * kPointerSize);
8543
8544 // Drop sp to the top stack handler.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008545 __ mov(r3, Operand(ExternalReference(Top::k_handler_address)));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008546 __ ldr(sp, MemOperand(r3));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008547
8548 // Unwind the handlers until the ENTRY handler is found.
8549 Label loop, done;
8550 __ bind(&loop);
8551 // Load the type of the current stack handler.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008552 const int kStateOffset = StackHandlerConstants::kStateOffset;
8553 __ ldr(r2, MemOperand(sp, kStateOffset));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008554 __ cmp(r2, Operand(StackHandler::ENTRY));
8555 __ b(eq, &done);
8556 // Fetch the next handler in the list.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008557 const int kNextOffset = StackHandlerConstants::kNextOffset;
8558 __ ldr(sp, MemOperand(sp, kNextOffset));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008559 __ jmp(&loop);
8560 __ bind(&done);
8561
8562 // Set the top handler address to next handler past the current ENTRY handler.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008563 ASSERT(StackHandlerConstants::kNextOffset == 0);
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00008564 __ pop(r2);
8565 __ str(r2, MemOperand(r3));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008566
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00008567 if (type == OUT_OF_MEMORY) {
8568 // Set external caught exception to false.
8569 ExternalReference external_caught(Top::k_external_caught_exception_address);
8570 __ mov(r0, Operand(false));
8571 __ mov(r2, Operand(external_caught));
8572 __ str(r0, MemOperand(r2));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008573
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00008574 // Set pending exception and r0 to out of memory exception.
8575 Failure* out_of_memory = Failure::OutOfMemoryException();
8576 __ mov(r0, Operand(reinterpret_cast<int32_t>(out_of_memory)));
8577 __ mov(r2, Operand(ExternalReference(Top::k_pending_exception_address)));
8578 __ str(r0, MemOperand(r2));
8579 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008580
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008581 // Stack layout at this point. See also StackHandlerConstants.
8582 // sp -> state (ENTRY)
8583 // fp
8584 // lr
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008585
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008586 // Discard handler state (r2 is not used) and restore frame pointer.
8587 ASSERT(StackHandlerConstants::kFPOffset == 2 * kPointerSize);
8588 __ ldm(ia_w, sp, r2.bit() | fp.bit()); // r2: discarded state.
8589 // Before returning we restore the context from the frame pointer if
8590 // not NULL. The frame pointer is NULL in the exception handler of a
8591 // JS entry frame.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008592 __ cmp(fp, Operand(0));
8593 // Set cp to NULL if fp is NULL.
8594 __ mov(cp, Operand(0), LeaveCC, eq);
8595 // Restore cp otherwise.
8596 __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset), ne);
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008597#ifdef DEBUG
8598 if (FLAG_debug_code) {
8599 __ mov(lr, Operand(pc));
8600 }
8601#endif
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008602 ASSERT(StackHandlerConstants::kPCOffset == 3 * kPointerSize);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008603 __ pop(pc);
8604}
8605
8606
8607void CEntryStub::GenerateCore(MacroAssembler* masm,
8608 Label* throw_normal_exception,
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00008609 Label* throw_termination_exception,
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008610 Label* throw_out_of_memory_exception,
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00008611 bool do_gc,
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00008612 bool always_allocate,
8613 int frame_alignment_skew) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008614 // r0: result parameter for PerformGC, if any
8615 // r4: number of arguments including receiver (C callee-saved)
8616 // r5: pointer to builtin function (C callee-saved)
8617 // r6: pointer to the first argument (C callee-saved)
8618
8619 if (do_gc) {
8620 // Passing r0.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00008621 __ PrepareCallCFunction(1, r1);
8622 __ CallCFunction(ExternalReference::perform_gc_function(), 1);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008623 }
8624
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00008625 ExternalReference scope_depth =
8626 ExternalReference::heap_always_allocate_scope_depth();
8627 if (always_allocate) {
8628 __ mov(r0, Operand(scope_depth));
8629 __ ldr(r1, MemOperand(r0));
8630 __ add(r1, r1, Operand(1));
8631 __ str(r1, MemOperand(r0));
8632 }
8633
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008634 // Call C built-in.
8635 // r0 = argc, r1 = argv
8636 __ mov(r0, Operand(r4));
8637 __ mov(r1, Operand(r6));
8638
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00008639 int frame_alignment = MacroAssembler::ActivationFrameAlignment();
8640 int frame_alignment_mask = frame_alignment - 1;
8641#if defined(V8_HOST_ARCH_ARM)
8642 if (FLAG_debug_code) {
8643 if (frame_alignment > kPointerSize) {
8644 Label alignment_as_expected;
8645 ASSERT(IsPowerOf2(frame_alignment));
8646 __ sub(r2, sp, Operand(frame_alignment_skew));
8647 __ tst(r2, Operand(frame_alignment_mask));
8648 __ b(eq, &alignment_as_expected);
8649 // Don't use Check here, as it will call Runtime_Abort re-entering here.
8650 __ stop("Unexpected alignment");
8651 __ bind(&alignment_as_expected);
8652 }
8653 }
8654#endif
8655
8656 // Just before the call (jump) below lr is pushed, so the actual alignment is
8657 // adding one to the current skew.
8658 int alignment_before_call =
8659 (frame_alignment_skew + kPointerSize) & frame_alignment_mask;
8660 if (alignment_before_call > 0) {
8661 // Push until the alignment before the call is met.
8662 __ mov(r2, Operand(0));
8663 for (int i = alignment_before_call;
8664 (i & frame_alignment_mask) != 0;
8665 i += kPointerSize) {
8666 __ push(r2);
8667 }
8668 }
8669
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008670 // TODO(1242173): To let the GC traverse the return address of the exit
8671 // frames, we need to know where the return address is. Right now,
8672 // we push it on the stack to be able to find it again, but we never
8673 // restore from it in case of changes, which makes it impossible to
8674 // support moving the C entry code stub. This should be fixed, but currently
8675 // this is OK because the CEntryStub gets generated so early in the V8 boot
8676 // sequence that it is not moving ever.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00008677 masm->add(lr, pc, Operand(4)); // Compute return address: (pc + 8) + 4
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00008678 masm->push(lr);
8679 masm->Jump(r5);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00008680
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00008681 // Restore sp back to before aligning the stack.
8682 if (alignment_before_call > 0) {
8683 __ add(sp, sp, Operand(alignment_before_call));
8684 }
8685
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00008686 if (always_allocate) {
8687 // It's okay to clobber r2 and r3 here. Don't mess with r0 and r1
8688 // though (contain the result).
8689 __ mov(r2, Operand(scope_depth));
8690 __ ldr(r3, MemOperand(r2));
8691 __ sub(r3, r3, Operand(1));
8692 __ str(r3, MemOperand(r2));
8693 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008694
8695 // check for failure result
8696 Label failure_returned;
8697 ASSERT(((kFailureTag + 1) & kFailureTagMask) == 0);
8698 // Lower 2 bits of r2 are 0 iff r0 has failure tag.
8699 __ add(r2, r0, Operand(1));
8700 __ tst(r2, Operand(kFailureTagMask));
8701 __ b(eq, &failure_returned);
8702
8703 // Exit C frame and return.
8704 // r0:r1: result
8705 // sp: stack pointer
8706 // fp: frame pointer
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008707 __ LeaveExitFrame(mode_);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008708
8709 // check if we should retry or throw exception
8710 Label retry;
8711 __ bind(&failure_returned);
8712 ASSERT(Failure::RETRY_AFTER_GC == 0);
8713 __ tst(r0, Operand(((1 << kFailureTypeTagSize) - 1) << kFailureTagSize));
8714 __ b(eq, &retry);
8715
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00008716 // Special handling of out of memory exceptions.
8717 Failure* out_of_memory = Failure::OutOfMemoryException();
8718 __ cmp(r0, Operand(reinterpret_cast<int32_t>(out_of_memory)));
8719 __ b(eq, throw_out_of_memory_exception);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008720
8721 // Retrieve the pending exception and clear the variable.
ager@chromium.org32912102009-01-16 10:38:43 +00008722 __ mov(ip, Operand(ExternalReference::the_hole_value_location()));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008723 __ ldr(r3, MemOperand(ip));
ager@chromium.org32912102009-01-16 10:38:43 +00008724 __ mov(ip, Operand(ExternalReference(Top::k_pending_exception_address)));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008725 __ ldr(r0, MemOperand(ip));
8726 __ str(r3, MemOperand(ip));
8727
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00008728 // Special handling of termination exceptions which are uncatchable
8729 // by javascript code.
8730 __ cmp(r0, Operand(Factory::termination_exception()));
8731 __ b(eq, throw_termination_exception);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008732
8733 // Handle normal exception.
8734 __ jmp(throw_normal_exception);
8735
8736 __ bind(&retry); // pass last failure (r0) as parameter (r0) when retrying
8737}
8738
8739
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008740void CEntryStub::Generate(MacroAssembler* masm) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008741 // Called from JavaScript; parameters are on stack as if calling JS function
8742 // r0: number of arguments including receiver
8743 // r1: pointer to builtin function
8744 // fp: frame pointer (restored after C call)
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008745 // sp: stack pointer (restored as callee's sp after C call)
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008746 // cp: current context (C callee-saved)
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008747
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008748 // Result returned in r0 or r0+r1 by default.
8749
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008750 // NOTE: Invocations of builtins may return failure objects
8751 // instead of a proper result. The builtin entry handles
8752 // this by performing a garbage collection and retrying the
8753 // builtin once.
8754
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008755 // Enter the exit frame that transitions from JavaScript to C++.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008756 __ EnterExitFrame(mode_);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008757
8758 // r4: number of arguments (C callee-saved)
8759 // r5: pointer to builtin function (C callee-saved)
8760 // r6: pointer to first argument (C callee-saved)
8761
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008762 Label throw_normal_exception;
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00008763 Label throw_termination_exception;
8764 Label throw_out_of_memory_exception;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008765
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00008766 // Call into the runtime system.
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00008767 GenerateCore(masm,
8768 &throw_normal_exception,
8769 &throw_termination_exception,
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008770 &throw_out_of_memory_exception,
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00008771 false,
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00008772 false,
8773 -kPointerSize);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00008774
8775 // Do space-specific GC and retry runtime call.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008776 GenerateCore(masm,
8777 &throw_normal_exception,
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00008778 &throw_termination_exception,
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008779 &throw_out_of_memory_exception,
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00008780 true,
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00008781 false,
8782 0);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00008783
8784 // Do full GC and retry runtime call one final time.
8785 Failure* failure = Failure::InternalError();
8786 __ mov(r0, Operand(reinterpret_cast<int32_t>(failure)));
8787 GenerateCore(masm,
8788 &throw_normal_exception,
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00008789 &throw_termination_exception,
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00008790 &throw_out_of_memory_exception,
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00008791 true,
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00008792 true,
8793 kPointerSize);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008794
8795 __ bind(&throw_out_of_memory_exception);
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00008796 GenerateThrowUncatchable(masm, OUT_OF_MEMORY);
8797
8798 __ bind(&throw_termination_exception);
8799 GenerateThrowUncatchable(masm, TERMINATION);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008800
8801 __ bind(&throw_normal_exception);
8802 GenerateThrowTOS(masm);
8803}
8804
8805
8806void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) {
8807 // r0: code entry
8808 // r1: function
8809 // r2: receiver
8810 // r3: argc
8811 // [sp+0]: argv
8812
8813 Label invoke, exit;
8814
8815 // Called from C, so do not pop argc and args on exit (preserve sp)
8816 // No need to save register-passed args
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008817 // Save callee-saved registers (incl. cp and fp), sp, and lr
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008818 __ stm(db_w, sp, kCalleeSaved | lr.bit());
8819
8820 // Get address of argv, see stm above.
8821 // r0: code entry
8822 // r1: function
8823 // r2: receiver
8824 // r3: argc
ager@chromium.org5c838252010-02-19 08:53:10 +00008825 __ ldr(r4, MemOperand(sp, (kNumCalleeSaved + 1) * kPointerSize)); // argv
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008826
8827 // Push a frame with special values setup to mark it as an entry frame.
8828 // r0: code entry
8829 // r1: function
8830 // r2: receiver
8831 // r3: argc
8832 // r4: argv
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008833 __ mov(r8, Operand(-1)); // Push a bad frame pointer to fail if it is used.
ager@chromium.org18ad94b2009-09-02 08:22:29 +00008834 int marker = is_construct ? StackFrame::ENTRY_CONSTRUCT : StackFrame::ENTRY;
8835 __ mov(r7, Operand(Smi::FromInt(marker)));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008836 __ mov(r6, Operand(Smi::FromInt(marker)));
8837 __ mov(r5, Operand(ExternalReference(Top::k_c_entry_fp_address)));
8838 __ ldr(r5, MemOperand(r5));
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00008839 __ Push(r8, r7, r6, r5);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008840
8841 // Setup frame pointer for the frame to be pushed.
8842 __ add(fp, sp, Operand(-EntryFrameConstants::kCallerFPOffset));
8843
8844 // Call a faked try-block that does the invoke.
8845 __ bl(&invoke);
8846
8847 // Caught exception: Store result (exception) in the pending
8848 // exception field in the JSEnv and return a failure sentinel.
8849 // Coming in here the fp will be invalid because the PushTryHandler below
8850 // sets it to 0 to signal the existence of the JSEntry frame.
ager@chromium.org32912102009-01-16 10:38:43 +00008851 __ mov(ip, Operand(ExternalReference(Top::k_pending_exception_address)));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008852 __ str(r0, MemOperand(ip));
ager@chromium.org3bf7b912008-11-17 09:09:45 +00008853 __ mov(r0, Operand(reinterpret_cast<int32_t>(Failure::Exception())));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008854 __ b(&exit);
8855
8856 // Invoke: Link this frame into the handler chain.
8857 __ bind(&invoke);
8858 // Must preserve r0-r4, r5-r7 are available.
8859 __ PushTryHandler(IN_JS_ENTRY, JS_ENTRY_HANDLER);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008860 // If an exception not caught by another handler occurs, this handler
8861 // returns control to the code after the bl(&invoke) above, which
8862 // restores all kCalleeSaved registers (including cp and fp) to their
8863 // saved values before returning a failure to C.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008864
8865 // Clear any pending exceptions.
8866 __ mov(ip, Operand(ExternalReference::the_hole_value_location()));
8867 __ ldr(r5, MemOperand(ip));
ager@chromium.org32912102009-01-16 10:38:43 +00008868 __ mov(ip, Operand(ExternalReference(Top::k_pending_exception_address)));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008869 __ str(r5, MemOperand(ip));
8870
8871 // Invoke the function by calling through JS entry trampoline builtin.
8872 // Notice that we cannot store a reference to the trampoline code directly in
8873 // this stub, because runtime stubs are not traversed when doing GC.
8874
8875 // Expected registers by Builtins::JSEntryTrampoline
8876 // r0: code entry
8877 // r1: function
8878 // r2: receiver
8879 // r3: argc
8880 // r4: argv
8881 if (is_construct) {
8882 ExternalReference construct_entry(Builtins::JSConstructEntryTrampoline);
8883 __ mov(ip, Operand(construct_entry));
8884 } else {
8885 ExternalReference entry(Builtins::JSEntryTrampoline);
8886 __ mov(ip, Operand(entry));
8887 }
8888 __ ldr(ip, MemOperand(ip)); // deref address
8889
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008890 // Branch and link to JSEntryTrampoline. We don't use the double underscore
8891 // macro for the add instruction because we don't want the coverage tool
8892 // inserting instructions here after we read the pc.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008893 __ mov(lr, Operand(pc));
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008894 masm->add(pc, ip, Operand(Code::kHeaderSize - kHeapObjectTag));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008895
8896 // Unlink this frame from the handler chain. When reading the
8897 // address of the next handler, there is no need to use the address
8898 // displacement since the current stack pointer (sp) points directly
8899 // to the stack handler.
8900 __ ldr(r3, MemOperand(sp, StackHandlerConstants::kNextOffset));
8901 __ mov(ip, Operand(ExternalReference(Top::k_handler_address)));
8902 __ str(r3, MemOperand(ip));
8903 // No need to restore registers
8904 __ add(sp, sp, Operand(StackHandlerConstants::kSize));
8905
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008906
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008907 __ bind(&exit); // r0 holds result
8908 // Restore the top frame descriptors from the stack.
8909 __ pop(r3);
8910 __ mov(ip, Operand(ExternalReference(Top::k_c_entry_fp_address)));
8911 __ str(r3, MemOperand(ip));
8912
8913 // Reset the stack to the callee saved registers.
8914 __ add(sp, sp, Operand(-EntryFrameConstants::kCallerFPOffset));
8915
8916 // Restore callee-saved registers and return.
8917#ifdef DEBUG
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008918 if (FLAG_debug_code) {
8919 __ mov(lr, Operand(pc));
8920 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008921#endif
8922 __ ldm(ia_w, sp, kCalleeSaved | pc.bit());
8923}
8924
8925
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008926// This stub performs an instanceof, calling the builtin function if
8927// necessary. Uses r1 for the object, r0 for the function that it may
8928// be an instance of (these are fetched from the stack).
8929void InstanceofStub::Generate(MacroAssembler* masm) {
8930 // Get the object - slow case for smis (we may need to throw an exception
8931 // depending on the rhs).
8932 Label slow, loop, is_instance, is_not_instance;
8933 __ ldr(r0, MemOperand(sp, 1 * kPointerSize));
8934 __ BranchOnSmi(r0, &slow);
8935
8936 // Check that the left hand is a JS object and put map in r3.
8937 __ CompareObjectType(r0, r3, r2, FIRST_JS_OBJECT_TYPE);
8938 __ b(lt, &slow);
8939 __ cmp(r2, Operand(LAST_JS_OBJECT_TYPE));
8940 __ b(gt, &slow);
8941
8942 // Get the prototype of the function (r4 is result, r2 is scratch).
ager@chromium.org5c838252010-02-19 08:53:10 +00008943 __ ldr(r1, MemOperand(sp, 0));
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00008944 // r1 is function, r3 is map.
8945
8946 // Look up the function and the map in the instanceof cache.
8947 Label miss;
8948 __ LoadRoot(ip, Heap::kInstanceofCacheFunctionRootIndex);
8949 __ cmp(r1, ip);
8950 __ b(ne, &miss);
8951 __ LoadRoot(ip, Heap::kInstanceofCacheMapRootIndex);
8952 __ cmp(r3, ip);
8953 __ b(ne, &miss);
8954 __ LoadRoot(r0, Heap::kInstanceofCacheAnswerRootIndex);
8955 __ pop();
8956 __ pop();
8957 __ mov(pc, Operand(lr));
8958
8959 __ bind(&miss);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008960 __ TryGetFunctionPrototype(r1, r4, r2, &slow);
8961
8962 // Check that the function prototype is a JS object.
8963 __ BranchOnSmi(r4, &slow);
8964 __ CompareObjectType(r4, r5, r5, FIRST_JS_OBJECT_TYPE);
8965 __ b(lt, &slow);
8966 __ cmp(r5, Operand(LAST_JS_OBJECT_TYPE));
8967 __ b(gt, &slow);
8968
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00008969 __ StoreRoot(r1, Heap::kInstanceofCacheFunctionRootIndex);
8970 __ StoreRoot(r3, Heap::kInstanceofCacheMapRootIndex);
8971
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008972 // Register mapping: r3 is object map and r4 is function prototype.
8973 // Get prototype of object into r2.
8974 __ ldr(r2, FieldMemOperand(r3, Map::kPrototypeOffset));
8975
8976 // Loop through the prototype chain looking for the function prototype.
8977 __ bind(&loop);
8978 __ cmp(r2, Operand(r4));
8979 __ b(eq, &is_instance);
ager@chromium.orgab99eea2009-08-25 07:05:41 +00008980 __ LoadRoot(ip, Heap::kNullValueRootIndex);
8981 __ cmp(r2, ip);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008982 __ b(eq, &is_not_instance);
8983 __ ldr(r2, FieldMemOperand(r2, HeapObject::kMapOffset));
8984 __ ldr(r2, FieldMemOperand(r2, Map::kPrototypeOffset));
8985 __ jmp(&loop);
8986
8987 __ bind(&is_instance);
8988 __ mov(r0, Operand(Smi::FromInt(0)));
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00008989 __ StoreRoot(r0, Heap::kInstanceofCacheAnswerRootIndex);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008990 __ pop();
8991 __ pop();
8992 __ mov(pc, Operand(lr)); // Return.
8993
8994 __ bind(&is_not_instance);
8995 __ mov(r0, Operand(Smi::FromInt(1)));
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00008996 __ StoreRoot(r0, Heap::kInstanceofCacheAnswerRootIndex);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008997 __ pop();
8998 __ pop();
8999 __ mov(pc, Operand(lr)); // Return.
9000
9001 // Slow-case. Tail call builtin.
9002 __ bind(&slow);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009003 __ InvokeBuiltin(Builtins::INSTANCE_OF, JUMP_JS);
9004}
9005
9006
ager@chromium.org7c537e22008-10-16 08:43:32 +00009007void ArgumentsAccessStub::GenerateReadElement(MacroAssembler* masm) {
9008 // The displacement is the offset of the last parameter (if any)
9009 // relative to the frame pointer.
9010 static const int kDisplacement =
9011 StandardFrameConstants::kCallerSPOffset - kPointerSize;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009012
ager@chromium.org7c537e22008-10-16 08:43:32 +00009013 // Check that the key is a smi.
9014 Label slow;
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009015 __ BranchOnNotSmi(r1, &slow);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009016
ager@chromium.org7c537e22008-10-16 08:43:32 +00009017 // Check if the calling frame is an arguments adaptor frame.
9018 Label adaptor;
9019 __ ldr(r2, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
9020 __ ldr(r3, MemOperand(r2, StandardFrameConstants::kContextOffset));
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009021 __ cmp(r3, Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
ager@chromium.org7c537e22008-10-16 08:43:32 +00009022 __ b(eq, &adaptor);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009023
ager@chromium.org7c537e22008-10-16 08:43:32 +00009024 // Check index against formal parameters count limit passed in
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009025 // through register r0. Use unsigned comparison to get negative
ager@chromium.org7c537e22008-10-16 08:43:32 +00009026 // check for free.
9027 __ cmp(r1, r0);
9028 __ b(cs, &slow);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009029
ager@chromium.org7c537e22008-10-16 08:43:32 +00009030 // Read the argument from the stack and return it.
9031 __ sub(r3, r0, r1);
9032 __ add(r3, fp, Operand(r3, LSL, kPointerSizeLog2 - kSmiTagSize));
9033 __ ldr(r0, MemOperand(r3, kDisplacement));
ager@chromium.org9085a012009-05-11 19:22:57 +00009034 __ Jump(lr);
ager@chromium.org7c537e22008-10-16 08:43:32 +00009035
9036 // Arguments adaptor case: Check index against actual arguments
9037 // limit found in the arguments adaptor frame. Use unsigned
9038 // comparison to get negative check for free.
9039 __ bind(&adaptor);
9040 __ ldr(r0, MemOperand(r2, ArgumentsAdaptorFrameConstants::kLengthOffset));
9041 __ cmp(r1, r0);
9042 __ b(cs, &slow);
9043
9044 // Read the argument from the adaptor frame and return it.
9045 __ sub(r3, r0, r1);
9046 __ add(r3, r2, Operand(r3, LSL, kPointerSizeLog2 - kSmiTagSize));
9047 __ ldr(r0, MemOperand(r3, kDisplacement));
ager@chromium.org9085a012009-05-11 19:22:57 +00009048 __ Jump(lr);
ager@chromium.org7c537e22008-10-16 08:43:32 +00009049
9050 // Slow-case: Handle non-smi or out-of-bounds access to arguments
9051 // by calling the runtime system.
9052 __ bind(&slow);
9053 __ push(r1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009054 __ TailCallRuntime(Runtime::kGetArgumentsProperty, 1, 1);
ager@chromium.org7c537e22008-10-16 08:43:32 +00009055}
9056
9057
9058void ArgumentsAccessStub::GenerateNewObject(MacroAssembler* masm) {
ager@chromium.org5c838252010-02-19 08:53:10 +00009059 // sp[0] : number of parameters
9060 // sp[4] : receiver displacement
9061 // sp[8] : function
9062
ager@chromium.org7c537e22008-10-16 08:43:32 +00009063 // Check if the calling frame is an arguments adaptor frame.
ager@chromium.org5c838252010-02-19 08:53:10 +00009064 Label adaptor_frame, try_allocate, runtime;
ager@chromium.org7c537e22008-10-16 08:43:32 +00009065 __ ldr(r2, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
9066 __ ldr(r3, MemOperand(r2, StandardFrameConstants::kContextOffset));
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009067 __ cmp(r3, Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
ager@chromium.org5c838252010-02-19 08:53:10 +00009068 __ b(eq, &adaptor_frame);
9069
9070 // Get the length from the frame.
9071 __ ldr(r1, MemOperand(sp, 0));
9072 __ b(&try_allocate);
ager@chromium.org7c537e22008-10-16 08:43:32 +00009073
9074 // Patch the arguments.length and the parameters pointer.
ager@chromium.org5c838252010-02-19 08:53:10 +00009075 __ bind(&adaptor_frame);
9076 __ ldr(r1, MemOperand(r2, ArgumentsAdaptorFrameConstants::kLengthOffset));
9077 __ str(r1, MemOperand(sp, 0));
9078 __ add(r3, r2, Operand(r1, LSL, kPointerSizeLog2 - kSmiTagSize));
ager@chromium.org7c537e22008-10-16 08:43:32 +00009079 __ add(r3, r3, Operand(StandardFrameConstants::kCallerSPOffset));
9080 __ str(r3, MemOperand(sp, 1 * kPointerSize));
9081
ager@chromium.org5c838252010-02-19 08:53:10 +00009082 // Try the new space allocation. Start out with computing the size
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00009083 // of the arguments object and the elements array in words.
ager@chromium.org5c838252010-02-19 08:53:10 +00009084 Label add_arguments_object;
9085 __ bind(&try_allocate);
9086 __ cmp(r1, Operand(0));
9087 __ b(eq, &add_arguments_object);
9088 __ mov(r1, Operand(r1, LSR, kSmiTagSize));
9089 __ add(r1, r1, Operand(FixedArray::kHeaderSize / kPointerSize));
9090 __ bind(&add_arguments_object);
9091 __ add(r1, r1, Operand(Heap::kArgumentsObjectSize / kPointerSize));
9092
9093 // Do the allocation of both objects in one go.
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00009094 __ AllocateInNewSpace(
9095 r1,
9096 r0,
9097 r2,
9098 r3,
9099 &runtime,
9100 static_cast<AllocationFlags>(TAG_OBJECT | SIZE_IN_WORDS));
ager@chromium.org5c838252010-02-19 08:53:10 +00009101
9102 // Get the arguments boilerplate from the current (global) context.
9103 int offset = Context::SlotOffset(Context::ARGUMENTS_BOILERPLATE_INDEX);
9104 __ ldr(r4, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_INDEX)));
9105 __ ldr(r4, FieldMemOperand(r4, GlobalObject::kGlobalContextOffset));
9106 __ ldr(r4, MemOperand(r4, offset));
9107
9108 // Copy the JS object part.
9109 for (int i = 0; i < JSObject::kHeaderSize; i += kPointerSize) {
9110 __ ldr(r3, FieldMemOperand(r4, i));
9111 __ str(r3, FieldMemOperand(r0, i));
9112 }
9113
9114 // Setup the callee in-object property.
9115 ASSERT(Heap::arguments_callee_index == 0);
9116 __ ldr(r3, MemOperand(sp, 2 * kPointerSize));
9117 __ str(r3, FieldMemOperand(r0, JSObject::kHeaderSize));
9118
9119 // Get the length (smi tagged) and set that as an in-object property too.
9120 ASSERT(Heap::arguments_length_index == 1);
9121 __ ldr(r1, MemOperand(sp, 0 * kPointerSize));
9122 __ str(r1, FieldMemOperand(r0, JSObject::kHeaderSize + kPointerSize));
9123
9124 // If there are no actual arguments, we're done.
9125 Label done;
9126 __ cmp(r1, Operand(0));
9127 __ b(eq, &done);
9128
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009129 // Get the parameters pointer from the stack.
ager@chromium.org5c838252010-02-19 08:53:10 +00009130 __ ldr(r2, MemOperand(sp, 1 * kPointerSize));
ager@chromium.org5c838252010-02-19 08:53:10 +00009131
9132 // Setup the elements pointer in the allocated arguments object and
9133 // initialize the header in the elements fixed array.
9134 __ add(r4, r0, Operand(Heap::kArgumentsObjectSize));
9135 __ str(r4, FieldMemOperand(r0, JSObject::kElementsOffset));
9136 __ LoadRoot(r3, Heap::kFixedArrayMapRootIndex);
9137 __ str(r3, FieldMemOperand(r4, FixedArray::kMapOffset));
9138 __ str(r1, FieldMemOperand(r4, FixedArray::kLengthOffset));
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009139 __ mov(r1, Operand(r1, LSR, kSmiTagSize)); // Untag the length for the loop.
ager@chromium.org5c838252010-02-19 08:53:10 +00009140
9141 // Copy the fixed array slots.
9142 Label loop;
9143 // Setup r4 to point to the first array slot.
9144 __ add(r4, r4, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
9145 __ bind(&loop);
9146 // Pre-decrement r2 with kPointerSize on each iteration.
9147 // Pre-decrement in order to skip receiver.
9148 __ ldr(r3, MemOperand(r2, kPointerSize, NegPreIndex));
9149 // Post-increment r4 with kPointerSize on each iteration.
9150 __ str(r3, MemOperand(r4, kPointerSize, PostIndex));
9151 __ sub(r1, r1, Operand(1));
9152 __ cmp(r1, Operand(0));
9153 __ b(ne, &loop);
9154
9155 // Return and remove the on-stack parameters.
9156 __ bind(&done);
9157 __ add(sp, sp, Operand(3 * kPointerSize));
9158 __ Ret();
9159
ager@chromium.org7c537e22008-10-16 08:43:32 +00009160 // Do the runtime call to allocate the arguments object.
9161 __ bind(&runtime);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009162 __ TailCallRuntime(Runtime::kNewArgumentsFast, 3, 1);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009163}
9164
9165
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009166void RegExpExecStub::Generate(MacroAssembler* masm) {
9167 // Just jump directly to runtime if native RegExp is not selected at compile
9168 // time or if regexp entry in generated code is turned off runtime switch or
9169 // at compilation.
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00009170#ifdef V8_INTERPRETED_REGEXP
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009171 __ TailCallRuntime(Runtime::kRegExpExec, 4, 1);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00009172#else // V8_INTERPRETED_REGEXP
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009173 if (!FLAG_regexp_entry_native) {
9174 __ TailCallRuntime(Runtime::kRegExpExec, 4, 1);
9175 return;
9176 }
9177
9178 // Stack frame on entry.
9179 // sp[0]: last_match_info (expected JSArray)
9180 // sp[4]: previous index
9181 // sp[8]: subject string
9182 // sp[12]: JSRegExp object
9183
9184 static const int kLastMatchInfoOffset = 0 * kPointerSize;
9185 static const int kPreviousIndexOffset = 1 * kPointerSize;
9186 static const int kSubjectOffset = 2 * kPointerSize;
9187 static const int kJSRegExpOffset = 3 * kPointerSize;
9188
9189 Label runtime, invoke_regexp;
9190
9191 // Allocation of registers for this function. These are in callee save
9192 // registers and will be preserved by the call to the native RegExp code, as
9193 // this code is called using the normal C calling convention. When calling
9194 // directly from generated code the native RegExp code will not do a GC and
9195 // therefore the content of these registers are safe to use after the call.
9196 Register subject = r4;
9197 Register regexp_data = r5;
9198 Register last_match_info_elements = r6;
9199
9200 // Ensure that a RegExp stack is allocated.
9201 ExternalReference address_of_regexp_stack_memory_address =
9202 ExternalReference::address_of_regexp_stack_memory_address();
9203 ExternalReference address_of_regexp_stack_memory_size =
9204 ExternalReference::address_of_regexp_stack_memory_size();
9205 __ mov(r0, Operand(address_of_regexp_stack_memory_size));
9206 __ ldr(r0, MemOperand(r0, 0));
9207 __ tst(r0, Operand(r0));
9208 __ b(eq, &runtime);
9209
9210 // Check that the first argument is a JSRegExp object.
9211 __ ldr(r0, MemOperand(sp, kJSRegExpOffset));
9212 ASSERT_EQ(0, kSmiTag);
9213 __ tst(r0, Operand(kSmiTagMask));
9214 __ b(eq, &runtime);
9215 __ CompareObjectType(r0, r1, r1, JS_REGEXP_TYPE);
9216 __ b(ne, &runtime);
9217
9218 // Check that the RegExp has been compiled (data contains a fixed array).
9219 __ ldr(regexp_data, FieldMemOperand(r0, JSRegExp::kDataOffset));
9220 if (FLAG_debug_code) {
9221 __ tst(regexp_data, Operand(kSmiTagMask));
9222 __ Check(nz, "Unexpected type for RegExp data, FixedArray expected");
9223 __ CompareObjectType(regexp_data, r0, r0, FIXED_ARRAY_TYPE);
9224 __ Check(eq, "Unexpected type for RegExp data, FixedArray expected");
9225 }
9226
9227 // regexp_data: RegExp data (FixedArray)
9228 // Check the type of the RegExp. Only continue if type is JSRegExp::IRREGEXP.
9229 __ ldr(r0, FieldMemOperand(regexp_data, JSRegExp::kDataTagOffset));
9230 __ cmp(r0, Operand(Smi::FromInt(JSRegExp::IRREGEXP)));
9231 __ b(ne, &runtime);
9232
9233 // regexp_data: RegExp data (FixedArray)
9234 // Check that the number of captures fit in the static offsets vector buffer.
9235 __ ldr(r2,
9236 FieldMemOperand(regexp_data, JSRegExp::kIrregexpCaptureCountOffset));
9237 // Calculate number of capture registers (number_of_captures + 1) * 2. This
9238 // uses the asumption that smis are 2 * their untagged value.
9239 ASSERT_EQ(0, kSmiTag);
9240 ASSERT_EQ(1, kSmiTagSize + kSmiShiftSize);
9241 __ add(r2, r2, Operand(2)); // r2 was a smi.
9242 // Check that the static offsets vector buffer is large enough.
9243 __ cmp(r2, Operand(OffsetsVector::kStaticOffsetsVectorSize));
9244 __ b(hi, &runtime);
9245
9246 // r2: Number of capture registers
9247 // regexp_data: RegExp data (FixedArray)
9248 // Check that the second argument is a string.
9249 __ ldr(subject, MemOperand(sp, kSubjectOffset));
9250 __ tst(subject, Operand(kSmiTagMask));
9251 __ b(eq, &runtime);
9252 Condition is_string = masm->IsObjectStringType(subject, r0);
9253 __ b(NegateCondition(is_string), &runtime);
9254 // Get the length of the string to r3.
9255 __ ldr(r3, FieldMemOperand(subject, String::kLengthOffset));
9256
9257 // r2: Number of capture registers
ager@chromium.orgac091b72010-05-05 07:34:42 +00009258 // r3: Length of subject string as a smi
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009259 // subject: Subject string
9260 // regexp_data: RegExp data (FixedArray)
9261 // Check that the third argument is a positive smi less than the subject
9262 // string length. A negative value will be greater (unsigned comparison).
9263 __ ldr(r0, MemOperand(sp, kPreviousIndexOffset));
ager@chromium.orgac091b72010-05-05 07:34:42 +00009264 __ tst(r0, Operand(kSmiTagMask));
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00009265 __ b(ne, &runtime);
ager@chromium.orgac091b72010-05-05 07:34:42 +00009266 __ cmp(r3, Operand(r0));
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00009267 __ b(ls, &runtime);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009268
9269 // r2: Number of capture registers
9270 // subject: Subject string
9271 // regexp_data: RegExp data (FixedArray)
9272 // Check that the fourth object is a JSArray object.
9273 __ ldr(r0, MemOperand(sp, kLastMatchInfoOffset));
9274 __ tst(r0, Operand(kSmiTagMask));
9275 __ b(eq, &runtime);
9276 __ CompareObjectType(r0, r1, r1, JS_ARRAY_TYPE);
9277 __ b(ne, &runtime);
9278 // Check that the JSArray is in fast case.
9279 __ ldr(last_match_info_elements,
9280 FieldMemOperand(r0, JSArray::kElementsOffset));
9281 __ ldr(r0, FieldMemOperand(last_match_info_elements, HeapObject::kMapOffset));
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00009282 __ LoadRoot(ip, Heap::kFixedArrayMapRootIndex);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00009283 __ cmp(r0, ip);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009284 __ b(ne, &runtime);
9285 // Check that the last match info has space for the capture registers and the
9286 // additional information.
9287 __ ldr(r0,
9288 FieldMemOperand(last_match_info_elements, FixedArray::kLengthOffset));
9289 __ add(r2, r2, Operand(RegExpImpl::kLastMatchOverhead));
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009290 __ cmp(r2, Operand(r0, ASR, kSmiTagSize));
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009291 __ b(gt, &runtime);
9292
9293 // subject: Subject string
9294 // regexp_data: RegExp data (FixedArray)
9295 // Check the representation and encoding of the subject string.
9296 Label seq_string;
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009297 __ ldr(r0, FieldMemOperand(subject, HeapObject::kMapOffset));
9298 __ ldrb(r0, FieldMemOperand(r0, Map::kInstanceTypeOffset));
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00009299 // First check for flat string.
9300 __ tst(r0, Operand(kIsNotStringMask | kStringRepresentationMask));
9301 ASSERT_EQ(0, kStringTag | kSeqStringTag);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009302 __ b(eq, &seq_string);
9303
9304 // subject: Subject string
9305 // regexp_data: RegExp data (FixedArray)
9306 // Check for flat cons string.
9307 // A flat cons string is a cons string where the second part is the empty
9308 // string. In that case the subject string is just the first part of the cons
9309 // string. Also in this case the first part of the cons string is known to be
9310 // a sequential string or an external string.
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00009311 ASSERT(kExternalStringTag !=0);
9312 ASSERT_EQ(0, kConsStringTag & kExternalStringTag);
9313 __ tst(r0, Operand(kIsNotStringMask | kExternalStringTag));
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009314 __ b(ne, &runtime);
9315 __ ldr(r0, FieldMemOperand(subject, ConsString::kSecondOffset));
9316 __ LoadRoot(r1, Heap::kEmptyStringRootIndex);
9317 __ cmp(r0, r1);
9318 __ b(ne, &runtime);
9319 __ ldr(subject, FieldMemOperand(subject, ConsString::kFirstOffset));
9320 __ ldr(r0, FieldMemOperand(subject, HeapObject::kMapOffset));
9321 __ ldrb(r0, FieldMemOperand(r0, Map::kInstanceTypeOffset));
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00009322 // Is first part a flat string?
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009323 ASSERT_EQ(0, kSeqStringTag);
9324 __ tst(r0, Operand(kStringRepresentationMask));
9325 __ b(nz, &runtime);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009326
9327 __ bind(&seq_string);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009328 // subject: Subject string
9329 // regexp_data: RegExp data (FixedArray)
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00009330 // r0: Instance type of subject string
9331 ASSERT_EQ(4, kAsciiStringTag);
9332 ASSERT_EQ(0, kTwoByteStringTag);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009333 // Find the code object based on the assumptions above.
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00009334 __ and_(r0, r0, Operand(kStringEncodingMask));
9335 __ mov(r3, Operand(r0, ASR, 2), SetCC);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009336 __ ldr(r7, FieldMemOperand(regexp_data, JSRegExp::kDataAsciiCodeOffset), ne);
9337 __ ldr(r7, FieldMemOperand(regexp_data, JSRegExp::kDataUC16CodeOffset), eq);
9338
9339 // Check that the irregexp code has been generated for the actual string
9340 // encoding. If it has, the field contains a code object otherwise it contains
9341 // the hole.
9342 __ CompareObjectType(r7, r0, r0, CODE_TYPE);
9343 __ b(ne, &runtime);
9344
9345 // r3: encoding of subject string (1 if ascii, 0 if two_byte);
9346 // r7: code
9347 // subject: Subject string
9348 // regexp_data: RegExp data (FixedArray)
9349 // Load used arguments before starting to push arguments for call to native
9350 // RegExp code to avoid handling changing stack height.
9351 __ ldr(r1, MemOperand(sp, kPreviousIndexOffset));
9352 __ mov(r1, Operand(r1, ASR, kSmiTagSize));
9353
9354 // r1: previous index
9355 // r3: encoding of subject string (1 if ascii, 0 if two_byte);
9356 // r7: code
9357 // subject: Subject string
9358 // regexp_data: RegExp data (FixedArray)
9359 // All checks done. Now push arguments for native regexp code.
9360 __ IncrementCounter(&Counters::regexp_entry_native, 1, r0, r2);
9361
9362 static const int kRegExpExecuteArguments = 7;
9363 __ push(lr);
9364 __ PrepareCallCFunction(kRegExpExecuteArguments, r0);
9365
9366 // Argument 7 (sp[8]): Indicate that this is a direct call from JavaScript.
9367 __ mov(r0, Operand(1));
9368 __ str(r0, MemOperand(sp, 2 * kPointerSize));
9369
9370 // Argument 6 (sp[4]): Start (high end) of backtracking stack memory area.
9371 __ mov(r0, Operand(address_of_regexp_stack_memory_address));
9372 __ ldr(r0, MemOperand(r0, 0));
9373 __ mov(r2, Operand(address_of_regexp_stack_memory_size));
9374 __ ldr(r2, MemOperand(r2, 0));
9375 __ add(r0, r0, Operand(r2));
9376 __ str(r0, MemOperand(sp, 1 * kPointerSize));
9377
9378 // Argument 5 (sp[0]): static offsets vector buffer.
9379 __ mov(r0, Operand(ExternalReference::address_of_static_offsets_vector()));
9380 __ str(r0, MemOperand(sp, 0 * kPointerSize));
9381
9382 // For arguments 4 and 3 get string length, calculate start of string data and
9383 // calculate the shift of the index (0 for ASCII and 1 for two byte).
9384 __ ldr(r0, FieldMemOperand(subject, String::kLengthOffset));
ager@chromium.orgac091b72010-05-05 07:34:42 +00009385 __ mov(r0, Operand(r0, ASR, kSmiTagSize));
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009386 ASSERT_EQ(SeqAsciiString::kHeaderSize, SeqTwoByteString::kHeaderSize);
9387 __ add(r9, subject, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
9388 __ eor(r3, r3, Operand(1));
9389 // Argument 4 (r3): End of string data
9390 // Argument 3 (r2): Start of string data
9391 __ add(r2, r9, Operand(r1, LSL, r3));
9392 __ add(r3, r9, Operand(r0, LSL, r3));
9393
9394 // Argument 2 (r1): Previous index.
9395 // Already there
9396
9397 // Argument 1 (r0): Subject string.
9398 __ mov(r0, subject);
9399
9400 // Locate the code entry and call it.
9401 __ add(r7, r7, Operand(Code::kHeaderSize - kHeapObjectTag));
9402 __ CallCFunction(r7, kRegExpExecuteArguments);
9403 __ pop(lr);
9404
9405 // r0: result
9406 // subject: subject string (callee saved)
9407 // regexp_data: RegExp data (callee saved)
9408 // last_match_info_elements: Last match info elements (callee saved)
9409
9410 // Check the result.
9411 Label success;
9412 __ cmp(r0, Operand(NativeRegExpMacroAssembler::SUCCESS));
9413 __ b(eq, &success);
9414 Label failure;
9415 __ cmp(r0, Operand(NativeRegExpMacroAssembler::FAILURE));
9416 __ b(eq, &failure);
9417 __ cmp(r0, Operand(NativeRegExpMacroAssembler::EXCEPTION));
9418 // If not exception it can only be retry. Handle that in the runtime system.
9419 __ b(ne, &runtime);
9420 // Result must now be exception. If there is no pending exception already a
9421 // stack overflow (on the backtrack stack) was detected in RegExp code but
9422 // haven't created the exception yet. Handle that in the runtime system.
9423 // TODO(592): Rerunning the RegExp to get the stack overflow exception.
9424 __ mov(r0, Operand(ExternalReference::the_hole_value_location()));
9425 __ ldr(r0, MemOperand(r0, 0));
9426 __ mov(r1, Operand(ExternalReference(Top::k_pending_exception_address)));
9427 __ ldr(r1, MemOperand(r1, 0));
9428 __ cmp(r0, r1);
9429 __ b(eq, &runtime);
9430 __ bind(&failure);
9431 // For failure and exception return null.
9432 __ mov(r0, Operand(Factory::null_value()));
9433 __ add(sp, sp, Operand(4 * kPointerSize));
9434 __ Ret();
9435
9436 // Process the result from the native regexp code.
9437 __ bind(&success);
9438 __ ldr(r1,
9439 FieldMemOperand(regexp_data, JSRegExp::kIrregexpCaptureCountOffset));
9440 // Calculate number of capture registers (number_of_captures + 1) * 2.
9441 ASSERT_EQ(0, kSmiTag);
9442 ASSERT_EQ(1, kSmiTagSize + kSmiShiftSize);
9443 __ add(r1, r1, Operand(2)); // r1 was a smi.
9444
9445 // r1: number of capture registers
9446 // r4: subject string
9447 // Store the capture count.
9448 __ mov(r2, Operand(r1, LSL, kSmiTagSize + kSmiShiftSize)); // To smi.
9449 __ str(r2, FieldMemOperand(last_match_info_elements,
9450 RegExpImpl::kLastCaptureCountOffset));
9451 // Store last subject and last input.
9452 __ mov(r3, last_match_info_elements); // Moved up to reduce latency.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009453 __ str(subject,
9454 FieldMemOperand(last_match_info_elements,
9455 RegExpImpl::kLastSubjectOffset));
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00009456 __ RecordWrite(r3, Operand(RegExpImpl::kLastSubjectOffset), r2, r7);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009457 __ str(subject,
9458 FieldMemOperand(last_match_info_elements,
9459 RegExpImpl::kLastInputOffset));
9460 __ mov(r3, last_match_info_elements);
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00009461 __ RecordWrite(r3, Operand(RegExpImpl::kLastInputOffset), r2, r7);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009462
9463 // Get the static offsets vector filled by the native regexp code.
9464 ExternalReference address_of_static_offsets_vector =
9465 ExternalReference::address_of_static_offsets_vector();
9466 __ mov(r2, Operand(address_of_static_offsets_vector));
9467
9468 // r1: number of capture registers
9469 // r2: offsets vector
9470 Label next_capture, done;
9471 // Capture register counter starts from number of capture registers and
9472 // counts down until wraping after zero.
9473 __ add(r0,
9474 last_match_info_elements,
9475 Operand(RegExpImpl::kFirstCaptureOffset - kHeapObjectTag));
9476 __ bind(&next_capture);
9477 __ sub(r1, r1, Operand(1), SetCC);
9478 __ b(mi, &done);
9479 // Read the value from the static offsets vector buffer.
9480 __ ldr(r3, MemOperand(r2, kPointerSize, PostIndex));
9481 // Store the smi value in the last match info.
9482 __ mov(r3, Operand(r3, LSL, kSmiTagSize));
9483 __ str(r3, MemOperand(r0, kPointerSize, PostIndex));
9484 __ jmp(&next_capture);
9485 __ bind(&done);
9486
9487 // Return last match info.
9488 __ ldr(r0, MemOperand(sp, kLastMatchInfoOffset));
9489 __ add(sp, sp, Operand(4 * kPointerSize));
9490 __ Ret();
9491
9492 // Do the runtime call to execute the regexp.
9493 __ bind(&runtime);
9494 __ TailCallRuntime(Runtime::kRegExpExec, 4, 1);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00009495#endif // V8_INTERPRETED_REGEXP
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009496}
9497
9498
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009499void CallFunctionStub::Generate(MacroAssembler* masm) {
9500 Label slow;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009501
9502 // If the receiver might be a value (string, number or boolean) check for this
9503 // and box it if it is.
9504 if (ReceiverMightBeValue()) {
9505 // Get the receiver from the stack.
9506 // function, receiver [, arguments]
9507 Label receiver_is_value, receiver_is_js_object;
9508 __ ldr(r1, MemOperand(sp, argc_ * kPointerSize));
9509
9510 // Check if receiver is a smi (which is a number value).
9511 __ BranchOnSmi(r1, &receiver_is_value);
9512
9513 // Check if the receiver is a valid JS object.
9514 __ CompareObjectType(r1, r2, r2, FIRST_JS_OBJECT_TYPE);
9515 __ b(ge, &receiver_is_js_object);
9516
9517 // Call the runtime to box the value.
9518 __ bind(&receiver_is_value);
9519 __ EnterInternalFrame();
9520 __ push(r1);
9521 __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_JS);
9522 __ LeaveInternalFrame();
9523 __ str(r0, MemOperand(sp, argc_ * kPointerSize));
9524
9525 __ bind(&receiver_is_js_object);
9526 }
9527
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009528 // Get the function to call from the stack.
9529 // function, receiver [, arguments]
9530 __ ldr(r1, MemOperand(sp, (argc_ + 1) * kPointerSize));
9531
9532 // Check that the function is really a JavaScript function.
9533 // r1: pushed function (to be verified)
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009534 __ BranchOnSmi(r1, &slow);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009535 // Get the map of the function object.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009536 __ CompareObjectType(r1, r2, r2, JS_FUNCTION_TYPE);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009537 __ b(ne, &slow);
9538
9539 // Fast-case: Invoke the function now.
9540 // r1: pushed function
9541 ParameterCount actual(argc_);
9542 __ InvokeFunction(r1, actual, JUMP_FUNCTION);
9543
9544 // Slow-case: Non-function called.
9545 __ bind(&slow);
ager@chromium.org5c838252010-02-19 08:53:10 +00009546 // CALL_NON_FUNCTION expects the non-function callee as receiver (instead
9547 // of the original receiver from the call site).
9548 __ str(r1, MemOperand(sp, argc_ * kPointerSize));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009549 __ mov(r0, Operand(argc_)); // Setup the number of arguments.
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009550 __ mov(r2, Operand(0));
9551 __ GetBuiltinEntry(r3, Builtins::CALL_NON_FUNCTION);
9552 __ Jump(Handle<Code>(Builtins::builtin(Builtins::ArgumentsAdaptorTrampoline)),
9553 RelocInfo::CODE_TARGET);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009554}
9555
9556
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00009557// Unfortunately you have to run without snapshots to see most of these
9558// names in the profile since most compare stubs end up in the snapshot.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00009559const char* CompareStub::GetName() {
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00009560 if (name_ != NULL) return name_;
9561 const int kMaxNameLength = 100;
9562 name_ = Bootstrapper::AllocateAutoDeletedArray(kMaxNameLength);
9563 if (name_ == NULL) return "OOM";
9564
9565 const char* cc_name;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00009566 switch (cc_) {
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00009567 case lt: cc_name = "LT"; break;
9568 case gt: cc_name = "GT"; break;
9569 case le: cc_name = "LE"; break;
9570 case ge: cc_name = "GE"; break;
9571 case eq: cc_name = "EQ"; break;
9572 case ne: cc_name = "NE"; break;
9573 default: cc_name = "UnknownCondition"; break;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00009574 }
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00009575
9576 const char* strict_name = "";
9577 if (strict_ && (cc_ == eq || cc_ == ne)) {
9578 strict_name = "_STRICT";
9579 }
9580
9581 const char* never_nan_nan_name = "";
9582 if (never_nan_nan_ && (cc_ == eq || cc_ == ne)) {
9583 never_nan_nan_name = "_NO_NAN";
9584 }
9585
9586 const char* include_number_compare_name = "";
9587 if (!include_number_compare_) {
9588 include_number_compare_name = "_NO_NUMBER";
9589 }
9590
9591 OS::SNPrintF(Vector<char>(name_, kMaxNameLength),
9592 "CompareStub_%s%s%s%s",
9593 cc_name,
9594 strict_name,
9595 never_nan_nan_name,
9596 include_number_compare_name);
9597 return name_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00009598}
9599
9600
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009601int CompareStub::MinorKey() {
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009602 // Encode the three parameters in a unique 16 bit value. To avoid duplicate
9603 // stubs the never NaN NaN condition is only taken into account if the
9604 // condition is equals.
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00009605 ASSERT((static_cast<unsigned>(cc_) >> 28) < (1 << 13));
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009606 return ConditionField::encode(static_cast<unsigned>(cc_) >> 28)
9607 | StrictField::encode(strict_)
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00009608 | NeverNanNanField::encode(cc_ == eq ? never_nan_nan_ : false)
9609 | IncludeNumberCompareField::encode(include_number_compare_);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009610}
9611
9612
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009613// StringCharCodeAtGenerator
9614
9615void StringCharCodeAtGenerator::GenerateFast(MacroAssembler* masm) {
9616 Label flat_string;
ager@chromium.orgac091b72010-05-05 07:34:42 +00009617 Label ascii_string;
9618 Label got_char_code;
9619
9620 // If the receiver is a smi trigger the non-string case.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009621 __ BranchOnSmi(object_, receiver_not_string_);
ager@chromium.orgac091b72010-05-05 07:34:42 +00009622
9623 // Fetch the instance type of the receiver into result register.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009624 __ ldr(result_, FieldMemOperand(object_, HeapObject::kMapOffset));
9625 __ ldrb(result_, FieldMemOperand(result_, Map::kInstanceTypeOffset));
ager@chromium.orgac091b72010-05-05 07:34:42 +00009626 // If the receiver is not a string trigger the non-string case.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009627 __ tst(result_, Operand(kIsNotStringMask));
9628 __ b(ne, receiver_not_string_);
ager@chromium.orgac091b72010-05-05 07:34:42 +00009629
9630 // If the index is non-smi trigger the non-smi case.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009631 __ BranchOnNotSmi(index_, &index_not_smi_);
9632
9633 // Put smi-tagged index into scratch register.
9634 __ mov(scratch_, index_);
9635 __ bind(&got_smi_index_);
ager@chromium.orgac091b72010-05-05 07:34:42 +00009636
9637 // Check for index out of range.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009638 __ ldr(ip, FieldMemOperand(object_, String::kLengthOffset));
9639 __ cmp(ip, Operand(scratch_));
9640 __ b(ls, index_out_of_range_);
ager@chromium.orgac091b72010-05-05 07:34:42 +00009641
9642 // We need special handling for non-flat strings.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009643 ASSERT(kSeqStringTag == 0);
9644 __ tst(result_, Operand(kStringRepresentationMask));
9645 __ b(eq, &flat_string);
ager@chromium.orgac091b72010-05-05 07:34:42 +00009646
9647 // Handle non-flat strings.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009648 __ tst(result_, Operand(kIsConsStringMask));
9649 __ b(eq, &call_runtime_);
ager@chromium.orgac091b72010-05-05 07:34:42 +00009650
9651 // ConsString.
9652 // Check whether the right hand side is the empty string (i.e. if
9653 // this is really a flat string in a cons string). If that is not
9654 // the case we would rather go to the runtime system now to flatten
9655 // the string.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009656 __ ldr(result_, FieldMemOperand(object_, ConsString::kSecondOffset));
9657 __ LoadRoot(ip, Heap::kEmptyStringRootIndex);
9658 __ cmp(result_, Operand(ip));
9659 __ b(ne, &call_runtime_);
ager@chromium.orgac091b72010-05-05 07:34:42 +00009660 // Get the first of the two strings and load its instance type.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009661 __ ldr(object_, FieldMemOperand(object_, ConsString::kFirstOffset));
9662 __ ldr(result_, FieldMemOperand(object_, HeapObject::kMapOffset));
9663 __ ldrb(result_, FieldMemOperand(result_, Map::kInstanceTypeOffset));
9664 // If the first cons component is also non-flat, then go to runtime.
9665 ASSERT(kSeqStringTag == 0);
9666 __ tst(result_, Operand(kStringRepresentationMask));
9667 __ b(nz, &call_runtime_);
9668
9669 // Check for 1-byte or 2-byte string.
9670 __ bind(&flat_string);
9671 ASSERT(kAsciiStringTag != 0);
9672 __ tst(result_, Operand(kStringEncodingMask));
9673 __ b(nz, &ascii_string);
9674
9675 // 2-byte string.
9676 // Load the 2-byte character code into the result register. We can
9677 // add without shifting since the smi tag size is the log2 of the
9678 // number of bytes in a two-byte character.
9679 ASSERT(kSmiTag == 0 && kSmiTagSize == 1 && kSmiShiftSize == 0);
9680 __ add(scratch_, object_, Operand(scratch_));
9681 __ ldrh(result_, FieldMemOperand(scratch_, SeqTwoByteString::kHeaderSize));
9682 __ jmp(&got_char_code);
ager@chromium.orgac091b72010-05-05 07:34:42 +00009683
9684 // ASCII string.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009685 // Load the byte into the result register.
ager@chromium.orgac091b72010-05-05 07:34:42 +00009686 __ bind(&ascii_string);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009687 __ add(scratch_, object_, Operand(scratch_, LSR, kSmiTagSize));
9688 __ ldrb(result_, FieldMemOperand(scratch_, SeqAsciiString::kHeaderSize));
ager@chromium.orgac091b72010-05-05 07:34:42 +00009689
9690 __ bind(&got_char_code);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009691 __ mov(result_, Operand(result_, LSL, kSmiTagSize));
9692 __ bind(&exit_);
ager@chromium.orgac091b72010-05-05 07:34:42 +00009693}
9694
9695
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009696void StringCharCodeAtGenerator::GenerateSlow(
9697 MacroAssembler* masm, const RuntimeCallHelper& call_helper) {
9698 __ Abort("Unexpected fallthrough to CharCodeAt slow case");
ager@chromium.orgac091b72010-05-05 07:34:42 +00009699
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009700 // Index is not a smi.
9701 __ bind(&index_not_smi_);
9702 // If index is a heap number, try converting it to an integer.
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00009703 __ CheckMap(index_,
9704 scratch_,
9705 Heap::kHeapNumberMapRootIndex,
9706 index_not_number_,
9707 true);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009708 call_helper.BeforeCall(masm);
sgjesse@chromium.org82dbbab2010-06-02 08:57:44 +00009709 __ Push(object_, index_);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009710 __ push(index_); // Consumed by runtime conversion function.
9711 if (index_flags_ == STRING_INDEX_IS_NUMBER) {
9712 __ CallRuntime(Runtime::kNumberToIntegerMapMinusZero, 1);
9713 } else {
9714 ASSERT(index_flags_ == STRING_INDEX_IS_ARRAY_INDEX);
9715 // NumberToSmi discards numbers that are not exact integers.
9716 __ CallRuntime(Runtime::kNumberToSmi, 1);
9717 }
9718 if (!scratch_.is(r0)) {
9719 // Save the conversion result before the pop instructions below
9720 // have a chance to overwrite it.
9721 __ mov(scratch_, r0);
9722 }
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009723 __ pop(index_);
9724 __ pop(object_);
sgjesse@chromium.org82dbbab2010-06-02 08:57:44 +00009725 // Reload the instance type.
9726 __ ldr(result_, FieldMemOperand(object_, HeapObject::kMapOffset));
9727 __ ldrb(result_, FieldMemOperand(result_, Map::kInstanceTypeOffset));
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009728 call_helper.AfterCall(masm);
9729 // If index is still not a smi, it must be out of range.
9730 __ BranchOnNotSmi(scratch_, index_out_of_range_);
9731 // Otherwise, return to the fast path.
9732 __ jmp(&got_smi_index_);
ager@chromium.orgac091b72010-05-05 07:34:42 +00009733
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009734 // Call runtime. We get here when the receiver is a string and the
9735 // index is a number, but the code of getting the actual character
9736 // is too complex (e.g., when the string needs to be flattened).
9737 __ bind(&call_runtime_);
9738 call_helper.BeforeCall(masm);
9739 __ Push(object_, index_);
9740 __ CallRuntime(Runtime::kStringCharCodeAt, 2);
9741 if (!result_.is(r0)) {
9742 __ mov(result_, r0);
9743 }
9744 call_helper.AfterCall(masm);
9745 __ jmp(&exit_);
9746
9747 __ Abort("Unexpected fallthrough from CharCodeAt slow case");
9748}
9749
9750
9751// -------------------------------------------------------------------------
9752// StringCharFromCodeGenerator
9753
9754void StringCharFromCodeGenerator::GenerateFast(MacroAssembler* masm) {
ager@chromium.orgac091b72010-05-05 07:34:42 +00009755 // Fast case of Heap::LookupSingleCharacterStringFromCode.
9756 ASSERT(kSmiTag == 0);
9757 ASSERT(kSmiShiftSize == 0);
9758 ASSERT(IsPowerOf2(String::kMaxAsciiCharCode + 1));
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009759 __ tst(code_,
9760 Operand(kSmiTagMask |
9761 ((~String::kMaxAsciiCharCode) << kSmiTagSize)));
9762 __ b(nz, &slow_case_);
ager@chromium.orgac091b72010-05-05 07:34:42 +00009763
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009764 __ LoadRoot(result_, Heap::kSingleCharacterStringCacheRootIndex);
9765 // At this point code register contains smi tagged ascii char code.
ager@chromium.orgac091b72010-05-05 07:34:42 +00009766 ASSERT(kSmiTag == 0);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009767 __ add(result_, result_, Operand(code_, LSL, kPointerSizeLog2 - kSmiTagSize));
9768 __ ldr(result_, FieldMemOperand(result_, FixedArray::kHeaderSize));
9769 __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
9770 __ cmp(result_, Operand(ip));
9771 __ b(eq, &slow_case_);
9772 __ bind(&exit_);
9773}
ager@chromium.orgac091b72010-05-05 07:34:42 +00009774
ager@chromium.orgac091b72010-05-05 07:34:42 +00009775
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009776void StringCharFromCodeGenerator::GenerateSlow(
9777 MacroAssembler* masm, const RuntimeCallHelper& call_helper) {
9778 __ Abort("Unexpected fallthrough to CharFromCode slow case");
9779
9780 __ bind(&slow_case_);
9781 call_helper.BeforeCall(masm);
9782 __ push(code_);
9783 __ CallRuntime(Runtime::kCharFromCode, 1);
9784 if (!result_.is(r0)) {
9785 __ mov(result_, r0);
ager@chromium.orgac091b72010-05-05 07:34:42 +00009786 }
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009787 call_helper.AfterCall(masm);
9788 __ jmp(&exit_);
9789
9790 __ Abort("Unexpected fallthrough from CharFromCode slow case");
9791}
9792
9793
9794// -------------------------------------------------------------------------
9795// StringCharAtGenerator
9796
9797void StringCharAtGenerator::GenerateFast(MacroAssembler* masm) {
9798 char_code_at_generator_.GenerateFast(masm);
9799 char_from_code_generator_.GenerateFast(masm);
9800}
9801
9802
9803void StringCharAtGenerator::GenerateSlow(
9804 MacroAssembler* masm, const RuntimeCallHelper& call_helper) {
9805 char_code_at_generator_.GenerateSlow(masm, call_helper);
9806 char_from_code_generator_.GenerateSlow(masm, call_helper);
ager@chromium.orgac091b72010-05-05 07:34:42 +00009807}
9808
9809
9810void StringHelper::GenerateCopyCharacters(MacroAssembler* masm,
9811 Register dest,
9812 Register src,
9813 Register count,
9814 Register scratch,
9815 bool ascii) {
ager@chromium.org5c838252010-02-19 08:53:10 +00009816 Label loop;
9817 Label done;
9818 // This loop just copies one character at a time, as it is only used for very
9819 // short strings.
9820 if (!ascii) {
9821 __ add(count, count, Operand(count), SetCC);
9822 } else {
9823 __ cmp(count, Operand(0));
9824 }
9825 __ b(eq, &done);
9826
9827 __ bind(&loop);
9828 __ ldrb(scratch, MemOperand(src, 1, PostIndex));
9829 // Perform sub between load and dependent store to get the load time to
9830 // complete.
9831 __ sub(count, count, Operand(1), SetCC);
9832 __ strb(scratch, MemOperand(dest, 1, PostIndex));
9833 // last iteration.
9834 __ b(gt, &loop);
9835
9836 __ bind(&done);
9837}
9838
9839
9840enum CopyCharactersFlags {
9841 COPY_ASCII = 1,
9842 DEST_ALWAYS_ALIGNED = 2
9843};
9844
9845
ager@chromium.orgac091b72010-05-05 07:34:42 +00009846void StringHelper::GenerateCopyCharactersLong(MacroAssembler* masm,
9847 Register dest,
9848 Register src,
9849 Register count,
9850 Register scratch1,
9851 Register scratch2,
9852 Register scratch3,
9853 Register scratch4,
9854 Register scratch5,
9855 int flags) {
ager@chromium.org5c838252010-02-19 08:53:10 +00009856 bool ascii = (flags & COPY_ASCII) != 0;
9857 bool dest_always_aligned = (flags & DEST_ALWAYS_ALIGNED) != 0;
9858
9859 if (dest_always_aligned && FLAG_debug_code) {
9860 // Check that destination is actually word aligned if the flag says
9861 // that it is.
9862 __ tst(dest, Operand(kPointerAlignmentMask));
9863 __ Check(eq, "Destination of copy not aligned.");
9864 }
9865
9866 const int kReadAlignment = 4;
9867 const int kReadAlignmentMask = kReadAlignment - 1;
9868 // Ensure that reading an entire aligned word containing the last character
9869 // of a string will not read outside the allocated area (because we pad up
9870 // to kObjectAlignment).
9871 ASSERT(kObjectAlignment >= kReadAlignment);
9872 // Assumes word reads and writes are little endian.
9873 // Nothing to do for zero characters.
9874 Label done;
9875 if (!ascii) {
9876 __ add(count, count, Operand(count), SetCC);
9877 } else {
9878 __ cmp(count, Operand(0));
9879 }
9880 __ b(eq, &done);
9881
9882 // Assume that you cannot read (or write) unaligned.
9883 Label byte_loop;
9884 // Must copy at least eight bytes, otherwise just do it one byte at a time.
9885 __ cmp(count, Operand(8));
9886 __ add(count, dest, Operand(count));
9887 Register limit = count; // Read until src equals this.
9888 __ b(lt, &byte_loop);
9889
9890 if (!dest_always_aligned) {
9891 // Align dest by byte copying. Copies between zero and three bytes.
9892 __ and_(scratch4, dest, Operand(kReadAlignmentMask), SetCC);
9893 Label dest_aligned;
9894 __ b(eq, &dest_aligned);
9895 __ cmp(scratch4, Operand(2));
9896 __ ldrb(scratch1, MemOperand(src, 1, PostIndex));
9897 __ ldrb(scratch2, MemOperand(src, 1, PostIndex), le);
9898 __ ldrb(scratch3, MemOperand(src, 1, PostIndex), lt);
9899 __ strb(scratch1, MemOperand(dest, 1, PostIndex));
9900 __ strb(scratch2, MemOperand(dest, 1, PostIndex), le);
9901 __ strb(scratch3, MemOperand(dest, 1, PostIndex), lt);
9902 __ bind(&dest_aligned);
9903 }
9904
9905 Label simple_loop;
9906
9907 __ sub(scratch4, dest, Operand(src));
9908 __ and_(scratch4, scratch4, Operand(0x03), SetCC);
9909 __ b(eq, &simple_loop);
9910 // Shift register is number of bits in a source word that
9911 // must be combined with bits in the next source word in order
9912 // to create a destination word.
9913
9914 // Complex loop for src/dst that are not aligned the same way.
9915 {
9916 Label loop;
9917 __ mov(scratch4, Operand(scratch4, LSL, 3));
9918 Register left_shift = scratch4;
9919 __ and_(src, src, Operand(~3)); // Round down to load previous word.
9920 __ ldr(scratch1, MemOperand(src, 4, PostIndex));
9921 // Store the "shift" most significant bits of scratch in the least
9922 // signficant bits (i.e., shift down by (32-shift)).
9923 __ rsb(scratch2, left_shift, Operand(32));
9924 Register right_shift = scratch2;
9925 __ mov(scratch1, Operand(scratch1, LSR, right_shift));
9926
9927 __ bind(&loop);
9928 __ ldr(scratch3, MemOperand(src, 4, PostIndex));
9929 __ sub(scratch5, limit, Operand(dest));
9930 __ orr(scratch1, scratch1, Operand(scratch3, LSL, left_shift));
9931 __ str(scratch1, MemOperand(dest, 4, PostIndex));
9932 __ mov(scratch1, Operand(scratch3, LSR, right_shift));
9933 // Loop if four or more bytes left to copy.
9934 // Compare to eight, because we did the subtract before increasing dst.
9935 __ sub(scratch5, scratch5, Operand(8), SetCC);
9936 __ b(ge, &loop);
9937 }
9938 // There is now between zero and three bytes left to copy (negative that
9939 // number is in scratch5), and between one and three bytes already read into
9940 // scratch1 (eight times that number in scratch4). We may have read past
9941 // the end of the string, but because objects are aligned, we have not read
9942 // past the end of the object.
9943 // Find the minimum of remaining characters to move and preloaded characters
9944 // and write those as bytes.
9945 __ add(scratch5, scratch5, Operand(4), SetCC);
9946 __ b(eq, &done);
9947 __ cmp(scratch4, Operand(scratch5, LSL, 3), ne);
9948 // Move minimum of bytes read and bytes left to copy to scratch4.
9949 __ mov(scratch5, Operand(scratch4, LSR, 3), LeaveCC, lt);
9950 // Between one and three (value in scratch5) characters already read into
9951 // scratch ready to write.
9952 __ cmp(scratch5, Operand(2));
9953 __ strb(scratch1, MemOperand(dest, 1, PostIndex));
9954 __ mov(scratch1, Operand(scratch1, LSR, 8), LeaveCC, ge);
9955 __ strb(scratch1, MemOperand(dest, 1, PostIndex), ge);
9956 __ mov(scratch1, Operand(scratch1, LSR, 8), LeaveCC, gt);
9957 __ strb(scratch1, MemOperand(dest, 1, PostIndex), gt);
9958 // Copy any remaining bytes.
9959 __ b(&byte_loop);
9960
9961 // Simple loop.
9962 // Copy words from src to dst, until less than four bytes left.
9963 // Both src and dest are word aligned.
9964 __ bind(&simple_loop);
9965 {
9966 Label loop;
9967 __ bind(&loop);
9968 __ ldr(scratch1, MemOperand(src, 4, PostIndex));
9969 __ sub(scratch3, limit, Operand(dest));
9970 __ str(scratch1, MemOperand(dest, 4, PostIndex));
9971 // Compare to 8, not 4, because we do the substraction before increasing
9972 // dest.
9973 __ cmp(scratch3, Operand(8));
9974 __ b(ge, &loop);
9975 }
9976
9977 // Copy bytes from src to dst until dst hits limit.
9978 __ bind(&byte_loop);
9979 __ cmp(dest, Operand(limit));
9980 __ ldrb(scratch1, MemOperand(src, 1, PostIndex), lt);
9981 __ b(ge, &done);
9982 __ strb(scratch1, MemOperand(dest, 1, PostIndex));
9983 __ b(&byte_loop);
9984
9985 __ bind(&done);
9986}
9987
9988
ager@chromium.orgac091b72010-05-05 07:34:42 +00009989void StringHelper::GenerateTwoCharacterSymbolTableProbe(MacroAssembler* masm,
9990 Register c1,
9991 Register c2,
9992 Register scratch1,
9993 Register scratch2,
9994 Register scratch3,
9995 Register scratch4,
9996 Register scratch5,
9997 Label* not_found) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009998 // Register scratch3 is the general scratch register in this function.
9999 Register scratch = scratch3;
10000
10001 // Make sure that both characters are not digits as such strings has a
10002 // different hash algorithm. Don't try to look for these in the symbol table.
10003 Label not_array_index;
10004 __ sub(scratch, c1, Operand(static_cast<int>('0')));
10005 __ cmp(scratch, Operand(static_cast<int>('9' - '0')));
10006 __ b(hi, &not_array_index);
10007 __ sub(scratch, c2, Operand(static_cast<int>('0')));
10008 __ cmp(scratch, Operand(static_cast<int>('9' - '0')));
10009
10010 // If check failed combine both characters into single halfword.
10011 // This is required by the contract of the method: code at the
10012 // not_found branch expects this combination in c1 register
10013 __ orr(c1, c1, Operand(c2, LSL, kBitsPerByte), LeaveCC, ls);
10014 __ b(ls, not_found);
10015
10016 __ bind(&not_array_index);
10017 // Calculate the two character string hash.
10018 Register hash = scratch1;
ager@chromium.orgac091b72010-05-05 07:34:42 +000010019 StringHelper::GenerateHashInit(masm, hash, c1);
10020 StringHelper::GenerateHashAddCharacter(masm, hash, c2);
10021 StringHelper::GenerateHashGetHash(masm, hash);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010022
10023 // Collect the two characters in a register.
10024 Register chars = c1;
10025 __ orr(chars, chars, Operand(c2, LSL, kBitsPerByte));
10026
10027 // chars: two character string, char 1 in byte 0 and char 2 in byte 1.
10028 // hash: hash of two character string.
10029
10030 // Load symbol table
10031 // Load address of first element of the symbol table.
10032 Register symbol_table = c2;
10033 __ LoadRoot(symbol_table, Heap::kSymbolTableRootIndex);
10034
10035 // Load undefined value
10036 Register undefined = scratch4;
10037 __ LoadRoot(undefined, Heap::kUndefinedValueRootIndex);
10038
10039 // Calculate capacity mask from the symbol table capacity.
10040 Register mask = scratch2;
10041 __ ldr(mask, FieldMemOperand(symbol_table, SymbolTable::kCapacityOffset));
10042 __ mov(mask, Operand(mask, ASR, 1));
10043 __ sub(mask, mask, Operand(1));
10044
10045 // Calculate untagged address of the first element of the symbol table.
10046 Register first_symbol_table_element = symbol_table;
10047 __ add(first_symbol_table_element, symbol_table,
10048 Operand(SymbolTable::kElementsStartOffset - kHeapObjectTag));
10049
10050 // Registers
10051 // chars: two character string, char 1 in byte 0 and char 2 in byte 1.
10052 // hash: hash of two character string
10053 // mask: capacity mask
10054 // first_symbol_table_element: address of the first element of
10055 // the symbol table
10056 // scratch: -
10057
10058 // Perform a number of probes in the symbol table.
10059 static const int kProbes = 4;
10060 Label found_in_symbol_table;
10061 Label next_probe[kProbes];
10062 for (int i = 0; i < kProbes; i++) {
10063 Register candidate = scratch5; // Scratch register contains candidate.
10064
10065 // Calculate entry in symbol table.
10066 if (i > 0) {
10067 __ add(candidate, hash, Operand(SymbolTable::GetProbeOffset(i)));
10068 } else {
10069 __ mov(candidate, hash);
10070 }
10071
10072 __ and_(candidate, candidate, Operand(mask));
10073
10074 // Load the entry from the symble table.
10075 ASSERT_EQ(1, SymbolTable::kEntrySize);
10076 __ ldr(candidate,
10077 MemOperand(first_symbol_table_element,
10078 candidate,
10079 LSL,
10080 kPointerSizeLog2));
10081
10082 // If entry is undefined no string with this hash can be found.
10083 __ cmp(candidate, undefined);
10084 __ b(eq, not_found);
10085
10086 // If length is not 2 the string is not a candidate.
10087 __ ldr(scratch, FieldMemOperand(candidate, String::kLengthOffset));
ager@chromium.orgac091b72010-05-05 07:34:42 +000010088 __ cmp(scratch, Operand(Smi::FromInt(2)));
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010089 __ b(ne, &next_probe[i]);
10090
10091 // Check that the candidate is a non-external ascii string.
10092 __ ldr(scratch, FieldMemOperand(candidate, HeapObject::kMapOffset));
10093 __ ldrb(scratch, FieldMemOperand(scratch, Map::kInstanceTypeOffset));
10094 __ JumpIfInstanceTypeIsNotSequentialAscii(scratch, scratch,
10095 &next_probe[i]);
10096
10097 // Check if the two characters match.
10098 // Assumes that word load is little endian.
10099 __ ldrh(scratch, FieldMemOperand(candidate, SeqAsciiString::kHeaderSize));
10100 __ cmp(chars, scratch);
10101 __ b(eq, &found_in_symbol_table);
10102 __ bind(&next_probe[i]);
10103 }
10104
10105 // No matching 2 character string found by probing.
10106 __ jmp(not_found);
10107
10108 // Scratch register contains result when we fall through to here.
10109 Register result = scratch;
10110 __ bind(&found_in_symbol_table);
ager@chromium.org357bf652010-04-12 11:30:10 +000010111 __ Move(r0, result);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010112}
10113
10114
ager@chromium.orgac091b72010-05-05 07:34:42 +000010115void StringHelper::GenerateHashInit(MacroAssembler* masm,
10116 Register hash,
10117 Register character) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010118 // hash = character + (character << 10);
10119 __ add(hash, character, Operand(character, LSL, 10));
10120 // hash ^= hash >> 6;
10121 __ eor(hash, hash, Operand(hash, ASR, 6));
10122}
10123
10124
ager@chromium.orgac091b72010-05-05 07:34:42 +000010125void StringHelper::GenerateHashAddCharacter(MacroAssembler* masm,
10126 Register hash,
10127 Register character) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010128 // hash += character;
10129 __ add(hash, hash, Operand(character));
10130 // hash += hash << 10;
10131 __ add(hash, hash, Operand(hash, LSL, 10));
10132 // hash ^= hash >> 6;
10133 __ eor(hash, hash, Operand(hash, ASR, 6));
10134}
10135
10136
ager@chromium.orgac091b72010-05-05 07:34:42 +000010137void StringHelper::GenerateHashGetHash(MacroAssembler* masm,
10138 Register hash) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010139 // hash += hash << 3;
10140 __ add(hash, hash, Operand(hash, LSL, 3));
10141 // hash ^= hash >> 11;
10142 __ eor(hash, hash, Operand(hash, ASR, 11));
10143 // hash += hash << 15;
10144 __ add(hash, hash, Operand(hash, LSL, 15), SetCC);
10145
10146 // if (hash == 0) hash = 27;
10147 __ mov(hash, Operand(27), LeaveCC, nz);
10148}
10149
10150
ager@chromium.org5c838252010-02-19 08:53:10 +000010151void SubStringStub::Generate(MacroAssembler* masm) {
10152 Label runtime;
10153
10154 // Stack frame on entry.
10155 // lr: return address
10156 // sp[0]: to
10157 // sp[4]: from
10158 // sp[8]: string
10159
10160 // This stub is called from the native-call %_SubString(...), so
10161 // nothing can be assumed about the arguments. It is tested that:
10162 // "string" is a sequential string,
10163 // both "from" and "to" are smis, and
10164 // 0 <= from <= to <= string.length.
10165 // If any of these assumptions fail, we call the runtime system.
10166
10167 static const int kToOffset = 0 * kPointerSize;
10168 static const int kFromOffset = 1 * kPointerSize;
10169 static const int kStringOffset = 2 * kPointerSize;
10170
10171
10172 // Check bounds and smi-ness.
10173 __ ldr(r7, MemOperand(sp, kToOffset));
10174 __ ldr(r6, MemOperand(sp, kFromOffset));
10175 ASSERT_EQ(0, kSmiTag);
10176 ASSERT_EQ(1, kSmiTagSize + kSmiShiftSize);
10177 // I.e., arithmetic shift right by one un-smi-tags.
10178 __ mov(r2, Operand(r7, ASR, 1), SetCC);
10179 __ mov(r3, Operand(r6, ASR, 1), SetCC, cc);
10180 // If either r2 or r6 had the smi tag bit set, then carry is set now.
10181 __ b(cs, &runtime); // Either "from" or "to" is not a smi.
10182 __ b(mi, &runtime); // From is negative.
10183
10184 __ sub(r2, r2, Operand(r3), SetCC);
10185 __ b(mi, &runtime); // Fail if from > to.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010186 // Special handling of sub-strings of length 1 and 2. One character strings
10187 // are handled in the runtime system (looked up in the single character
10188 // cache). Two character strings are looked for in the symbol cache.
ager@chromium.org5c838252010-02-19 08:53:10 +000010189 __ cmp(r2, Operand(2));
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010190 __ b(lt, &runtime);
ager@chromium.org5c838252010-02-19 08:53:10 +000010191
10192 // r2: length
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010193 // r3: from index (untaged smi)
ager@chromium.org5c838252010-02-19 08:53:10 +000010194 // r6: from (smi)
10195 // r7: to (smi)
10196
10197 // Make sure first argument is a sequential (or flat) string.
10198 __ ldr(r5, MemOperand(sp, kStringOffset));
10199 ASSERT_EQ(0, kSmiTag);
10200 __ tst(r5, Operand(kSmiTagMask));
10201 __ b(eq, &runtime);
10202 Condition is_string = masm->IsObjectStringType(r5, r1);
10203 __ b(NegateCondition(is_string), &runtime);
10204
10205 // r1: instance type
10206 // r2: length
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010207 // r3: from index (untaged smi)
ager@chromium.org5c838252010-02-19 08:53:10 +000010208 // r5: string
10209 // r6: from (smi)
10210 // r7: to (smi)
10211 Label seq_string;
10212 __ and_(r4, r1, Operand(kStringRepresentationMask));
10213 ASSERT(kSeqStringTag < kConsStringTag);
10214 ASSERT(kExternalStringTag > kConsStringTag);
10215 __ cmp(r4, Operand(kConsStringTag));
10216 __ b(gt, &runtime); // External strings go to runtime.
10217 __ b(lt, &seq_string); // Sequential strings are handled directly.
10218
10219 // Cons string. Try to recurse (once) on the first substring.
10220 // (This adds a little more generality than necessary to handle flattened
10221 // cons strings, but not much).
10222 __ ldr(r5, FieldMemOperand(r5, ConsString::kFirstOffset));
10223 __ ldr(r4, FieldMemOperand(r5, HeapObject::kMapOffset));
10224 __ ldrb(r1, FieldMemOperand(r4, Map::kInstanceTypeOffset));
10225 __ tst(r1, Operand(kStringRepresentationMask));
10226 ASSERT_EQ(0, kSeqStringTag);
10227 __ b(ne, &runtime); // Cons and External strings go to runtime.
10228
10229 // Definitly a sequential string.
10230 __ bind(&seq_string);
10231
10232 // r1: instance type.
10233 // r2: length
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010234 // r3: from index (untaged smi)
ager@chromium.org5c838252010-02-19 08:53:10 +000010235 // r5: string
10236 // r6: from (smi)
10237 // r7: to (smi)
10238 __ ldr(r4, FieldMemOperand(r5, String::kLengthOffset));
ager@chromium.orgac091b72010-05-05 07:34:42 +000010239 __ cmp(r4, Operand(r7));
ager@chromium.org5c838252010-02-19 08:53:10 +000010240 __ b(lt, &runtime); // Fail if to > length.
10241
10242 // r1: instance type.
10243 // r2: result string length.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010244 // r3: from index (untaged smi)
ager@chromium.org5c838252010-02-19 08:53:10 +000010245 // r5: string.
10246 // r6: from offset (smi)
10247 // Check for flat ascii string.
10248 Label non_ascii_flat;
10249 __ tst(r1, Operand(kStringEncodingMask));
10250 ASSERT_EQ(0, kTwoByteStringTag);
10251 __ b(eq, &non_ascii_flat);
10252
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010253 Label result_longer_than_two;
10254 __ cmp(r2, Operand(2));
10255 __ b(gt, &result_longer_than_two);
10256
10257 // Sub string of length 2 requested.
10258 // Get the two characters forming the sub string.
10259 __ add(r5, r5, Operand(r3));
10260 __ ldrb(r3, FieldMemOperand(r5, SeqAsciiString::kHeaderSize));
10261 __ ldrb(r4, FieldMemOperand(r5, SeqAsciiString::kHeaderSize + 1));
10262
10263 // Try to lookup two character string in symbol table.
10264 Label make_two_character_string;
ager@chromium.orgac091b72010-05-05 07:34:42 +000010265 StringHelper::GenerateTwoCharacterSymbolTableProbe(
10266 masm, r3, r4, r1, r5, r6, r7, r9, &make_two_character_string);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010267 __ IncrementCounter(&Counters::sub_string_native, 1, r3, r4);
10268 __ add(sp, sp, Operand(3 * kPointerSize));
10269 __ Ret();
10270
10271 // r2: result string length.
10272 // r3: two characters combined into halfword in little endian byte order.
10273 __ bind(&make_two_character_string);
10274 __ AllocateAsciiString(r0, r2, r4, r5, r9, &runtime);
10275 __ strh(r3, FieldMemOperand(r0, SeqAsciiString::kHeaderSize));
10276 __ IncrementCounter(&Counters::sub_string_native, 1, r3, r4);
10277 __ add(sp, sp, Operand(3 * kPointerSize));
10278 __ Ret();
10279
10280 __ bind(&result_longer_than_two);
10281
ager@chromium.org5c838252010-02-19 08:53:10 +000010282 // Allocate the result.
10283 __ AllocateAsciiString(r0, r2, r3, r4, r1, &runtime);
10284
10285 // r0: result string.
10286 // r2: result string length.
10287 // r5: string.
10288 // r6: from offset (smi)
10289 // Locate first character of result.
10290 __ add(r1, r0, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
10291 // Locate 'from' character of string.
10292 __ add(r5, r5, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
10293 __ add(r5, r5, Operand(r6, ASR, 1));
10294
10295 // r0: result string.
10296 // r1: first character of result string.
10297 // r2: result string length.
10298 // r5: first character of sub string to copy.
10299 ASSERT_EQ(0, SeqAsciiString::kHeaderSize & kObjectAlignmentMask);
ager@chromium.orgac091b72010-05-05 07:34:42 +000010300 StringHelper::GenerateCopyCharactersLong(masm, r1, r5, r2, r3, r4, r6, r7, r9,
10301 COPY_ASCII | DEST_ALWAYS_ALIGNED);
ager@chromium.org5c838252010-02-19 08:53:10 +000010302 __ IncrementCounter(&Counters::sub_string_native, 1, r3, r4);
10303 __ add(sp, sp, Operand(3 * kPointerSize));
10304 __ Ret();
10305
10306 __ bind(&non_ascii_flat);
10307 // r2: result string length.
10308 // r5: string.
10309 // r6: from offset (smi)
10310 // Check for flat two byte string.
10311
10312 // Allocate the result.
10313 __ AllocateTwoByteString(r0, r2, r1, r3, r4, &runtime);
10314
10315 // r0: result string.
10316 // r2: result string length.
10317 // r5: string.
10318 // Locate first character of result.
10319 __ add(r1, r0, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
10320 // Locate 'from' character of string.
10321 __ add(r5, r5, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
10322 // As "from" is a smi it is 2 times the value which matches the size of a two
10323 // byte character.
10324 __ add(r5, r5, Operand(r6));
10325
10326 // r0: result string.
10327 // r1: first character of result.
10328 // r2: result length.
10329 // r5: first character of string to copy.
10330 ASSERT_EQ(0, SeqTwoByteString::kHeaderSize & kObjectAlignmentMask);
ager@chromium.orgac091b72010-05-05 07:34:42 +000010331 StringHelper::GenerateCopyCharactersLong(masm, r1, r5, r2, r3, r4, r6, r7, r9,
10332 DEST_ALWAYS_ALIGNED);
ager@chromium.org5c838252010-02-19 08:53:10 +000010333 __ IncrementCounter(&Counters::sub_string_native, 1, r3, r4);
10334 __ add(sp, sp, Operand(3 * kPointerSize));
10335 __ Ret();
10336
10337 // Just jump to runtime to create the sub string.
10338 __ bind(&runtime);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010339 __ TailCallRuntime(Runtime::kSubString, 3, 1);
ager@chromium.org5c838252010-02-19 08:53:10 +000010340}
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010341
10342
10343void StringCompareStub::GenerateCompareFlatAsciiStrings(MacroAssembler* masm,
10344 Register left,
10345 Register right,
10346 Register scratch1,
10347 Register scratch2,
10348 Register scratch3,
10349 Register scratch4) {
10350 Label compare_lengths;
10351 // Find minimum length and length difference.
10352 __ ldr(scratch1, FieldMemOperand(left, String::kLengthOffset));
10353 __ ldr(scratch2, FieldMemOperand(right, String::kLengthOffset));
10354 __ sub(scratch3, scratch1, Operand(scratch2), SetCC);
10355 Register length_delta = scratch3;
10356 __ mov(scratch1, scratch2, LeaveCC, gt);
10357 Register min_length = scratch1;
ager@chromium.orgac091b72010-05-05 07:34:42 +000010358 ASSERT(kSmiTag == 0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010359 __ tst(min_length, Operand(min_length));
10360 __ b(eq, &compare_lengths);
10361
ager@chromium.orgac091b72010-05-05 07:34:42 +000010362 // Untag smi.
10363 __ mov(min_length, Operand(min_length, ASR, kSmiTagSize));
10364
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010365 // Setup registers so that we only need to increment one register
10366 // in the loop.
10367 __ add(scratch2, min_length,
10368 Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
10369 __ add(left, left, Operand(scratch2));
10370 __ add(right, right, Operand(scratch2));
10371 // Registers left and right points to the min_length character of strings.
10372 __ rsb(min_length, min_length, Operand(-1));
10373 Register index = min_length;
10374 // Index starts at -min_length.
10375
10376 {
10377 // Compare loop.
10378 Label loop;
10379 __ bind(&loop);
10380 // Compare characters.
10381 __ add(index, index, Operand(1), SetCC);
10382 __ ldrb(scratch2, MemOperand(left, index), ne);
10383 __ ldrb(scratch4, MemOperand(right, index), ne);
10384 // Skip to compare lengths with eq condition true.
10385 __ b(eq, &compare_lengths);
10386 __ cmp(scratch2, scratch4);
10387 __ b(eq, &loop);
10388 // Fallthrough with eq condition false.
10389 }
10390 // Compare lengths - strings up to min-length are equal.
10391 __ bind(&compare_lengths);
10392 ASSERT(Smi::FromInt(EQUAL) == static_cast<Smi*>(0));
10393 // Use zero length_delta as result.
10394 __ mov(r0, Operand(length_delta), SetCC, eq);
10395 // Fall through to here if characters compare not-equal.
10396 __ mov(r0, Operand(Smi::FromInt(GREATER)), LeaveCC, gt);
10397 __ mov(r0, Operand(Smi::FromInt(LESS)), LeaveCC, lt);
10398 __ Ret();
10399}
10400
10401
10402void StringCompareStub::Generate(MacroAssembler* masm) {
10403 Label runtime;
10404
10405 // Stack frame on entry.
ager@chromium.org5c838252010-02-19 08:53:10 +000010406 // sp[0]: right string
10407 // sp[4]: left string
10408 __ ldr(r0, MemOperand(sp, 1 * kPointerSize)); // left
10409 __ ldr(r1, MemOperand(sp, 0 * kPointerSize)); // right
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010410
10411 Label not_same;
10412 __ cmp(r0, r1);
10413 __ b(ne, &not_same);
10414 ASSERT_EQ(0, EQUAL);
10415 ASSERT_EQ(0, kSmiTag);
10416 __ mov(r0, Operand(Smi::FromInt(EQUAL)));
10417 __ IncrementCounter(&Counters::string_compare_native, 1, r1, r2);
10418 __ add(sp, sp, Operand(2 * kPointerSize));
10419 __ Ret();
10420
10421 __ bind(&not_same);
10422
10423 // Check that both objects are sequential ascii strings.
10424 __ JumpIfNotBothSequentialAsciiStrings(r0, r1, r2, r3, &runtime);
10425
10426 // Compare flat ascii strings natively. Remove arguments from stack first.
10427 __ IncrementCounter(&Counters::string_compare_native, 1, r2, r3);
10428 __ add(sp, sp, Operand(2 * kPointerSize));
10429 GenerateCompareFlatAsciiStrings(masm, r0, r1, r2, r3, r4, r5);
10430
10431 // Call the runtime; it returns -1 (less), 0 (equal), or 1 (greater)
10432 // tagged as a small integer.
10433 __ bind(&runtime);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010434 __ TailCallRuntime(Runtime::kStringCompare, 2, 1);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010435}
10436
10437
ager@chromium.org5c838252010-02-19 08:53:10 +000010438void StringAddStub::Generate(MacroAssembler* masm) {
10439 Label string_add_runtime;
10440 // Stack on entry:
10441 // sp[0]: second argument.
10442 // sp[4]: first argument.
10443
10444 // Load the two arguments.
10445 __ ldr(r0, MemOperand(sp, 1 * kPointerSize)); // First argument.
10446 __ ldr(r1, MemOperand(sp, 0 * kPointerSize)); // Second argument.
10447
10448 // Make sure that both arguments are strings if not known in advance.
10449 if (string_check_) {
10450 ASSERT_EQ(0, kSmiTag);
10451 __ JumpIfEitherSmi(r0, r1, &string_add_runtime);
10452 // Load instance types.
10453 __ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset));
10454 __ ldr(r5, FieldMemOperand(r1, HeapObject::kMapOffset));
10455 __ ldrb(r4, FieldMemOperand(r4, Map::kInstanceTypeOffset));
10456 __ ldrb(r5, FieldMemOperand(r5, Map::kInstanceTypeOffset));
10457 ASSERT_EQ(0, kStringTag);
10458 // If either is not a string, go to runtime.
10459 __ tst(r4, Operand(kIsNotStringMask));
10460 __ tst(r5, Operand(kIsNotStringMask), eq);
10461 __ b(ne, &string_add_runtime);
10462 }
10463
10464 // Both arguments are strings.
10465 // r0: first string
10466 // r1: second string
10467 // r4: first string instance type (if string_check_)
10468 // r5: second string instance type (if string_check_)
10469 {
10470 Label strings_not_empty;
10471 // Check if either of the strings are empty. In that case return the other.
10472 __ ldr(r2, FieldMemOperand(r0, String::kLengthOffset));
10473 __ ldr(r3, FieldMemOperand(r1, String::kLengthOffset));
ager@chromium.orgac091b72010-05-05 07:34:42 +000010474 ASSERT(kSmiTag == 0);
10475 __ cmp(r2, Operand(Smi::FromInt(0))); // Test if first string is empty.
ager@chromium.org5c838252010-02-19 08:53:10 +000010476 __ mov(r0, Operand(r1), LeaveCC, eq); // If first is empty, return second.
ager@chromium.orgac091b72010-05-05 07:34:42 +000010477 ASSERT(kSmiTag == 0);
10478 // Else test if second string is empty.
10479 __ cmp(r3, Operand(Smi::FromInt(0)), ne);
ager@chromium.org5c838252010-02-19 08:53:10 +000010480 __ b(ne, &strings_not_empty); // If either string was empty, return r0.
10481
10482 __ IncrementCounter(&Counters::string_add_native, 1, r2, r3);
10483 __ add(sp, sp, Operand(2 * kPointerSize));
10484 __ Ret();
10485
10486 __ bind(&strings_not_empty);
10487 }
10488
ager@chromium.orgac091b72010-05-05 07:34:42 +000010489 __ mov(r2, Operand(r2, ASR, kSmiTagSize));
10490 __ mov(r3, Operand(r3, ASR, kSmiTagSize));
ager@chromium.org5c838252010-02-19 08:53:10 +000010491 // Both strings are non-empty.
10492 // r0: first string
10493 // r1: second string
10494 // r2: length of first string
10495 // r3: length of second string
10496 // r4: first string instance type (if string_check_)
10497 // r5: second string instance type (if string_check_)
10498 // Look at the length of the result of adding the two strings.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010499 Label string_add_flat_result, longer_than_two;
ager@chromium.org5c838252010-02-19 08:53:10 +000010500 // Adding two lengths can't overflow.
10501 ASSERT(String::kMaxLength * 2 > String::kMaxLength);
10502 __ add(r6, r2, Operand(r3));
10503 // Use the runtime system when adding two one character strings, as it
10504 // contains optimizations for this specific case using the symbol table.
10505 __ cmp(r6, Operand(2));
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010506 __ b(ne, &longer_than_two);
10507
10508 // Check that both strings are non-external ascii strings.
10509 if (!string_check_) {
10510 __ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset));
10511 __ ldr(r5, FieldMemOperand(r1, HeapObject::kMapOffset));
10512 __ ldrb(r4, FieldMemOperand(r4, Map::kInstanceTypeOffset));
10513 __ ldrb(r5, FieldMemOperand(r5, Map::kInstanceTypeOffset));
10514 }
10515 __ JumpIfBothInstanceTypesAreNotSequentialAscii(r4, r5, r6, r7,
10516 &string_add_runtime);
10517
10518 // Get the two characters forming the sub string.
10519 __ ldrb(r2, FieldMemOperand(r0, SeqAsciiString::kHeaderSize));
10520 __ ldrb(r3, FieldMemOperand(r1, SeqAsciiString::kHeaderSize));
10521
10522 // Try to lookup two character string in symbol table. If it is not found
10523 // just allocate a new one.
10524 Label make_two_character_string;
ager@chromium.orgac091b72010-05-05 07:34:42 +000010525 StringHelper::GenerateTwoCharacterSymbolTableProbe(
10526 masm, r2, r3, r6, r7, r4, r5, r9, &make_two_character_string);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010527 __ IncrementCounter(&Counters::string_add_native, 1, r2, r3);
10528 __ add(sp, sp, Operand(2 * kPointerSize));
10529 __ Ret();
10530
10531 __ bind(&make_two_character_string);
10532 // Resulting string has length 2 and first chars of two strings
10533 // are combined into single halfword in r2 register.
10534 // So we can fill resulting string without two loops by a single
10535 // halfword store instruction (which assumes that processor is
10536 // in a little endian mode)
10537 __ mov(r6, Operand(2));
10538 __ AllocateAsciiString(r0, r6, r4, r5, r9, &string_add_runtime);
10539 __ strh(r2, FieldMemOperand(r0, SeqAsciiString::kHeaderSize));
10540 __ IncrementCounter(&Counters::string_add_native, 1, r2, r3);
10541 __ add(sp, sp, Operand(2 * kPointerSize));
10542 __ Ret();
10543
10544 __ bind(&longer_than_two);
ager@chromium.org5c838252010-02-19 08:53:10 +000010545 // Check if resulting string will be flat.
10546 __ cmp(r6, Operand(String::kMinNonFlatLength));
10547 __ b(lt, &string_add_flat_result);
10548 // Handle exceptionally long strings in the runtime system.
10549 ASSERT((String::kMaxLength & 0x80000000) == 0);
10550 ASSERT(IsPowerOf2(String::kMaxLength + 1));
10551 // kMaxLength + 1 is representable as shifted literal, kMaxLength is not.
10552 __ cmp(r6, Operand(String::kMaxLength + 1));
10553 __ b(hs, &string_add_runtime);
10554
10555 // If result is not supposed to be flat, allocate a cons string object.
10556 // If both strings are ascii the result is an ascii cons string.
10557 if (!string_check_) {
10558 __ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset));
10559 __ ldr(r5, FieldMemOperand(r1, HeapObject::kMapOffset));
10560 __ ldrb(r4, FieldMemOperand(r4, Map::kInstanceTypeOffset));
10561 __ ldrb(r5, FieldMemOperand(r5, Map::kInstanceTypeOffset));
10562 }
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +000010563 Label non_ascii, allocated, ascii_data;
ager@chromium.org5c838252010-02-19 08:53:10 +000010564 ASSERT_EQ(0, kTwoByteStringTag);
10565 __ tst(r4, Operand(kStringEncodingMask));
10566 __ tst(r5, Operand(kStringEncodingMask), ne);
10567 __ b(eq, &non_ascii);
10568
10569 // Allocate an ASCII cons string.
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +000010570 __ bind(&ascii_data);
ager@chromium.org5c838252010-02-19 08:53:10 +000010571 __ AllocateAsciiConsString(r7, r6, r4, r5, &string_add_runtime);
10572 __ bind(&allocated);
10573 // Fill the fields of the cons string.
10574 __ str(r0, FieldMemOperand(r7, ConsString::kFirstOffset));
10575 __ str(r1, FieldMemOperand(r7, ConsString::kSecondOffset));
10576 __ mov(r0, Operand(r7));
10577 __ IncrementCounter(&Counters::string_add_native, 1, r2, r3);
10578 __ add(sp, sp, Operand(2 * kPointerSize));
10579 __ Ret();
10580
10581 __ bind(&non_ascii);
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +000010582 // At least one of the strings is two-byte. Check whether it happens
10583 // to contain only ascii characters.
10584 // r4: first instance type.
10585 // r5: second instance type.
10586 __ tst(r4, Operand(kAsciiDataHintMask));
10587 __ tst(r5, Operand(kAsciiDataHintMask), ne);
10588 __ b(ne, &ascii_data);
10589 __ eor(r4, r4, Operand(r5));
10590 ASSERT(kAsciiStringTag != 0 && kAsciiDataHintTag != 0);
10591 __ and_(r4, r4, Operand(kAsciiStringTag | kAsciiDataHintTag));
10592 __ cmp(r4, Operand(kAsciiStringTag | kAsciiDataHintTag));
10593 __ b(eq, &ascii_data);
10594
ager@chromium.org5c838252010-02-19 08:53:10 +000010595 // Allocate a two byte cons string.
10596 __ AllocateTwoByteConsString(r7, r6, r4, r5, &string_add_runtime);
10597 __ jmp(&allocated);
10598
10599 // Handle creating a flat result. First check that both strings are
10600 // sequential and that they have the same encoding.
10601 // r0: first string
10602 // r1: second string
10603 // r2: length of first string
10604 // r3: length of second string
10605 // r4: first string instance type (if string_check_)
10606 // r5: second string instance type (if string_check_)
10607 // r6: sum of lengths.
10608 __ bind(&string_add_flat_result);
10609 if (!string_check_) {
10610 __ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset));
10611 __ ldr(r5, FieldMemOperand(r1, HeapObject::kMapOffset));
10612 __ ldrb(r4, FieldMemOperand(r4, Map::kInstanceTypeOffset));
10613 __ ldrb(r5, FieldMemOperand(r5, Map::kInstanceTypeOffset));
10614 }
10615 // Check that both strings are sequential.
10616 ASSERT_EQ(0, kSeqStringTag);
10617 __ tst(r4, Operand(kStringRepresentationMask));
10618 __ tst(r5, Operand(kStringRepresentationMask), eq);
10619 __ b(ne, &string_add_runtime);
10620 // Now check if both strings have the same encoding (ASCII/Two-byte).
10621 // r0: first string.
10622 // r1: second string.
10623 // r2: length of first string.
10624 // r3: length of second string.
10625 // r6: sum of lengths..
10626 Label non_ascii_string_add_flat_result;
10627 ASSERT(IsPowerOf2(kStringEncodingMask)); // Just one bit to test.
10628 __ eor(r7, r4, Operand(r5));
10629 __ tst(r7, Operand(kStringEncodingMask));
10630 __ b(ne, &string_add_runtime);
10631 // And see if it's ASCII or two-byte.
10632 __ tst(r4, Operand(kStringEncodingMask));
10633 __ b(eq, &non_ascii_string_add_flat_result);
10634
10635 // Both strings are sequential ASCII strings. We also know that they are
10636 // short (since the sum of the lengths is less than kMinNonFlatLength).
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010637 // r6: length of resulting flat string
ager@chromium.org5c838252010-02-19 08:53:10 +000010638 __ AllocateAsciiString(r7, r6, r4, r5, r9, &string_add_runtime);
10639 // Locate first character of result.
10640 __ add(r6, r7, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
10641 // Locate first character of first argument.
10642 __ add(r0, r0, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
10643 // r0: first character of first string.
10644 // r1: second string.
10645 // r2: length of first string.
10646 // r3: length of second string.
10647 // r6: first character of result.
10648 // r7: result string.
ager@chromium.orgac091b72010-05-05 07:34:42 +000010649 StringHelper::GenerateCopyCharacters(masm, r6, r0, r2, r4, true);
ager@chromium.org5c838252010-02-19 08:53:10 +000010650
10651 // Load second argument and locate first character.
10652 __ add(r1, r1, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
10653 // r1: first character of second string.
10654 // r3: length of second string.
10655 // r6: next character of result.
10656 // r7: result string.
ager@chromium.orgac091b72010-05-05 07:34:42 +000010657 StringHelper::GenerateCopyCharacters(masm, r6, r1, r3, r4, true);
ager@chromium.org5c838252010-02-19 08:53:10 +000010658 __ mov(r0, Operand(r7));
10659 __ IncrementCounter(&Counters::string_add_native, 1, r2, r3);
10660 __ add(sp, sp, Operand(2 * kPointerSize));
10661 __ Ret();
10662
10663 __ bind(&non_ascii_string_add_flat_result);
10664 // Both strings are sequential two byte strings.
10665 // r0: first string.
10666 // r1: second string.
10667 // r2: length of first string.
10668 // r3: length of second string.
10669 // r6: sum of length of strings.
10670 __ AllocateTwoByteString(r7, r6, r4, r5, r9, &string_add_runtime);
10671 // r0: first string.
10672 // r1: second string.
10673 // r2: length of first string.
10674 // r3: length of second string.
10675 // r7: result string.
10676
10677 // Locate first character of result.
10678 __ add(r6, r7, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
10679 // Locate first character of first argument.
10680 __ add(r0, r0, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
10681
10682 // r0: first character of first string.
10683 // r1: second string.
10684 // r2: length of first string.
10685 // r3: length of second string.
10686 // r6: first character of result.
10687 // r7: result string.
ager@chromium.orgac091b72010-05-05 07:34:42 +000010688 StringHelper::GenerateCopyCharacters(masm, r6, r0, r2, r4, false);
ager@chromium.org5c838252010-02-19 08:53:10 +000010689
10690 // Locate first character of second argument.
10691 __ add(r1, r1, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
10692
10693 // r1: first character of second string.
10694 // r3: length of second string.
10695 // r6: next character of result (after copy of first string).
10696 // r7: result string.
ager@chromium.orgac091b72010-05-05 07:34:42 +000010697 StringHelper::GenerateCopyCharacters(masm, r6, r1, r3, r4, false);
ager@chromium.org5c838252010-02-19 08:53:10 +000010698
10699 __ mov(r0, Operand(r7));
10700 __ IncrementCounter(&Counters::string_add_native, 1, r2, r3);
10701 __ add(sp, sp, Operand(2 * kPointerSize));
10702 __ Ret();
10703
10704 // Just jump to runtime to add the two strings.
10705 __ bind(&string_add_runtime);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010706 __ TailCallRuntime(Runtime::kStringAdd, 2, 1);
ager@chromium.org5c838252010-02-19 08:53:10 +000010707}
10708
10709
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000010710#undef __
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010711
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010712} } // namespace v8::internal
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +000010713
10714#endif // V8_TARGET_ARCH_ARM