blob: 6e0604bc6ebddbcd7e9751904730cb48ac5542db [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.
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +0000751 bool known_smi = frame_->KnownSmiAt(0);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +0000752 Register tos = frame_->PopToRegister();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000753
754 // Fast case checks
755
mads.s.ager31e71382008-08-13 09:32:07 +0000756 // Check if the value is 'false'.
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +0000757 if (!known_smi) {
758 __ LoadRoot(ip, Heap::kFalseValueRootIndex);
759 __ cmp(tos, ip);
760 false_target->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000761
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +0000762 // Check if the value is 'true'.
763 __ LoadRoot(ip, Heap::kTrueValueRootIndex);
764 __ cmp(tos, ip);
765 true_target->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000766
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +0000767 // Check if the value is 'undefined'.
768 __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
769 __ cmp(tos, ip);
770 false_target->Branch(eq);
771 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000772
mads.s.ager31e71382008-08-13 09:32:07 +0000773 // Check if the value is a smi.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +0000774 __ cmp(tos, Operand(Smi::FromInt(0)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000775
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +0000776 if (!known_smi) {
777 false_target->Branch(eq);
778 __ tst(tos, Operand(kSmiTagMask));
779 true_target->Branch(eq);
780
781 // Slow case: call the runtime.
782 frame_->EmitPush(tos);
783 frame_->CallRuntime(Runtime::kToBool, 1);
784 // Convert the result (r0) to a condition code.
785 __ LoadRoot(ip, Heap::kFalseValueRootIndex);
786 __ cmp(r0, ip);
787 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000788
789 cc_reg_ = ne;
790}
791
792
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000793void CodeGenerator::GenericBinaryOperation(Token::Value op,
kasperl@chromium.org86f77b72009-07-06 08:21:57 +0000794 OverwriteMode overwrite_mode,
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +0000795 GenerateInlineSmi inline_smi,
kasperl@chromium.org86f77b72009-07-06 08:21:57 +0000796 int constant_rhs) {
ager@chromium.org357bf652010-04-12 11:30:10 +0000797 // top of virtual frame: y
798 // 2nd elt. on virtual frame : x
799 // result : top of virtual frame
800
801 // Stub is entered with a call: 'return address' is in lr.
802 switch (op) {
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +0000803 case Token::ADD:
804 case Token::SUB:
805 if (inline_smi) {
806 JumpTarget done;
807 Register rhs = frame_->PopToRegister();
808 Register lhs = frame_->PopToRegister(rhs);
809 Register scratch = VirtualFrame::scratch0();
810 __ orr(scratch, rhs, Operand(lhs));
811 // Check they are both small and positive.
812 __ tst(scratch, Operand(kSmiTagMask | 0xc0000000));
813 ASSERT(rhs.is(r0) || lhs.is(r0)); // r0 is free now.
814 ASSERT_EQ(0, kSmiTag);
815 if (op == Token::ADD) {
816 __ add(r0, lhs, Operand(rhs), LeaveCC, eq);
817 } else {
818 __ sub(r0, lhs, Operand(rhs), LeaveCC, eq);
819 }
820 done.Branch(eq);
821 GenericBinaryOpStub stub(op, overwrite_mode, lhs, rhs, constant_rhs);
822 frame_->SpillAll();
823 frame_->CallStub(&stub, 0);
824 done.Bind();
825 frame_->EmitPush(r0);
826 break;
827 } else {
828 // Fall through!
829 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000830 case Token::BIT_OR:
831 case Token::BIT_AND:
832 case Token::BIT_XOR:
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +0000833 if (inline_smi) {
834 bool rhs_is_smi = frame_->KnownSmiAt(0);
835 bool lhs_is_smi = frame_->KnownSmiAt(1);
836 Register rhs = frame_->PopToRegister();
837 Register lhs = frame_->PopToRegister(rhs);
838 Register smi_test_reg;
839 Condition cond;
840 if (!rhs_is_smi || !lhs_is_smi) {
841 if (rhs_is_smi) {
842 smi_test_reg = lhs;
843 } else if (lhs_is_smi) {
844 smi_test_reg = rhs;
845 } else {
846 smi_test_reg = VirtualFrame::scratch0();
847 __ orr(smi_test_reg, rhs, Operand(lhs));
848 }
849 // Check they are both Smis.
850 __ tst(smi_test_reg, Operand(kSmiTagMask));
851 cond = eq;
852 } else {
853 cond = al;
854 }
855 ASSERT(rhs.is(r0) || lhs.is(r0)); // r0 is free now.
856 if (op == Token::BIT_OR) {
857 __ orr(r0, lhs, Operand(rhs), LeaveCC, cond);
858 } else if (op == Token::BIT_AND) {
859 __ and_(r0, lhs, Operand(rhs), LeaveCC, cond);
860 } else {
861 ASSERT(op == Token::BIT_XOR);
862 ASSERT_EQ(0, kSmiTag);
863 __ eor(r0, lhs, Operand(rhs), LeaveCC, cond);
864 }
865 if (cond != al) {
866 JumpTarget done;
867 done.Branch(cond);
868 GenericBinaryOpStub stub(op, overwrite_mode, lhs, rhs, constant_rhs);
869 frame_->SpillAll();
870 frame_->CallStub(&stub, 0);
871 done.Bind();
872 }
873 frame_->EmitPush(r0);
874 break;
875 } else {
876 // Fall through!
877 }
878 case Token::MUL:
879 case Token::DIV:
880 case Token::MOD:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000881 case Token::SHL:
882 case Token::SHR:
883 case Token::SAR: {
ager@chromium.org357bf652010-04-12 11:30:10 +0000884 Register rhs = frame_->PopToRegister();
885 Register lhs = frame_->PopToRegister(rhs); // Don't pop to rhs register.
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000886 GenericBinaryOpStub stub(op, overwrite_mode, lhs, rhs, constant_rhs);
887 frame_->SpillAll();
888 frame_->CallStub(&stub, 0);
ager@chromium.org357bf652010-04-12 11:30:10 +0000889 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000890 break;
891 }
892
ager@chromium.org357bf652010-04-12 11:30:10 +0000893 case Token::COMMA: {
894 Register scratch = frame_->PopToRegister();
895 // Simply discard left value.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000896 frame_->Drop();
ager@chromium.org357bf652010-04-12 11:30:10 +0000897 frame_->EmitPush(scratch);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000898 break;
ager@chromium.org357bf652010-04-12 11:30:10 +0000899 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000900
901 default:
902 // Other cases should have been handled before this point.
903 UNREACHABLE();
904 break;
905 }
906}
907
908
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000909class DeferredInlineSmiOperation: public DeferredCode {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000910 public:
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +0000911 DeferredInlineSmiOperation(Token::Value op,
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000912 int value,
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000913 bool reversed,
ager@chromium.org357bf652010-04-12 11:30:10 +0000914 OverwriteMode overwrite_mode,
915 Register tos)
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +0000916 : op_(op),
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000917 value_(value),
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000918 reversed_(reversed),
ager@chromium.org357bf652010-04-12 11:30:10 +0000919 overwrite_mode_(overwrite_mode),
920 tos_register_(tos) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000921 set_comment("[ DeferredInlinedSmiOperation");
922 }
923
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000924 virtual void Generate();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000925
926 private:
927 Token::Value op_;
928 int value_;
929 bool reversed_;
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000930 OverwriteMode overwrite_mode_;
ager@chromium.org357bf652010-04-12 11:30:10 +0000931 Register tos_register_;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000932};
933
934
fschneider@chromium.org40b9da32010-06-28 11:29:21 +0000935
936// On entry the non-constant side of the binary operation is in tos_register_
937// and the constant smi side is nowhere. The tos_register_ is not used by the
938// virtual frame. On exit the answer is in the tos_register_ and the virtual
939// frame is unchanged.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000940void DeferredInlineSmiOperation::Generate() {
fschneider@chromium.org40b9da32010-06-28 11:29:21 +0000941 VirtualFrame copied_frame(*frame_state()->frame());
942 copied_frame.SpillAll();
943
ager@chromium.org357bf652010-04-12 11:30:10 +0000944 Register lhs = r1;
945 Register rhs = r0;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000946 switch (op_) {
947 case Token::ADD: {
ager@chromium.orge2902be2009-06-08 12:21:35 +0000948 // Revert optimistic add.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000949 if (reversed_) {
ager@chromium.org357bf652010-04-12 11:30:10 +0000950 __ sub(r0, tos_register_, Operand(Smi::FromInt(value_)));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000951 __ mov(r1, Operand(Smi::FromInt(value_)));
952 } else {
ager@chromium.org357bf652010-04-12 11:30:10 +0000953 __ sub(r1, tos_register_, Operand(Smi::FromInt(value_)));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000954 __ mov(r0, Operand(Smi::FromInt(value_)));
955 }
956 break;
957 }
958
959 case Token::SUB: {
ager@chromium.orge2902be2009-06-08 12:21:35 +0000960 // Revert optimistic sub.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000961 if (reversed_) {
ager@chromium.org357bf652010-04-12 11:30:10 +0000962 __ rsb(r0, tos_register_, Operand(Smi::FromInt(value_)));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000963 __ mov(r1, Operand(Smi::FromInt(value_)));
964 } else {
ager@chromium.org357bf652010-04-12 11:30:10 +0000965 __ add(r1, tos_register_, Operand(Smi::FromInt(value_)));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000966 __ mov(r0, Operand(Smi::FromInt(value_)));
967 }
968 break;
969 }
970
kasperl@chromium.org86f77b72009-07-06 08:21:57 +0000971 // For these operations there is no optimistic operation that needs to be
972 // reverted.
973 case Token::MUL:
974 case Token::MOD:
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000975 case Token::BIT_OR:
976 case Token::BIT_XOR:
fschneider@chromium.org40b9da32010-06-28 11:29:21 +0000977 case Token::BIT_AND:
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000978 case Token::SHL:
979 case Token::SHR:
980 case Token::SAR: {
fschneider@chromium.org40b9da32010-06-28 11:29:21 +0000981 if (tos_register_.is(r1)) {
982 __ mov(r0, Operand(Smi::FromInt(value_)));
983 } else {
984 ASSERT(tos_register_.is(r0));
985 __ mov(r1, Operand(Smi::FromInt(value_)));
986 }
987 if (reversed_ == tos_register_.is(r1)) {
ager@chromium.org357bf652010-04-12 11:30:10 +0000988 lhs = r0;
989 rhs = r1;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000990 }
991 break;
992 }
993
994 default:
ager@chromium.orge2902be2009-06-08 12:21:35 +0000995 // Other cases should have been handled before this point.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000996 UNREACHABLE();
997 break;
998 }
999
ager@chromium.org357bf652010-04-12 11:30:10 +00001000 GenericBinaryOpStub stub(op_, overwrite_mode_, lhs, rhs, value_);
ager@chromium.orge2902be2009-06-08 12:21:35 +00001001 __ CallStub(&stub);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00001002
ager@chromium.org357bf652010-04-12 11:30:10 +00001003 // The generic stub returns its value in r0, but that's not
1004 // necessarily what we want. We want whatever the inlined code
1005 // expected, which is that the answer is in the same register as
1006 // the operand was.
1007 __ Move(tos_register_, r0);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00001008
1009 // The tos register was not in use for the virtual frame that we
1010 // came into this function with, so we can merge back to that frame
1011 // without trashing it.
1012 copied_frame.MergeTo(frame_state()->frame());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001013}
1014
1015
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00001016static bool PopCountLessThanEqual2(unsigned int x) {
1017 x &= x - 1;
1018 return (x & (x - 1)) == 0;
1019}
1020
1021
1022// Returns the index of the lowest bit set.
1023static int BitPosition(unsigned x) {
1024 int bit_posn = 0;
1025 while ((x & 0xf) == 0) {
1026 bit_posn += 4;
1027 x >>= 4;
1028 }
1029 while ((x & 1) == 0) {
1030 bit_posn++;
1031 x >>= 1;
1032 }
1033 return bit_posn;
1034}
1035
1036
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00001037void CodeGenerator::SmiOperation(Token::Value op,
1038 Handle<Object> value,
1039 bool reversed,
1040 OverwriteMode mode) {
ager@chromium.org357bf652010-04-12 11:30:10 +00001041 int int_value = Smi::cast(*value)->value();
1042
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001043 bool both_sides_are_smi = frame_->KnownSmiAt(0);
1044
ager@chromium.org357bf652010-04-12 11:30:10 +00001045 bool something_to_inline;
1046 switch (op) {
1047 case Token::ADD:
1048 case Token::SUB:
1049 case Token::BIT_AND:
1050 case Token::BIT_OR:
1051 case Token::BIT_XOR: {
1052 something_to_inline = true;
1053 break;
1054 }
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001055 case Token::SHL: {
1056 something_to_inline = (both_sides_are_smi || !reversed);
1057 break;
1058 }
ager@chromium.org357bf652010-04-12 11:30:10 +00001059 case Token::SHR:
1060 case Token::SAR: {
1061 if (reversed) {
1062 something_to_inline = false;
1063 } else {
1064 something_to_inline = true;
1065 }
1066 break;
1067 }
1068 case Token::MOD: {
1069 if (reversed || int_value < 2 || !IsPowerOf2(int_value)) {
1070 something_to_inline = false;
1071 } else {
1072 something_to_inline = true;
1073 }
1074 break;
1075 }
1076 case Token::MUL: {
1077 if (!IsEasyToMultiplyBy(int_value)) {
1078 something_to_inline = false;
1079 } else {
1080 something_to_inline = true;
1081 }
1082 break;
1083 }
1084 default: {
1085 something_to_inline = false;
1086 break;
1087 }
1088 }
1089
1090 if (!something_to_inline) {
1091 if (!reversed) {
1092 // Push the rhs onto the virtual frame by putting it in a TOS register.
1093 Register rhs = frame_->GetTOSRegister();
1094 __ mov(rhs, Operand(value));
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001095 frame_->EmitPush(rhs, TypeInfo::Smi());
1096 GenericBinaryOperation(op, mode, GENERATE_INLINE_SMI, int_value);
ager@chromium.org357bf652010-04-12 11:30:10 +00001097 } else {
1098 // Pop the rhs, then push lhs and rhs in the right order. Only performs
1099 // at most one pop, the rest takes place in TOS registers.
1100 Register lhs = frame_->GetTOSRegister(); // Get reg for pushing.
1101 Register rhs = frame_->PopToRegister(lhs); // Don't use lhs for this.
1102 __ mov(lhs, Operand(value));
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001103 frame_->EmitPush(lhs, TypeInfo::Smi());
1104 TypeInfo t = both_sides_are_smi ? TypeInfo::Smi() : TypeInfo::Unknown();
1105 frame_->EmitPush(rhs, t);
1106 GenericBinaryOperation(op, mode, GENERATE_INLINE_SMI, kUnknownIntValue);
ager@chromium.org357bf652010-04-12 11:30:10 +00001107 }
1108 return;
1109 }
1110
1111 // We move the top of stack to a register (normally no move is invoved).
1112 Register tos = frame_->PopToRegister();
ager@chromium.org357bf652010-04-12 11:30:10 +00001113 switch (op) {
1114 case Token::ADD: {
1115 DeferredCode* deferred =
1116 new DeferredInlineSmiOperation(op, int_value, reversed, mode, tos);
1117
1118 __ add(tos, tos, Operand(value), SetCC);
1119 deferred->Branch(vs);
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001120 if (!both_sides_are_smi) {
1121 __ tst(tos, Operand(kSmiTagMask));
1122 deferred->Branch(ne);
1123 }
ager@chromium.org357bf652010-04-12 11:30:10 +00001124 deferred->BindExit();
1125 frame_->EmitPush(tos);
1126 break;
1127 }
1128
1129 case Token::SUB: {
1130 DeferredCode* deferred =
1131 new DeferredInlineSmiOperation(op, int_value, reversed, mode, tos);
1132
1133 if (reversed) {
1134 __ rsb(tos, tos, Operand(value), SetCC);
1135 } else {
1136 __ sub(tos, tos, Operand(value), SetCC);
1137 }
1138 deferred->Branch(vs);
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001139 if (!both_sides_are_smi) {
1140 __ tst(tos, Operand(kSmiTagMask));
1141 deferred->Branch(ne);
1142 }
ager@chromium.org357bf652010-04-12 11:30:10 +00001143 deferred->BindExit();
1144 frame_->EmitPush(tos);
1145 break;
1146 }
1147
1148
1149 case Token::BIT_OR:
1150 case Token::BIT_XOR:
1151 case Token::BIT_AND: {
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001152 if (both_sides_are_smi) {
1153 switch (op) {
1154 case Token::BIT_OR: __ orr(tos, tos, Operand(value)); break;
1155 case Token::BIT_XOR: __ eor(tos, tos, Operand(value)); break;
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00001156 case Token::BIT_AND: __ And(tos, tos, Operand(value)); break;
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001157 default: UNREACHABLE();
1158 }
1159 frame_->EmitPush(tos, TypeInfo::Smi());
1160 } else {
1161 DeferredCode* deferred =
1162 new DeferredInlineSmiOperation(op, int_value, reversed, mode, tos);
1163 __ tst(tos, Operand(kSmiTagMask));
1164 deferred->Branch(ne);
1165 switch (op) {
1166 case Token::BIT_OR: __ orr(tos, tos, Operand(value)); break;
1167 case Token::BIT_XOR: __ eor(tos, tos, Operand(value)); break;
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00001168 case Token::BIT_AND: __ And(tos, tos, Operand(value)); break;
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001169 default: UNREACHABLE();
1170 }
1171 deferred->BindExit();
1172 TypeInfo result_type =
1173 (op == Token::BIT_AND) ? TypeInfo::Smi() : TypeInfo::Integer32();
1174 frame_->EmitPush(tos, result_type);
ager@chromium.org357bf652010-04-12 11:30:10 +00001175 }
ager@chromium.org357bf652010-04-12 11:30:10 +00001176 break;
1177 }
1178
1179 case Token::SHL:
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001180 if (reversed) {
1181 ASSERT(both_sides_are_smi);
1182 int max_shift = 0;
1183 int max_result = int_value == 0 ? 1 : int_value;
1184 while (Smi::IsValid(max_result << 1)) {
1185 max_shift++;
1186 max_result <<= 1;
1187 }
1188 DeferredCode* deferred =
1189 new DeferredInlineSmiOperation(op, int_value, true, mode, tos);
1190 // Mask off the last 5 bits of the shift operand (rhs). This is part
1191 // of the definition of shift in JS and we know we have a Smi so we
1192 // can safely do this. The masked version gets passed to the
1193 // deferred code, but that makes no difference.
1194 __ and_(tos, tos, Operand(Smi::FromInt(0x1f)));
1195 __ cmp(tos, Operand(Smi::FromInt(max_shift)));
1196 deferred->Branch(ge);
1197 Register scratch = VirtualFrame::scratch0();
1198 __ mov(scratch, Operand(tos, ASR, kSmiTagSize)); // Untag.
1199 __ mov(tos, Operand(Smi::FromInt(int_value))); // Load constant.
1200 __ mov(tos, Operand(tos, LSL, scratch)); // Shift constant.
1201 deferred->BindExit();
1202 TypeInfo result = TypeInfo::Integer32();
1203 frame_->EmitPush(tos, result);
1204 break;
1205 }
1206 // Fall through!
ager@chromium.org357bf652010-04-12 11:30:10 +00001207 case Token::SHR:
1208 case Token::SAR: {
1209 ASSERT(!reversed);
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001210 TypeInfo result = TypeInfo::Integer32();
ager@chromium.org357bf652010-04-12 11:30:10 +00001211 Register scratch = VirtualFrame::scratch0();
1212 Register scratch2 = VirtualFrame::scratch1();
1213 int shift_value = int_value & 0x1f; // least significant 5 bits
1214 DeferredCode* deferred =
1215 new DeferredInlineSmiOperation(op, shift_value, false, mode, tos);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00001216 uint32_t problematic_mask = kSmiTagMask;
1217 // For unsigned shift by zero all negative smis are problematic.
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001218 bool skip_smi_test = both_sides_are_smi;
1219 if (shift_value == 0 && op == Token::SHR) {
1220 problematic_mask |= 0x80000000;
1221 skip_smi_test = false;
1222 }
1223 if (!skip_smi_test) {
1224 __ tst(tos, Operand(problematic_mask));
1225 deferred->Branch(ne); // Go slow for problematic input.
1226 }
ager@chromium.org357bf652010-04-12 11:30:10 +00001227 switch (op) {
1228 case Token::SHL: {
1229 if (shift_value != 0) {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00001230 int adjusted_shift = shift_value - kSmiTagSize;
1231 ASSERT(adjusted_shift >= 0);
1232 if (adjusted_shift != 0) {
1233 __ mov(scratch, Operand(tos, LSL, adjusted_shift));
1234 // Check that the *signed* result fits in a smi.
1235 __ add(scratch2, scratch, Operand(0x40000000), SetCC);
1236 deferred->Branch(mi);
1237 __ mov(tos, Operand(scratch, LSL, kSmiTagSize));
1238 } else {
1239 // Check that the *signed* result fits in a smi.
1240 __ add(scratch2, tos, Operand(0x40000000), SetCC);
1241 deferred->Branch(mi);
1242 __ mov(tos, Operand(tos, LSL, kSmiTagSize));
1243 }
ager@chromium.org357bf652010-04-12 11:30:10 +00001244 }
ager@chromium.org357bf652010-04-12 11:30:10 +00001245 break;
1246 }
1247 case Token::SHR: {
ager@chromium.org357bf652010-04-12 11:30:10 +00001248 if (shift_value != 0) {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00001249 __ mov(scratch, Operand(tos, ASR, kSmiTagSize)); // Remove tag.
1250 // LSR by immediate 0 means shifting 32 bits.
ager@chromium.org357bf652010-04-12 11:30:10 +00001251 __ mov(scratch, Operand(scratch, LSR, shift_value));
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00001252 if (shift_value == 1) {
1253 // check that the *unsigned* result fits in a smi
1254 // neither of the two high-order bits can be set:
1255 // - 0x80000000: high bit would be lost when smi tagging
1256 // - 0x40000000: this number would convert to negative when
1257 // smi tagging these two cases can only happen with shifts
1258 // by 0 or 1 when handed a valid smi
1259 __ tst(scratch, Operand(0xc0000000));
1260 deferred->Branch(ne);
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001261 } else {
1262 ASSERT(shift_value >= 2);
1263 result = TypeInfo::Smi(); // SHR by at least 2 gives a Smi.
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00001264 }
1265 __ mov(tos, Operand(scratch, LSL, kSmiTagSize));
ager@chromium.org357bf652010-04-12 11:30:10 +00001266 }
ager@chromium.org357bf652010-04-12 11:30:10 +00001267 break;
1268 }
1269 case Token::SAR: {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00001270 // In the ARM instructions set, ASR by immediate 0 means shifting 32
1271 // bits.
ager@chromium.org357bf652010-04-12 11:30:10 +00001272 if (shift_value != 0) {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00001273 // Do the shift and the tag removal in one operation. If the shift
1274 // is 31 bits (the highest possible value) then we emit the
1275 // instruction as a shift by 0 which means shift arithmetically by
1276 // 32.
1277 __ mov(tos, Operand(tos, ASR, (kSmiTagSize + shift_value) & 0x1f));
1278 // Put tag back.
1279 __ mov(tos, Operand(tos, LSL, kSmiTagSize));
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001280 // SAR by at least 1 gives a Smi.
1281 result = TypeInfo::Smi();
ager@chromium.org357bf652010-04-12 11:30:10 +00001282 }
1283 break;
1284 }
1285 default: UNREACHABLE();
1286 }
ager@chromium.org357bf652010-04-12 11:30:10 +00001287 deferred->BindExit();
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001288 frame_->EmitPush(tos, result);
ager@chromium.org357bf652010-04-12 11:30:10 +00001289 break;
1290 }
1291
1292 case Token::MOD: {
1293 ASSERT(!reversed);
1294 ASSERT(int_value >= 2);
1295 ASSERT(IsPowerOf2(int_value));
1296 DeferredCode* deferred =
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001297 new DeferredInlineSmiOperation(op, int_value, reversed, mode, tos);
ager@chromium.org357bf652010-04-12 11:30:10 +00001298 unsigned mask = (0x80000000u | kSmiTagMask);
1299 __ tst(tos, Operand(mask));
1300 deferred->Branch(ne); // Go to deferred code on non-Smis and negative.
1301 mask = (int_value << kSmiTagSize) - 1;
1302 __ and_(tos, tos, Operand(mask));
1303 deferred->BindExit();
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001304 // Mod of positive power of 2 Smi gives a Smi if the lhs is an integer.
1305 frame_->EmitPush(
1306 tos,
1307 both_sides_are_smi ? TypeInfo::Smi() : TypeInfo::Number());
ager@chromium.org357bf652010-04-12 11:30:10 +00001308 break;
1309 }
1310
1311 case Token::MUL: {
1312 ASSERT(IsEasyToMultiplyBy(int_value));
1313 DeferredCode* deferred =
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001314 new DeferredInlineSmiOperation(op, int_value, reversed, mode, tos);
ager@chromium.org357bf652010-04-12 11:30:10 +00001315 unsigned max_smi_that_wont_overflow = Smi::kMaxValue / int_value;
1316 max_smi_that_wont_overflow <<= kSmiTagSize;
1317 unsigned mask = 0x80000000u;
1318 while ((mask & max_smi_that_wont_overflow) == 0) {
1319 mask |= mask >> 1;
1320 }
1321 mask |= kSmiTagMask;
1322 // This does a single mask that checks for a too high value in a
1323 // conservative way and for a non-Smi. It also filters out negative
1324 // numbers, unfortunately, but since this code is inline we prefer
1325 // brevity to comprehensiveness.
1326 __ tst(tos, Operand(mask));
1327 deferred->Branch(ne);
1328 MultiplyByKnownInt(masm_, tos, tos, int_value);
1329 deferred->BindExit();
1330 frame_->EmitPush(tos);
1331 break;
1332 }
1333
1334 default:
1335 UNREACHABLE();
1336 break;
1337 }
1338}
1339
1340
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001341void CodeGenerator::Comparison(Condition cc,
1342 Expression* left,
1343 Expression* right,
1344 bool strict) {
ager@chromium.org357bf652010-04-12 11:30:10 +00001345 VirtualFrame::RegisterAllocationScope scope(this);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001346
ager@chromium.org357bf652010-04-12 11:30:10 +00001347 if (left != NULL) Load(left);
1348 if (right != NULL) Load(right);
1349
mads.s.ager31e71382008-08-13 09:32:07 +00001350 // sp[0] : y
1351 // sp[1] : x
1352 // result : cc register
1353
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001354 // Strict only makes sense for equality comparisons.
1355 ASSERT(!strict || cc == eq);
1356
ager@chromium.org357bf652010-04-12 11:30:10 +00001357 Register lhs;
1358 Register rhs;
1359
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001360 bool lhs_is_smi;
1361 bool rhs_is_smi;
1362
ager@chromium.org357bf652010-04-12 11:30:10 +00001363 // We load the top two stack positions into registers chosen by the virtual
1364 // frame. This should keep the register shuffling to a minimum.
mads.s.ager@gmail.com769cc962008-08-06 10:02:49 +00001365 // Implement '>' and '<=' by reversal to obtain ECMA-262 conversion order.
1366 if (cc == gt || cc == le) {
1367 cc = ReverseCondition(cc);
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001368 lhs_is_smi = frame_->KnownSmiAt(0);
1369 rhs_is_smi = frame_->KnownSmiAt(1);
ager@chromium.org357bf652010-04-12 11:30:10 +00001370 lhs = frame_->PopToRegister();
1371 rhs = frame_->PopToRegister(lhs); // Don't pop to the same register again!
mads.s.ager@gmail.com769cc962008-08-06 10:02:49 +00001372 } else {
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001373 rhs_is_smi = frame_->KnownSmiAt(0);
1374 lhs_is_smi = frame_->KnownSmiAt(1);
ager@chromium.org357bf652010-04-12 11:30:10 +00001375 rhs = frame_->PopToRegister();
1376 lhs = frame_->PopToRegister(rhs); // Don't pop to the same register again!
mads.s.ager@gmail.com769cc962008-08-06 10:02:49 +00001377 }
ager@chromium.org357bf652010-04-12 11:30:10 +00001378
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001379 bool both_sides_are_smi = (lhs_is_smi && rhs_is_smi);
1380
ager@chromium.org357bf652010-04-12 11:30:10 +00001381 ASSERT(rhs.is(r0) || rhs.is(r1));
1382 ASSERT(lhs.is(r0) || lhs.is(r1));
1383
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001384 JumpTarget exit;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001385
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001386 if (!both_sides_are_smi) {
1387 // Now we have the two sides in r0 and r1. We flush any other registers
1388 // because the stub doesn't know about register allocation.
1389 frame_->SpillAll();
1390 Register scratch = VirtualFrame::scratch0();
1391 Register smi_test_reg;
1392 if (lhs_is_smi) {
1393 smi_test_reg = rhs;
1394 } else if (rhs_is_smi) {
1395 smi_test_reg = lhs;
1396 } else {
1397 __ orr(scratch, lhs, Operand(rhs));
1398 smi_test_reg = scratch;
1399 }
1400 __ tst(smi_test_reg, Operand(kSmiTagMask));
1401 JumpTarget smi;
1402 smi.Branch(eq);
1403
1404 // Perform non-smi comparison by stub.
1405 // CompareStub takes arguments in r0 and r1, returns <0, >0 or 0 in r0.
1406 // We call with 0 args because there are 0 on the stack.
1407 if (!rhs.is(r0)) {
1408 __ Swap(rhs, lhs, ip);
1409 }
1410
1411 CompareStub stub(cc, strict);
1412 frame_->CallStub(&stub, 0);
1413 __ cmp(r0, Operand(0));
1414 exit.Jump();
1415
1416 smi.Bind();
ager@chromium.org357bf652010-04-12 11:30:10 +00001417 }
1418
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001419 // Do smi comparisons by pointer comparison.
ager@chromium.org357bf652010-04-12 11:30:10 +00001420 __ cmp(lhs, Operand(rhs));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001421
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001422 exit.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001423 cc_reg_ = cc;
1424}
1425
1426
mads.s.ager31e71382008-08-13 09:32:07 +00001427// Call the function on the stack with the given arguments.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001428void CodeGenerator::CallWithArguments(ZoneList<Expression*>* args,
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001429 CallFunctionFlags flags,
1430 int position) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001431 // Push the arguments ("left-to-right") on the stack.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001432 int arg_count = args->length();
1433 for (int i = 0; i < arg_count; i++) {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001434 Load(args->at(i));
mads.s.ager31e71382008-08-13 09:32:07 +00001435 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001436
kasper.lund7276f142008-07-30 08:49:36 +00001437 // Record the position for debugging purposes.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001438 CodeForSourcePosition(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001439
kasper.lund7276f142008-07-30 08:49:36 +00001440 // Use the shared code stub to call the function.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001441 InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001442 CallFunctionStub call_function(arg_count, in_loop, flags);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001443 frame_->CallStub(&call_function, arg_count + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001444
1445 // Restore context and pop function from the stack.
ager@chromium.org3bf7b912008-11-17 09:09:45 +00001446 __ ldr(cp, frame_->Context());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001447 frame_->Drop(); // discard the TOS
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001448}
1449
1450
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00001451void CodeGenerator::CallApplyLazy(Expression* applicand,
1452 Expression* receiver,
1453 VariableProxy* arguments,
1454 int position) {
1455 // An optimized implementation of expressions of the form
1456 // x.apply(y, arguments).
1457 // If the arguments object of the scope has not been allocated,
1458 // and x.apply is Function.prototype.apply, this optimization
1459 // just copies y and the arguments of the current function on the
1460 // stack, as receiver and arguments, and calls x.
1461 // In the implementation comments, we call x the applicand
1462 // and y the receiver.
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00001463
1464 ASSERT(ArgumentsMode() == LAZY_ARGUMENTS_ALLOCATION);
1465 ASSERT(arguments->IsArguments());
1466
1467 // Load applicand.apply onto the stack. This will usually
1468 // give us a megamorphic load site. Not super, but it works.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001469 Load(applicand);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00001470 Handle<String> name = Factory::LookupAsciiSymbol("apply");
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001471 frame_->Dup();
ager@chromium.orgac091b72010-05-05 07:34:42 +00001472 frame_->CallLoadIC(name, RelocInfo::CODE_TARGET);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00001473 frame_->EmitPush(r0);
1474
1475 // Load the receiver and the existing arguments object onto the
1476 // expression stack. Avoid allocating the arguments object here.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001477 Load(receiver);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00001478 LoadFromSlot(scope()->arguments()->var()->slot(), NOT_INSIDE_TYPEOF);
1479
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00001480 // At this point the top two stack elements are probably in registers
1481 // since they were just loaded. Ensure they are in regs and get the
1482 // regs.
1483 Register receiver_reg = frame_->Peek2();
1484 Register arguments_reg = frame_->Peek();
1485
1486 // From now on the frame is spilled.
1487 frame_->SpillAll();
1488
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00001489 // Emit the source position information after having loaded the
1490 // receiver and the arguments.
1491 CodeForSourcePosition(position);
1492 // Contents of the stack at this point:
1493 // sp[0]: arguments object of the current function or the hole.
1494 // sp[1]: receiver
1495 // sp[2]: applicand.apply
1496 // sp[3]: applicand.
1497
1498 // Check if the arguments object has been lazily allocated
1499 // already. If so, just use that instead of copying the arguments
1500 // from the stack. This also deals with cases where a local variable
1501 // named 'arguments' has been introduced.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00001502 JumpTarget slow;
1503 Label done;
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00001504 __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00001505 __ cmp(ip, arguments_reg);
1506 slow.Branch(ne);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00001507
1508 Label build_args;
1509 // Get rid of the arguments object probe.
1510 frame_->Drop();
1511 // Stack now has 3 elements on it.
1512 // Contents of stack at this point:
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00001513 // sp[0]: receiver - in the receiver_reg register.
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00001514 // sp[1]: applicand.apply
1515 // sp[2]: applicand.
1516
1517 // Check that the receiver really is a JavaScript object.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00001518 __ BranchOnSmi(receiver_reg, &build_args);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00001519 // We allow all JSObjects including JSFunctions. As long as
1520 // JS_FUNCTION_TYPE is the last instance type and it is right
1521 // after LAST_JS_OBJECT_TYPE, we do not have to check the upper
1522 // bound.
1523 ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
1524 ASSERT(JS_FUNCTION_TYPE == LAST_JS_OBJECT_TYPE + 1);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00001525 __ CompareObjectType(receiver_reg, r2, r3, FIRST_JS_OBJECT_TYPE);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00001526 __ b(lt, &build_args);
1527
1528 // Check that applicand.apply is Function.prototype.apply.
1529 __ ldr(r0, MemOperand(sp, kPointerSize));
1530 __ BranchOnSmi(r0, &build_args);
1531 __ CompareObjectType(r0, r1, r2, JS_FUNCTION_TYPE);
1532 __ b(ne, &build_args);
1533 __ ldr(r0, FieldMemOperand(r0, JSFunction::kSharedFunctionInfoOffset));
1534 Handle<Code> apply_code(Builtins::builtin(Builtins::FunctionApply));
1535 __ ldr(r1, FieldMemOperand(r0, SharedFunctionInfo::kCodeOffset));
1536 __ cmp(r1, Operand(apply_code));
1537 __ b(ne, &build_args);
1538
1539 // Check that applicand is a function.
1540 __ ldr(r1, MemOperand(sp, 2 * kPointerSize));
1541 __ BranchOnSmi(r1, &build_args);
1542 __ CompareObjectType(r1, r2, r3, JS_FUNCTION_TYPE);
1543 __ b(ne, &build_args);
1544
1545 // Copy the arguments to this function possibly from the
1546 // adaptor frame below it.
1547 Label invoke, adapted;
1548 __ ldr(r2, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
1549 __ ldr(r3, MemOperand(r2, StandardFrameConstants::kContextOffset));
1550 __ cmp(r3, Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
1551 __ b(eq, &adapted);
1552
1553 // No arguments adaptor frame. Copy fixed number of arguments.
1554 __ mov(r0, Operand(scope()->num_parameters()));
1555 for (int i = 0; i < scope()->num_parameters(); i++) {
1556 __ ldr(r2, frame_->ParameterAt(i));
1557 __ push(r2);
1558 }
1559 __ jmp(&invoke);
1560
1561 // Arguments adaptor frame present. Copy arguments from there, but
1562 // avoid copying too many arguments to avoid stack overflows.
1563 __ bind(&adapted);
1564 static const uint32_t kArgumentsLimit = 1 * KB;
1565 __ ldr(r0, MemOperand(r2, ArgumentsAdaptorFrameConstants::kLengthOffset));
1566 __ mov(r0, Operand(r0, LSR, kSmiTagSize));
1567 __ mov(r3, r0);
1568 __ cmp(r0, Operand(kArgumentsLimit));
1569 __ b(gt, &build_args);
1570
1571 // Loop through the arguments pushing them onto the execution
1572 // stack. We don't inform the virtual frame of the push, so we don't
1573 // have to worry about getting rid of the elements from the virtual
1574 // frame.
1575 Label loop;
1576 // r3 is a small non-negative integer, due to the test above.
1577 __ cmp(r3, Operand(0));
1578 __ b(eq, &invoke);
1579 // Compute the address of the first argument.
1580 __ add(r2, r2, Operand(r3, LSL, kPointerSizeLog2));
1581 __ add(r2, r2, Operand(kPointerSize));
1582 __ bind(&loop);
1583 // Post-decrement argument address by kPointerSize on each iteration.
1584 __ ldr(r4, MemOperand(r2, kPointerSize, NegPostIndex));
1585 __ push(r4);
1586 __ sub(r3, r3, Operand(1), SetCC);
1587 __ b(gt, &loop);
1588
1589 // Invoke the function.
1590 __ bind(&invoke);
1591 ParameterCount actual(r0);
1592 __ InvokeFunction(r1, actual, CALL_FUNCTION);
1593 // Drop applicand.apply and applicand from the stack, and push
1594 // the result of the function call, but leave the spilled frame
1595 // unchanged, with 3 elements, so it is correct when we compile the
1596 // slow-case code.
1597 __ add(sp, sp, Operand(2 * kPointerSize));
1598 __ push(r0);
1599 // Stack now has 1 element:
1600 // sp[0]: result
1601 __ jmp(&done);
1602
1603 // Slow-case: Allocate the arguments object since we know it isn't
1604 // there, and fall-through to the slow-case where we call
1605 // applicand.apply.
1606 __ bind(&build_args);
1607 // Stack now has 3 elements, because we have jumped from where:
1608 // sp[0]: receiver
1609 // sp[1]: applicand.apply
1610 // sp[2]: applicand.
1611 StoreArgumentsObject(false);
1612
1613 // Stack and frame now have 4 elements.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00001614 slow.Bind();
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00001615
1616 // Generic computation of x.apply(y, args) with no special optimization.
1617 // Flip applicand.apply and applicand on the stack, so
1618 // applicand looks like the receiver of the applicand.apply call.
1619 // Then process it as a normal function call.
1620 __ ldr(r0, MemOperand(sp, 3 * kPointerSize));
1621 __ ldr(r1, MemOperand(sp, 2 * kPointerSize));
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00001622 __ Strd(r0, r1, MemOperand(sp, 2 * kPointerSize));
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00001623
1624 CallFunctionStub call_function(2, NOT_IN_LOOP, NO_CALL_FUNCTION_FLAGS);
1625 frame_->CallStub(&call_function, 3);
1626 // The function and its two arguments have been dropped.
1627 frame_->Drop(); // Drop the receiver as well.
1628 frame_->EmitPush(r0);
1629 // Stack now has 1 element:
1630 // sp[0]: result
1631 __ bind(&done);
1632
1633 // Restore the context register after a call.
1634 __ ldr(cp, frame_->Context());
1635}
1636
1637
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001638void CodeGenerator::Branch(bool if_true, JumpTarget* target) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001639 ASSERT(has_cc());
1640 Condition cc = if_true ? cc_reg_ : NegateCondition(cc_reg_);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001641 target->Branch(cc);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001642 cc_reg_ = al;
1643}
1644
1645
ager@chromium.org7c537e22008-10-16 08:43:32 +00001646void CodeGenerator::CheckStack() {
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00001647 frame_->SpillAll();
ager@chromium.org3811b432009-10-28 14:53:37 +00001648 Comment cmnt(masm_, "[ check stack");
1649 __ LoadRoot(ip, Heap::kStackLimitRootIndex);
1650 // Put the lr setup instruction in the delay slot. kInstrSize is added to
1651 // the implicit 8 byte offset that always applies to operations with pc and
1652 // gives a return address 12 bytes down.
1653 masm_->add(lr, pc, Operand(Assembler::kInstrSize));
1654 masm_->cmp(sp, Operand(ip));
1655 StackCheckStub stub;
1656 // Call the stub if lower.
1657 masm_->mov(pc,
1658 Operand(reinterpret_cast<intptr_t>(stub.GetCode().location()),
1659 RelocInfo::CODE_TARGET),
1660 LeaveCC,
1661 lo);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001662}
1663
1664
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001665void CodeGenerator::VisitStatements(ZoneList<Statement*>* statements) {
1666#ifdef DEBUG
1667 int original_height = frame_->height();
1668#endif
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001669 for (int i = 0; frame_ != NULL && i < statements->length(); i++) {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001670 Visit(statements->at(i));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001671 }
1672 ASSERT(!has_valid_frame() || frame_->height() == original_height);
1673}
1674
1675
ager@chromium.org7c537e22008-10-16 08:43:32 +00001676void CodeGenerator::VisitBlock(Block* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001677#ifdef DEBUG
1678 int original_height = frame_->height();
1679#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001680 Comment cmnt(masm_, "[ Block");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001681 CodeForStatementPosition(node);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00001682 node->break_target()->SetExpectedHeight();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001683 VisitStatements(node->statements());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001684 if (node->break_target()->is_linked()) {
1685 node->break_target()->Bind();
1686 }
1687 node->break_target()->Unuse();
1688 ASSERT(!has_valid_frame() || frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001689}
1690
1691
ager@chromium.org7c537e22008-10-16 08:43:32 +00001692void CodeGenerator::DeclareGlobals(Handle<FixedArray> pairs) {
ager@chromium.org3811b432009-10-28 14:53:37 +00001693 frame_->EmitPush(cp);
ager@chromium.orgac091b72010-05-05 07:34:42 +00001694 frame_->EmitPush(Operand(pairs));
1695 frame_->EmitPush(Operand(Smi::FromInt(is_eval() ? 1 : 0)));
1696
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001697 frame_->CallRuntime(Runtime::kDeclareGlobals, 3);
mads.s.ager31e71382008-08-13 09:32:07 +00001698 // The result is discarded.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001699}
1700
1701
ager@chromium.org7c537e22008-10-16 08:43:32 +00001702void CodeGenerator::VisitDeclaration(Declaration* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001703#ifdef DEBUG
1704 int original_height = frame_->height();
1705#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001706 Comment cmnt(masm_, "[ Declaration");
1707 Variable* var = node->proxy()->var();
1708 ASSERT(var != NULL); // must have been resolved
1709 Slot* slot = var->slot();
1710
1711 // If it was not possible to allocate the variable at compile time,
1712 // we need to "declare" it at runtime to make sure it actually
1713 // exists in the local context.
1714 if (slot != NULL && slot->type() == Slot::LOOKUP) {
1715 // Variables with a "LOOKUP" slot were introduced as non-locals
1716 // during variable resolution and must have mode DYNAMIC.
ager@chromium.org381abbb2009-02-25 13:23:22 +00001717 ASSERT(var->is_dynamic());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001718 // For now, just do a runtime call.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001719 frame_->EmitPush(cp);
ager@chromium.orgac091b72010-05-05 07:34:42 +00001720 frame_->EmitPush(Operand(var->name()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001721 // Declaration nodes are always declared in only two modes.
1722 ASSERT(node->mode() == Variable::VAR || node->mode() == Variable::CONST);
1723 PropertyAttributes attr = node->mode() == Variable::VAR ? NONE : READ_ONLY;
ager@chromium.orgac091b72010-05-05 07:34:42 +00001724 frame_->EmitPush(Operand(Smi::FromInt(attr)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001725 // Push initial value, if any.
1726 // Note: For variables we must not push an initial value (such as
1727 // 'undefined') because we may have a (legal) redeclaration and we
1728 // must not destroy the current value.
1729 if (node->mode() == Variable::CONST) {
ager@chromium.orgac091b72010-05-05 07:34:42 +00001730 frame_->EmitPushRoot(Heap::kTheHoleValueRootIndex);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001731 } else if (node->fun() != NULL) {
ager@chromium.orgac091b72010-05-05 07:34:42 +00001732 Load(node->fun());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001733 } else {
ager@chromium.orgac091b72010-05-05 07:34:42 +00001734 frame_->EmitPush(Operand(0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001735 }
ager@chromium.orgac091b72010-05-05 07:34:42 +00001736
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001737 frame_->CallRuntime(Runtime::kDeclareContextSlot, 4);
ager@chromium.org7c537e22008-10-16 08:43:32 +00001738 // Ignore the return value (declarations are statements).
ager@chromium.orgac091b72010-05-05 07:34:42 +00001739
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001740 ASSERT(frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001741 return;
1742 }
1743
1744 ASSERT(!var->is_global());
1745
1746 // If we have a function or a constant, we need to initialize the variable.
1747 Expression* val = NULL;
1748 if (node->mode() == Variable::CONST) {
1749 val = new Literal(Factory::the_hole_value());
1750 } else {
1751 val = node->fun(); // NULL if we don't have a function
1752 }
1753
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00001754
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001755 if (val != NULL) {
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00001756 WriteBarrierCharacter wb_info =
1757 val->type()->IsLikelySmi() ? LIKELY_SMI : UNLIKELY_SMI;
1758 if (val->AsLiteral() != NULL) wb_info = NEVER_NEWSPACE;
ager@chromium.orgac091b72010-05-05 07:34:42 +00001759 // Set initial value.
1760 Reference target(this, node->proxy());
1761 Load(val);
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00001762 target.SetValue(NOT_CONST_INIT, wb_info);
ager@chromium.orgac091b72010-05-05 07:34:42 +00001763
iposva@chromium.org245aa852009-02-10 00:49:54 +00001764 // Get rid of the assigned value (declarations are statements).
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001765 frame_->Drop();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001766 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001767 ASSERT(frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001768}
1769
1770
ager@chromium.org7c537e22008-10-16 08:43:32 +00001771void CodeGenerator::VisitExpressionStatement(ExpressionStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001772#ifdef DEBUG
1773 int original_height = frame_->height();
1774#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001775 Comment cmnt(masm_, "[ ExpressionStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001776 CodeForStatementPosition(node);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001777 Expression* expression = node->expression();
1778 expression->MarkAsStatement();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001779 Load(expression);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001780 frame_->Drop();
1781 ASSERT(frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001782}
1783
1784
ager@chromium.org7c537e22008-10-16 08:43:32 +00001785void CodeGenerator::VisitEmptyStatement(EmptyStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001786#ifdef DEBUG
1787 int original_height = frame_->height();
1788#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001789 Comment cmnt(masm_, "// EmptyStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001790 CodeForStatementPosition(node);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001791 // nothing to do
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001792 ASSERT(frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001793}
1794
1795
ager@chromium.org7c537e22008-10-16 08:43:32 +00001796void CodeGenerator::VisitIfStatement(IfStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001797#ifdef DEBUG
1798 int original_height = frame_->height();
1799#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001800 Comment cmnt(masm_, "[ IfStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001801 // Generate different code depending on which parts of the if statement
1802 // are present or not.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001803 bool has_then_stm = node->HasThenStatement();
1804 bool has_else_stm = node->HasElseStatement();
1805
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001806 CodeForStatementPosition(node);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001807
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001808 JumpTarget exit;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001809 if (has_then_stm && has_else_stm) {
mads.s.ager31e71382008-08-13 09:32:07 +00001810 Comment cmnt(masm_, "[ IfThenElse");
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001811 JumpTarget then;
1812 JumpTarget else_;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001813 // if (cond)
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001814 LoadCondition(node->condition(), &then, &else_, true);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001815 if (frame_ != NULL) {
1816 Branch(false, &else_);
1817 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001818 // then
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001819 if (frame_ != NULL || then.is_linked()) {
1820 then.Bind();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001821 Visit(node->then_statement());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001822 }
1823 if (frame_ != NULL) {
1824 exit.Jump();
1825 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001826 // else
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001827 if (else_.is_linked()) {
1828 else_.Bind();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001829 Visit(node->else_statement());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001830 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001831
1832 } else if (has_then_stm) {
mads.s.ager31e71382008-08-13 09:32:07 +00001833 Comment cmnt(masm_, "[ IfThen");
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001834 ASSERT(!has_else_stm);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001835 JumpTarget then;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001836 // if (cond)
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001837 LoadCondition(node->condition(), &then, &exit, true);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001838 if (frame_ != NULL) {
1839 Branch(false, &exit);
1840 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001841 // then
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001842 if (frame_ != NULL || then.is_linked()) {
1843 then.Bind();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001844 Visit(node->then_statement());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001845 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001846
1847 } else if (has_else_stm) {
mads.s.ager31e71382008-08-13 09:32:07 +00001848 Comment cmnt(masm_, "[ IfElse");
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001849 ASSERT(!has_then_stm);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001850 JumpTarget else_;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001851 // if (!cond)
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001852 LoadCondition(node->condition(), &exit, &else_, true);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001853 if (frame_ != NULL) {
1854 Branch(true, &exit);
1855 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001856 // else
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001857 if (frame_ != NULL || else_.is_linked()) {
1858 else_.Bind();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001859 Visit(node->else_statement());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001860 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001861
1862 } else {
mads.s.ager31e71382008-08-13 09:32:07 +00001863 Comment cmnt(masm_, "[ If");
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001864 ASSERT(!has_then_stm && !has_else_stm);
1865 // if (cond)
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001866 LoadCondition(node->condition(), &exit, &exit, false);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001867 if (frame_ != NULL) {
1868 if (has_cc()) {
1869 cc_reg_ = al;
1870 } else {
1871 frame_->Drop();
1872 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001873 }
1874 }
1875
1876 // end
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001877 if (exit.is_linked()) {
1878 exit.Bind();
1879 }
1880 ASSERT(!has_valid_frame() || frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001881}
1882
1883
ager@chromium.org7c537e22008-10-16 08:43:32 +00001884void CodeGenerator::VisitContinueStatement(ContinueStatement* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001885 Comment cmnt(masm_, "[ ContinueStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001886 CodeForStatementPosition(node);
1887 node->target()->continue_target()->Jump();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001888}
1889
1890
ager@chromium.org7c537e22008-10-16 08:43:32 +00001891void CodeGenerator::VisitBreakStatement(BreakStatement* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001892 Comment cmnt(masm_, "[ BreakStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001893 CodeForStatementPosition(node);
1894 node->target()->break_target()->Jump();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001895}
1896
1897
ager@chromium.org7c537e22008-10-16 08:43:32 +00001898void CodeGenerator::VisitReturnStatement(ReturnStatement* node) {
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00001899 frame_->SpillAll();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001900 Comment cmnt(masm_, "[ ReturnStatement");
mads.s.ager31e71382008-08-13 09:32:07 +00001901
ager@chromium.org4af710e2009-09-15 12:20:11 +00001902 CodeForStatementPosition(node);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001903 Load(node->expression());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001904 if (function_return_is_shadowed_) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001905 frame_->EmitPop(r0);
1906 function_return_.Jump();
1907 } else {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001908 // Pop the result from the frame and prepare the frame for
1909 // returning thus making it easier to merge.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00001910 frame_->PopToR0();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001911 frame_->PrepareForReturn();
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00001912 if (function_return_.is_bound()) {
1913 // If the function return label is already bound we reuse the
1914 // code by jumping to the return site.
1915 function_return_.Jump();
1916 } else {
1917 function_return_.Bind();
1918 GenerateReturnSequence();
1919 }
1920 }
1921}
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001922
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00001923
1924void CodeGenerator::GenerateReturnSequence() {
1925 if (FLAG_trace) {
1926 // Push the return value on the stack as the parameter.
1927 // Runtime::TraceExit returns the parameter as it is.
1928 frame_->EmitPush(r0);
1929 frame_->CallRuntime(Runtime::kTraceExit, 1);
1930 }
1931
1932#ifdef DEBUG
1933 // Add a label for checking the size of the code used for returning.
1934 Label check_exit_codesize;
1935 masm_->bind(&check_exit_codesize);
1936#endif
1937 // Make sure that the constant pool is not emitted inside of the return
1938 // sequence.
1939 { Assembler::BlockConstPoolScope block_const_pool(masm_);
1940 // Tear down the frame which will restore the caller's frame pointer and
1941 // the link register.
1942 frame_->Exit();
1943
1944 // Here we use masm_-> instead of the __ macro to avoid the code coverage
1945 // tool from instrumenting as we rely on the code size here.
1946 int32_t sp_delta = (scope()->num_parameters() + 1) * kPointerSize;
1947 masm_->add(sp, sp, Operand(sp_delta));
1948 masm_->Jump(lr);
1949 DeleteFrame();
1950
1951#ifdef DEBUG
1952 // Check that the size of the code used for returning matches what is
1953 // expected by the debugger. If the sp_delts above cannot be encoded in
1954 // the add instruction the add will generate two instructions.
1955 int return_sequence_length =
1956 masm_->InstructionsGeneratedSince(&check_exit_codesize);
1957 CHECK(return_sequence_length ==
1958 Assembler::kJSReturnSequenceInstructions ||
1959 return_sequence_length ==
1960 Assembler::kJSReturnSequenceInstructions + 1);
1961#endif
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001962 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001963}
1964
1965
ager@chromium.org7c537e22008-10-16 08:43:32 +00001966void CodeGenerator::VisitWithEnterStatement(WithEnterStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001967#ifdef DEBUG
1968 int original_height = frame_->height();
1969#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001970 Comment cmnt(masm_, "[ WithEnterStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001971 CodeForStatementPosition(node);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001972 Load(node->expression());
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001973 if (node->is_catch_block()) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001974 frame_->CallRuntime(Runtime::kPushCatchContext, 1);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001975 } else {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001976 frame_->CallRuntime(Runtime::kPushContext, 1);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001977 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00001978#ifdef DEBUG
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001979 JumpTarget verified_true;
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00001980 __ cmp(r0, cp);
ager@chromium.org65dad4b2009-04-23 08:48:43 +00001981 verified_true.Branch(eq);
1982 __ stop("PushContext: r0 is expected to be the same as cp");
1983 verified_true.Bind();
1984#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001985 // Update context local.
ager@chromium.org3bf7b912008-11-17 09:09:45 +00001986 __ str(cp, frame_->Context());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001987 ASSERT(frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001988}
1989
1990
ager@chromium.org7c537e22008-10-16 08:43:32 +00001991void CodeGenerator::VisitWithExitStatement(WithExitStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001992#ifdef DEBUG
1993 int original_height = frame_->height();
1994#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001995 Comment cmnt(masm_, "[ WithExitStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001996 CodeForStatementPosition(node);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001997 // Pop context.
1998 __ ldr(cp, ContextOperand(cp, Context::PREVIOUS_INDEX));
1999 // Update context local.
ager@chromium.org3bf7b912008-11-17 09:09:45 +00002000 __ str(cp, frame_->Context());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002001 ASSERT(frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002002}
2003
2004
ager@chromium.org7c537e22008-10-16 08:43:32 +00002005void CodeGenerator::VisitSwitchStatement(SwitchStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002006#ifdef DEBUG
2007 int original_height = frame_->height();
2008#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002009 Comment cmnt(masm_, "[ SwitchStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002010 CodeForStatementPosition(node);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00002011 node->break_target()->SetExpectedHeight();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002012
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002013 Load(node->tag());
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002014
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002015 JumpTarget next_test;
2016 JumpTarget fall_through;
2017 JumpTarget default_entry;
2018 JumpTarget default_exit(JumpTarget::BIDIRECTIONAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002019 ZoneList<CaseClause*>* cases = node->cases();
2020 int length = cases->length();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002021 CaseClause* default_clause = NULL;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002022
2023 for (int i = 0; i < length; i++) {
2024 CaseClause* clause = cases->at(i);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002025 if (clause->is_default()) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002026 // Remember the default clause and compile it at the end.
2027 default_clause = clause;
2028 continue;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002029 }
2030
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002031 Comment cmnt(masm_, "[ Case clause");
2032 // Compile the test.
2033 next_test.Bind();
2034 next_test.Unuse();
2035 // Duplicate TOS.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00002036 frame_->Dup();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00002037 Comparison(eq, NULL, clause->label(), true);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002038 Branch(false, &next_test);
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00002039
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002040 // Before entering the body from the test, remove the switch value from
2041 // the stack.
2042 frame_->Drop();
2043
2044 // Label the body so that fall through is enabled.
2045 if (i > 0 && cases->at(i - 1)->is_default()) {
2046 default_exit.Bind();
2047 } else {
2048 fall_through.Bind();
2049 fall_through.Unuse();
2050 }
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002051 VisitStatements(clause->statements());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002052
2053 // If control flow can fall through from the body, jump to the next body
2054 // or the end of the statement.
2055 if (frame_ != NULL) {
2056 if (i < length - 1 && cases->at(i + 1)->is_default()) {
2057 default_entry.Jump();
2058 } else {
2059 fall_through.Jump();
2060 }
2061 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002062 }
2063
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002064 // The final "test" removes the switch value.
2065 next_test.Bind();
2066 frame_->Drop();
2067
2068 // If there is a default clause, compile it.
2069 if (default_clause != NULL) {
2070 Comment cmnt(masm_, "[ Default clause");
2071 default_entry.Bind();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002072 VisitStatements(default_clause->statements());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002073 // If control flow can fall out of the default and there is a case after
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00002074 // it, jump to that case's body.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002075 if (frame_ != NULL && default_exit.is_bound()) {
2076 default_exit.Jump();
2077 }
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00002078 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002079
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002080 if (fall_through.is_linked()) {
2081 fall_through.Bind();
2082 }
2083
2084 if (node->break_target()->is_linked()) {
2085 node->break_target()->Bind();
2086 }
2087 node->break_target()->Unuse();
2088 ASSERT(!has_valid_frame() || frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002089}
2090
2091
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002092void CodeGenerator::VisitDoWhileStatement(DoWhileStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002093#ifdef DEBUG
2094 int original_height = frame_->height();
2095#endif
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002096 Comment cmnt(masm_, "[ DoWhileStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002097 CodeForStatementPosition(node);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00002098 node->break_target()->SetExpectedHeight();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002099 JumpTarget body(JumpTarget::BIDIRECTIONAL);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00002100 IncrementLoopNesting();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002101
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002102 // Label the top of the loop for the backward CFG edge. If the test
2103 // is always true we can use the continue target, and if the test is
2104 // always false there is no need.
2105 ConditionAnalysis info = AnalyzeCondition(node->cond());
2106 switch (info) {
2107 case ALWAYS_TRUE:
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00002108 node->continue_target()->SetExpectedHeight();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002109 node->continue_target()->Bind();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002110 break;
2111 case ALWAYS_FALSE:
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00002112 node->continue_target()->SetExpectedHeight();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002113 break;
2114 case DONT_KNOW:
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00002115 node->continue_target()->SetExpectedHeight();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002116 body.Bind();
2117 break;
2118 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002119
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002120 CheckStack(); // TODO(1222600): ignore if body contains calls.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002121 Visit(node->body());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002122
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00002123 // Compile the test.
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002124 switch (info) {
2125 case ALWAYS_TRUE:
2126 // If control can fall off the end of the body, jump back to the
2127 // top.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002128 if (has_valid_frame()) {
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002129 node->continue_target()->Jump();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002130 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002131 break;
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002132 case ALWAYS_FALSE:
2133 // If we have a continue in the body, we only have to bind its
2134 // jump target.
2135 if (node->continue_target()->is_linked()) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002136 node->continue_target()->Bind();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002137 }
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002138 break;
2139 case DONT_KNOW:
2140 // We have to compile the test expression if it can be reached by
2141 // control flow falling out of the body or via continue.
2142 if (node->continue_target()->is_linked()) {
2143 node->continue_target()->Bind();
2144 }
2145 if (has_valid_frame()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002146 Comment cmnt(masm_, "[ DoWhileCondition");
2147 CodeForDoWhileConditionPosition(node);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002148 LoadCondition(node->cond(), &body, node->break_target(), true);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002149 if (has_valid_frame()) {
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002150 // A invalid frame here indicates that control did not
2151 // fall out of the test expression.
2152 Branch(true, &body);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002153 }
2154 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002155 break;
2156 }
2157
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002158 if (node->break_target()->is_linked()) {
2159 node->break_target()->Bind();
2160 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00002161 DecrementLoopNesting();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002162 ASSERT(!has_valid_frame() || frame_->height() == original_height);
2163}
2164
2165
2166void CodeGenerator::VisitWhileStatement(WhileStatement* node) {
2167#ifdef DEBUG
2168 int original_height = frame_->height();
2169#endif
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002170 Comment cmnt(masm_, "[ WhileStatement");
2171 CodeForStatementPosition(node);
2172
2173 // If the test is never true and has no side effects there is no need
2174 // to compile the test or body.
2175 ConditionAnalysis info = AnalyzeCondition(node->cond());
2176 if (info == ALWAYS_FALSE) return;
2177
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00002178 node->break_target()->SetExpectedHeight();
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00002179 IncrementLoopNesting();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002180
2181 // Label the top of the loop with the continue target for the backward
2182 // CFG edge.
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00002183 node->continue_target()->SetExpectedHeight();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002184 node->continue_target()->Bind();
2185
2186 if (info == DONT_KNOW) {
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00002187 JumpTarget body(JumpTarget::BIDIRECTIONAL);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002188 LoadCondition(node->cond(), &body, node->break_target(), true);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002189 if (has_valid_frame()) {
2190 // A NULL frame indicates that control did not fall out of the
2191 // test expression.
2192 Branch(false, node->break_target());
2193 }
2194 if (has_valid_frame() || body.is_linked()) {
2195 body.Bind();
2196 }
2197 }
2198
2199 if (has_valid_frame()) {
2200 CheckStack(); // TODO(1222600): ignore if body contains calls.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002201 Visit(node->body());
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002202
2203 // If control flow can fall out of the body, jump back to the top.
2204 if (has_valid_frame()) {
2205 node->continue_target()->Jump();
2206 }
2207 }
2208 if (node->break_target()->is_linked()) {
2209 node->break_target()->Bind();
2210 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00002211 DecrementLoopNesting();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002212 ASSERT(!has_valid_frame() || frame_->height() == original_height);
2213}
2214
2215
2216void CodeGenerator::VisitForStatement(ForStatement* node) {
2217#ifdef DEBUG
2218 int original_height = frame_->height();
2219#endif
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002220 Comment cmnt(masm_, "[ ForStatement");
2221 CodeForStatementPosition(node);
2222 if (node->init() != NULL) {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002223 Visit(node->init());
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002224 }
2225
2226 // If the test is never true there is no need to compile the test or
2227 // body.
2228 ConditionAnalysis info = AnalyzeCondition(node->cond());
2229 if (info == ALWAYS_FALSE) return;
2230
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00002231 node->break_target()->SetExpectedHeight();
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00002232 IncrementLoopNesting();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002233
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00002234 // We know that the loop index is a smi if it is not modified in the
2235 // loop body and it is checked against a constant limit in the loop
2236 // condition. In this case, we reset the static type information of the
2237 // loop index to smi before compiling the body, the update expression, and
2238 // the bottom check of the loop condition.
2239 TypeInfoCodeGenState type_info_scope(this,
2240 node->is_fast_smi_loop() ?
2241 node->loop_variable()->slot() :
2242 NULL,
2243 TypeInfo::Smi());
2244
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002245 // If there is no update statement, label the top of the loop with the
2246 // continue target, otherwise with the loop target.
2247 JumpTarget loop(JumpTarget::BIDIRECTIONAL);
2248 if (node->next() == NULL) {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00002249 node->continue_target()->SetExpectedHeight();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002250 node->continue_target()->Bind();
2251 } else {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00002252 node->continue_target()->SetExpectedHeight();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002253 loop.Bind();
2254 }
2255
2256 // If the test is always true, there is no need to compile it.
2257 if (info == DONT_KNOW) {
2258 JumpTarget body;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002259 LoadCondition(node->cond(), &body, node->break_target(), true);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002260 if (has_valid_frame()) {
2261 Branch(false, node->break_target());
2262 }
2263 if (has_valid_frame() || body.is_linked()) {
2264 body.Bind();
2265 }
2266 }
2267
2268 if (has_valid_frame()) {
2269 CheckStack(); // TODO(1222600): ignore if body contains calls.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002270 Visit(node->body());
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002271
2272 if (node->next() == NULL) {
2273 // If there is no update statement and control flow can fall out
2274 // of the loop, jump directly to the continue label.
2275 if (has_valid_frame()) {
2276 node->continue_target()->Jump();
2277 }
2278 } else {
2279 // If there is an update statement and control flow can reach it
2280 // via falling out of the body of the loop or continuing, we
2281 // compile the update statement.
2282 if (node->continue_target()->is_linked()) {
2283 node->continue_target()->Bind();
2284 }
2285 if (has_valid_frame()) {
2286 // Record source position of the statement as this code which is
2287 // after the code for the body actually belongs to the loop
2288 // statement and not the body.
2289 CodeForStatementPosition(node);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002290 Visit(node->next());
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002291 loop.Jump();
2292 }
2293 }
2294 }
2295 if (node->break_target()->is_linked()) {
2296 node->break_target()->Bind();
2297 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00002298 DecrementLoopNesting();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002299 ASSERT(!has_valid_frame() || frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002300}
2301
2302
ager@chromium.org7c537e22008-10-16 08:43:32 +00002303void CodeGenerator::VisitForInStatement(ForInStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002304#ifdef DEBUG
2305 int original_height = frame_->height();
2306#endif
ager@chromium.org357bf652010-04-12 11:30:10 +00002307 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002308 Comment cmnt(masm_, "[ ForInStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002309 CodeForStatementPosition(node);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002310
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002311 JumpTarget primitive;
2312 JumpTarget jsobject;
2313 JumpTarget fixed_array;
2314 JumpTarget entry(JumpTarget::BIDIRECTIONAL);
2315 JumpTarget end_del_check;
2316 JumpTarget exit;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002317
2318 // Get the object to enumerate over (converted to JSObject).
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002319 Load(node->enumerable());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002320
2321 // Both SpiderMonkey and kjs ignore null and undefined in contrast
2322 // to the specification. 12.6.4 mandates a call to ToObject.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002323 frame_->EmitPop(r0);
ager@chromium.orgab99eea2009-08-25 07:05:41 +00002324 __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
2325 __ cmp(r0, ip);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002326 exit.Branch(eq);
ager@chromium.orgab99eea2009-08-25 07:05:41 +00002327 __ LoadRoot(ip, Heap::kNullValueRootIndex);
2328 __ cmp(r0, ip);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002329 exit.Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002330
2331 // Stack layout in body:
2332 // [iteration counter (Smi)]
2333 // [length of array]
2334 // [FixedArray]
2335 // [Map or 0]
2336 // [Object]
2337
2338 // Check if enumerable is already a JSObject
2339 __ tst(r0, Operand(kSmiTagMask));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002340 primitive.Branch(eq);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002341 __ CompareObjectType(r0, r1, r1, FIRST_JS_OBJECT_TYPE);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002342 jsobject.Branch(hs);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002343
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002344 primitive.Bind();
2345 frame_->EmitPush(r0);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00002346 frame_->InvokeBuiltin(Builtins::TO_OBJECT, CALL_JS, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002347
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002348 jsobject.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002349 // Get the set of properties (as a FixedArray or Map).
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00002350 // r0: value to be iterated over
2351 frame_->EmitPush(r0); // Push the object being iterated over.
2352
2353 // Check cache validity in generated code. This is a fast case for
2354 // the JSObject::IsSimpleEnum cache validity checks. If we cannot
2355 // guarantee cache validity, call the runtime system to check cache
2356 // validity or get the property names in a fixed array.
2357 JumpTarget call_runtime;
2358 JumpTarget loop(JumpTarget::BIDIRECTIONAL);
2359 JumpTarget check_prototype;
2360 JumpTarget use_cache;
2361 __ mov(r1, Operand(r0));
2362 loop.Bind();
2363 // Check that there are no elements.
2364 __ ldr(r2, FieldMemOperand(r1, JSObject::kElementsOffset));
2365 __ LoadRoot(r4, Heap::kEmptyFixedArrayRootIndex);
2366 __ cmp(r2, r4);
2367 call_runtime.Branch(ne);
2368 // Check that instance descriptors are not empty so that we can
2369 // check for an enum cache. Leave the map in r3 for the subsequent
2370 // prototype load.
2371 __ ldr(r3, FieldMemOperand(r1, HeapObject::kMapOffset));
2372 __ ldr(r2, FieldMemOperand(r3, Map::kInstanceDescriptorsOffset));
2373 __ LoadRoot(ip, Heap::kEmptyDescriptorArrayRootIndex);
2374 __ cmp(r2, ip);
2375 call_runtime.Branch(eq);
2376 // Check that there in an enum cache in the non-empty instance
2377 // descriptors. This is the case if the next enumeration index
2378 // field does not contain a smi.
2379 __ ldr(r2, FieldMemOperand(r2, DescriptorArray::kEnumerationIndexOffset));
2380 __ tst(r2, Operand(kSmiTagMask));
2381 call_runtime.Branch(eq);
2382 // For all objects but the receiver, check that the cache is empty.
2383 // r4: empty fixed array root.
2384 __ cmp(r1, r0);
2385 check_prototype.Branch(eq);
2386 __ ldr(r2, FieldMemOperand(r2, DescriptorArray::kEnumCacheBridgeCacheOffset));
2387 __ cmp(r2, r4);
2388 call_runtime.Branch(ne);
2389 check_prototype.Bind();
2390 // Load the prototype from the map and loop if non-null.
2391 __ ldr(r1, FieldMemOperand(r3, Map::kPrototypeOffset));
2392 __ LoadRoot(ip, Heap::kNullValueRootIndex);
2393 __ cmp(r1, ip);
2394 loop.Branch(ne);
2395 // The enum cache is valid. Load the map of the object being
2396 // iterated over and use the cache for the iteration.
2397 __ ldr(r0, FieldMemOperand(r0, HeapObject::kMapOffset));
2398 use_cache.Jump();
2399
2400 call_runtime.Bind();
2401 // Call the runtime to get the property names for the object.
2402 frame_->EmitPush(r0); // push the object (slot 4) for the runtime call
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002403 frame_->CallRuntime(Runtime::kGetPropertyNamesFast, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002404
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00002405 // If we got a map from the runtime call, we can do a fast
2406 // modification check. Otherwise, we got a fixed array, and we have
2407 // to do a slow check.
2408 // r0: map or fixed array (result from call to
2409 // Runtime::kGetPropertyNamesFast)
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002410 __ mov(r2, Operand(r0));
2411 __ ldr(r1, FieldMemOperand(r2, HeapObject::kMapOffset));
ager@chromium.orgab99eea2009-08-25 07:05:41 +00002412 __ LoadRoot(ip, Heap::kMetaMapRootIndex);
2413 __ cmp(r1, ip);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002414 fixed_array.Branch(ne);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002415
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00002416 use_cache.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002417 // Get enum cache
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00002418 // r0: map (either the result from a call to
2419 // Runtime::kGetPropertyNamesFast or has been fetched directly from
2420 // the object)
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002421 __ mov(r1, Operand(r0));
2422 __ ldr(r1, FieldMemOperand(r1, Map::kInstanceDescriptorsOffset));
2423 __ ldr(r1, FieldMemOperand(r1, DescriptorArray::kEnumerationIndexOffset));
2424 __ ldr(r2,
2425 FieldMemOperand(r1, DescriptorArray::kEnumCacheBridgeCacheOffset));
2426
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002427 frame_->EmitPush(r0); // map
2428 frame_->EmitPush(r2); // enum cache bridge cache
mads.s.ager31e71382008-08-13 09:32:07 +00002429 __ ldr(r0, FieldMemOperand(r2, FixedArray::kLengthOffset));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002430 frame_->EmitPush(r0);
mads.s.ager31e71382008-08-13 09:32:07 +00002431 __ mov(r0, Operand(Smi::FromInt(0)));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002432 frame_->EmitPush(r0);
2433 entry.Jump();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002434
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002435 fixed_array.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002436 __ mov(r1, Operand(Smi::FromInt(0)));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002437 frame_->EmitPush(r1); // insert 0 in place of Map
2438 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002439
2440 // Push the length of the array and the initial index onto the stack.
mads.s.ager31e71382008-08-13 09:32:07 +00002441 __ ldr(r0, FieldMemOperand(r0, FixedArray::kLengthOffset));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002442 frame_->EmitPush(r0);
mads.s.ager31e71382008-08-13 09:32:07 +00002443 __ mov(r0, Operand(Smi::FromInt(0))); // init index
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002444 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002445
2446 // Condition.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002447 entry.Bind();
mads.s.ager31e71382008-08-13 09:32:07 +00002448 // sp[0] : index
2449 // sp[1] : array/enum cache length
2450 // sp[2] : array or enum cache
2451 // sp[3] : 0 or map
2452 // sp[4] : enumerable
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002453 // Grab the current frame's height for the break and continue
2454 // targets only after all the state is pushed on the frame.
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00002455 node->break_target()->SetExpectedHeight();
2456 node->continue_target()->SetExpectedHeight();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002457
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00002458 // Load the current count to r0, load the length to r1.
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00002459 __ Ldrd(r0, r1, frame_->ElementAt(0));
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00002460 __ cmp(r0, r1); // compare to the array length
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002461 node->break_target()->Branch(hs);
2462
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002463 // Get the i'th entry of the array.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002464 __ ldr(r2, frame_->ElementAt(2));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002465 __ add(r2, r2, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
2466 __ ldr(r3, MemOperand(r2, r0, LSL, kPointerSizeLog2 - kSmiTagSize));
2467
2468 // Get Map or 0.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002469 __ ldr(r2, frame_->ElementAt(3));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002470 // Check if this (still) matches the map of the enumerable.
2471 // If not, we have to filter the key.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002472 __ ldr(r1, frame_->ElementAt(4));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002473 __ ldr(r1, FieldMemOperand(r1, HeapObject::kMapOffset));
2474 __ cmp(r1, Operand(r2));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002475 end_del_check.Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002476
2477 // Convert the entry to a string (or null if it isn't a property anymore).
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002478 __ ldr(r0, frame_->ElementAt(4)); // push enumerable
2479 frame_->EmitPush(r0);
2480 frame_->EmitPush(r3); // push entry
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00002481 frame_->InvokeBuiltin(Builtins::FILTER_KEY, CALL_JS, 2);
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00002482 __ mov(r3, Operand(r0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002483
2484 // If the property has been removed while iterating, we just skip it.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00002485 __ LoadRoot(ip, Heap::kNullValueRootIndex);
2486 __ cmp(r3, ip);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002487 node->continue_target()->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002488
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002489 end_del_check.Bind();
2490 // Store the entry in the 'each' expression and take another spin in the
2491 // loop. r3: i'th entry of the enum cache (or string there of)
2492 frame_->EmitPush(r3); // push entry
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002493 { Reference each(this, node->each());
2494 if (!each.is_illegal()) {
mads.s.ager31e71382008-08-13 09:32:07 +00002495 if (each.size() > 0) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002496 __ ldr(r0, frame_->ElementAt(each.size()));
2497 frame_->EmitPush(r0);
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00002498 each.SetValue(NOT_CONST_INIT, UNLIKELY_SMI);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002499 frame_->Drop(2);
2500 } else {
2501 // If the reference was to a slot we rely on the convenient property
2502 // that it doesn't matter whether a value (eg, r3 pushed above) is
2503 // right on top of or right underneath a zero-sized reference.
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00002504 each.SetValue(NOT_CONST_INIT, UNLIKELY_SMI);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002505 frame_->Drop();
mads.s.ager31e71382008-08-13 09:32:07 +00002506 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002507 }
2508 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002509 // Body.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002510 CheckStack(); // TODO(1222600): ignore if body contains calls.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002511 Visit(node->body());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002512
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002513 // Next. Reestablish a spilled frame in case we are coming here via
2514 // a continue in the body.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002515 node->continue_target()->Bind();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002516 frame_->SpillAll();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002517 frame_->EmitPop(r0);
2518 __ add(r0, r0, Operand(Smi::FromInt(1)));
2519 frame_->EmitPush(r0);
2520 entry.Jump();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002521
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002522 // Cleanup. No need to spill because VirtualFrame::Drop is safe for
2523 // any frame.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002524 node->break_target()->Bind();
ager@chromium.org3bf7b912008-11-17 09:09:45 +00002525 frame_->Drop(5);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002526
2527 // Exit.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002528 exit.Bind();
2529 node->continue_target()->Unuse();
2530 node->break_target()->Unuse();
2531 ASSERT(frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002532}
2533
2534
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002535void CodeGenerator::VisitTryCatchStatement(TryCatchStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002536#ifdef DEBUG
2537 int original_height = frame_->height();
2538#endif
ager@chromium.org357bf652010-04-12 11:30:10 +00002539 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002540 Comment cmnt(masm_, "[ TryCatchStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002541 CodeForStatementPosition(node);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002542
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002543 JumpTarget try_block;
2544 JumpTarget exit;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002545
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002546 try_block.Call();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002547 // --- Catch block ---
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002548 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002549
2550 // Store the caught exception in the catch variable.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002551 Variable* catch_var = node->catch_var()->var();
2552 ASSERT(catch_var != NULL && catch_var->slot() != NULL);
2553 StoreToSlot(catch_var->slot(), NOT_CONST_INIT);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002554
2555 // Remove the exception from the stack.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002556 frame_->Drop();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002557
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002558 VisitStatements(node->catch_block()->statements());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002559 if (frame_ != NULL) {
2560 exit.Jump();
2561 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002562
2563
2564 // --- Try block ---
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002565 try_block.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002566
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002567 frame_->PushTryHandler(TRY_CATCH_HANDLER);
2568 int handler_height = frame_->height();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002569
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002570 // Shadow the labels for all escapes from the try block, including
2571 // returns. During shadowing, the original label is hidden as the
2572 // LabelShadow and operations on the original actually affect the
2573 // shadowing label.
2574 //
2575 // We should probably try to unify the escaping labels and the return
2576 // label.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002577 int nof_escapes = node->escaping_targets()->length();
2578 List<ShadowTarget*> shadows(1 + nof_escapes);
2579
2580 // Add the shadow target for the function return.
2581 static const int kReturnShadowIndex = 0;
2582 shadows.Add(new ShadowTarget(&function_return_));
2583 bool function_return_was_shadowed = function_return_is_shadowed_;
2584 function_return_is_shadowed_ = true;
2585 ASSERT(shadows[kReturnShadowIndex]->other_target() == &function_return_);
2586
2587 // Add the remaining shadow targets.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002588 for (int i = 0; i < nof_escapes; i++) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002589 shadows.Add(new ShadowTarget(node->escaping_targets()->at(i)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002590 }
2591
2592 // Generate code for the statements in the try block.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002593 VisitStatements(node->try_block()->statements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002594
2595 // Stop the introduced shadowing and count the number of required unlinks.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002596 // After shadowing stops, the original labels are unshadowed and the
2597 // LabelShadows represent the formerly shadowing labels.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002598 bool has_unlinks = false;
2599 for (int i = 0; i < shadows.length(); i++) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002600 shadows[i]->StopShadowing();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002601 has_unlinks = has_unlinks || shadows[i]->is_linked();
2602 }
2603 function_return_is_shadowed_ = function_return_was_shadowed;
2604
2605 // Get an external reference to the handler address.
2606 ExternalReference handler_address(Top::k_handler_address);
2607
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002608 // If we can fall off the end of the try block, unlink from try chain.
2609 if (has_valid_frame()) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002610 // The next handler address is on top of the frame. Unlink from
2611 // the handler list and drop the rest of this handler from the
2612 // frame.
2613 ASSERT(StackHandlerConstants::kNextOffset == 0);
2614 frame_->EmitPop(r1);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002615 __ mov(r3, Operand(handler_address));
2616 __ str(r1, MemOperand(r3));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002617 frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002618 if (has_unlinks) {
2619 exit.Jump();
2620 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002621 }
2622
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002623 // Generate unlink code for the (formerly) shadowing labels that have been
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002624 // jumped to. Deallocate each shadow target.
2625 for (int i = 0; i < shadows.length(); i++) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002626 if (shadows[i]->is_linked()) {
mads.s.ager31e71382008-08-13 09:32:07 +00002627 // Unlink from try chain;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002628 shadows[i]->Bind();
2629 // Because we can be jumping here (to spilled code) from unspilled
2630 // code, we need to reestablish a spilled frame at this block.
2631 frame_->SpillAll();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002632
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002633 // Reload sp from the top handler, because some statements that we
2634 // break from (eg, for...in) may have left stuff on the stack.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002635 __ mov(r3, Operand(handler_address));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002636 __ ldr(sp, MemOperand(r3));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002637 frame_->Forget(frame_->height() - handler_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002638
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002639 ASSERT(StackHandlerConstants::kNextOffset == 0);
2640 frame_->EmitPop(r1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002641 __ str(r1, MemOperand(r3));
ager@chromium.org3bf7b912008-11-17 09:09:45 +00002642 frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002643
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002644 if (!function_return_is_shadowed_ && i == kReturnShadowIndex) {
2645 frame_->PrepareForReturn();
2646 }
2647 shadows[i]->other_target()->Jump();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002648 }
2649 }
2650
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002651 exit.Bind();
2652 ASSERT(!has_valid_frame() || frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002653}
2654
2655
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002656void CodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002657#ifdef DEBUG
2658 int original_height = frame_->height();
2659#endif
ager@chromium.org357bf652010-04-12 11:30:10 +00002660 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002661 Comment cmnt(masm_, "[ TryFinallyStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002662 CodeForStatementPosition(node);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002663
2664 // State: Used to keep track of reason for entering the finally
2665 // block. Should probably be extended to hold information for
2666 // break/continue from within the try block.
2667 enum { FALLING, THROWING, JUMPING };
2668
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002669 JumpTarget try_block;
2670 JumpTarget finally_block;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002671
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002672 try_block.Call();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002673
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002674 frame_->EmitPush(r0); // save exception object on the stack
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002675 // In case of thrown exceptions, this is where we continue.
2676 __ mov(r2, Operand(Smi::FromInt(THROWING)));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002677 finally_block.Jump();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002678
2679 // --- Try block ---
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002680 try_block.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002681
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002682 frame_->PushTryHandler(TRY_FINALLY_HANDLER);
2683 int handler_height = frame_->height();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002684
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002685 // Shadow the labels for all escapes from the try block, including
2686 // returns. Shadowing hides the original label as the LabelShadow and
2687 // operations on the original actually affect the shadowing label.
2688 //
2689 // We should probably try to unify the escaping labels and the return
2690 // label.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002691 int nof_escapes = node->escaping_targets()->length();
2692 List<ShadowTarget*> shadows(1 + nof_escapes);
2693
2694 // Add the shadow target for the function return.
2695 static const int kReturnShadowIndex = 0;
2696 shadows.Add(new ShadowTarget(&function_return_));
2697 bool function_return_was_shadowed = function_return_is_shadowed_;
2698 function_return_is_shadowed_ = true;
2699 ASSERT(shadows[kReturnShadowIndex]->other_target() == &function_return_);
2700
2701 // Add the remaining shadow targets.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002702 for (int i = 0; i < nof_escapes; i++) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002703 shadows.Add(new ShadowTarget(node->escaping_targets()->at(i)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002704 }
2705
2706 // Generate code for the statements in the try block.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002707 VisitStatements(node->try_block()->statements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002708
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002709 // Stop the introduced shadowing and count the number of required unlinks.
2710 // After shadowing stops, the original labels are unshadowed and the
2711 // LabelShadows represent the formerly shadowing labels.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002712 int nof_unlinks = 0;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002713 for (int i = 0; i < shadows.length(); i++) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002714 shadows[i]->StopShadowing();
2715 if (shadows[i]->is_linked()) nof_unlinks++;
2716 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002717 function_return_is_shadowed_ = function_return_was_shadowed;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002718
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002719 // Get an external reference to the handler address.
2720 ExternalReference handler_address(Top::k_handler_address);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002721
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002722 // If we can fall off the end of the try block, unlink from the try
2723 // chain and set the state on the frame to FALLING.
2724 if (has_valid_frame()) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002725 // The next handler address is on top of the frame.
2726 ASSERT(StackHandlerConstants::kNextOffset == 0);
2727 frame_->EmitPop(r1);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002728 __ mov(r3, Operand(handler_address));
2729 __ str(r1, MemOperand(r3));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002730 frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002731
2732 // Fake a top of stack value (unneeded when FALLING) and set the
2733 // state in r2, then jump around the unlink blocks if any.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00002734 __ LoadRoot(r0, Heap::kUndefinedValueRootIndex);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002735 frame_->EmitPush(r0);
2736 __ mov(r2, Operand(Smi::FromInt(FALLING)));
2737 if (nof_unlinks > 0) {
2738 finally_block.Jump();
2739 }
2740 }
2741
2742 // Generate code to unlink and set the state for the (formerly)
2743 // shadowing targets that have been jumped to.
2744 for (int i = 0; i < shadows.length(); i++) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002745 if (shadows[i]->is_linked()) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002746 // If we have come from the shadowed return, the return value is
2747 // in (a non-refcounted reference to) r0. We must preserve it
2748 // until it is pushed.
2749 //
2750 // Because we can be jumping here (to spilled code) from
2751 // unspilled code, we need to reestablish a spilled frame at
2752 // this block.
2753 shadows[i]->Bind();
2754 frame_->SpillAll();
2755
2756 // Reload sp from the top handler, because some statements that
2757 // we break from (eg, for...in) may have left stuff on the
2758 // stack.
2759 __ mov(r3, Operand(handler_address));
2760 __ ldr(sp, MemOperand(r3));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002761 frame_->Forget(frame_->height() - handler_height);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002762
2763 // Unlink this handler and drop it from the frame. The next
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002764 // handler address is currently on top of the frame.
2765 ASSERT(StackHandlerConstants::kNextOffset == 0);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002766 frame_->EmitPop(r1);
2767 __ str(r1, MemOperand(r3));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002768 frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002769
2770 if (i == kReturnShadowIndex) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002771 // If this label shadowed the function return, materialize the
2772 // return value on the stack.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002773 frame_->EmitPush(r0);
mads.s.ager31e71382008-08-13 09:32:07 +00002774 } else {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002775 // Fake TOS for targets that shadowed breaks and continues.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00002776 __ LoadRoot(r0, Heap::kUndefinedValueRootIndex);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002777 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002778 }
2779 __ mov(r2, Operand(Smi::FromInt(JUMPING + i)));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002780 if (--nof_unlinks > 0) {
2781 // If this is not the last unlink block, jump around the next.
2782 finally_block.Jump();
2783 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002784 }
2785 }
2786
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002787 // --- Finally block ---
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002788 finally_block.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002789
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002790 // Push the state on the stack.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002791 frame_->EmitPush(r2);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002792
2793 // We keep two elements on the stack - the (possibly faked) result
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002794 // and the state - while evaluating the finally block.
2795 //
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002796 // Generate code for the statements in the finally block.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002797 VisitStatements(node->finally_block()->statements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002798
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002799 if (has_valid_frame()) {
2800 // Restore state and return value or faked TOS.
2801 frame_->EmitPop(r2);
2802 frame_->EmitPop(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002803 }
2804
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002805 // Generate code to jump to the right destination for all used
2806 // formerly shadowing targets. Deallocate each shadow target.
2807 for (int i = 0; i < shadows.length(); i++) {
2808 if (has_valid_frame() && shadows[i]->is_bound()) {
2809 JumpTarget* original = shadows[i]->other_target();
2810 __ cmp(r2, Operand(Smi::FromInt(JUMPING + i)));
2811 if (!function_return_is_shadowed_ && i == kReturnShadowIndex) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002812 JumpTarget skip;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002813 skip.Branch(ne);
2814 frame_->PrepareForReturn();
2815 original->Jump();
2816 skip.Bind();
2817 } else {
2818 original->Branch(eq);
2819 }
2820 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002821 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002822
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002823 if (has_valid_frame()) {
2824 // Check if we need to rethrow the exception.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002825 JumpTarget exit;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002826 __ cmp(r2, Operand(Smi::FromInt(THROWING)));
2827 exit.Branch(ne);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002828
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002829 // Rethrow exception.
2830 frame_->EmitPush(r0);
2831 frame_->CallRuntime(Runtime::kReThrow, 1);
2832
2833 // Done.
2834 exit.Bind();
2835 }
2836 ASSERT(!has_valid_frame() || frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002837}
2838
2839
ager@chromium.org7c537e22008-10-16 08:43:32 +00002840void CodeGenerator::VisitDebuggerStatement(DebuggerStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002841#ifdef DEBUG
2842 int original_height = frame_->height();
2843#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002844 Comment cmnt(masm_, "[ DebuggerStatament");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002845 CodeForStatementPosition(node);
ager@chromium.org65dad4b2009-04-23 08:48:43 +00002846#ifdef ENABLE_DEBUGGER_SUPPORT
ager@chromium.org5c838252010-02-19 08:53:10 +00002847 frame_->DebugBreak();
ager@chromium.org65dad4b2009-04-23 08:48:43 +00002848#endif
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00002849 // Ignore the return value.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002850 ASSERT(frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002851}
2852
2853
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00002854void CodeGenerator::InstantiateFunction(
2855 Handle<SharedFunctionInfo> function_info) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002856 // Use the fast case closure allocation code that allocates in new
2857 // space for nested functions that don't need literals cloning.
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00002858 if (scope()->is_function_scope() && function_info->num_literals() == 0) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002859 FastNewClosureStub stub;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002860 frame_->EmitPush(Operand(function_info));
2861 frame_->SpillAll();
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002862 frame_->CallStub(&stub, 1);
2863 frame_->EmitPush(r0);
2864 } else {
2865 // Create a new closure.
2866 frame_->EmitPush(cp);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002867 frame_->EmitPush(Operand(function_info));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002868 frame_->CallRuntime(Runtime::kNewClosure, 2);
2869 frame_->EmitPush(r0);
2870 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002871}
2872
2873
ager@chromium.org7c537e22008-10-16 08:43:32 +00002874void CodeGenerator::VisitFunctionLiteral(FunctionLiteral* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002875#ifdef DEBUG
2876 int original_height = frame_->height();
2877#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002878 Comment cmnt(masm_, "[ FunctionLiteral");
2879
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00002880 // Build the function info and instantiate it.
2881 Handle<SharedFunctionInfo> function_info =
2882 Compiler::BuildFunctionInfo(node, script(), this);
kasper.lund212ac232008-07-16 07:07:30 +00002883 // Check for stack-overflow exception.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002884 if (HasStackOverflow()) {
2885 ASSERT(frame_->height() == original_height);
2886 return;
2887 }
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00002888 InstantiateFunction(function_info);
ager@chromium.orgac091b72010-05-05 07:34:42 +00002889 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002890}
2891
2892
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00002893void CodeGenerator::VisitSharedFunctionInfoLiteral(
2894 SharedFunctionInfoLiteral* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002895#ifdef DEBUG
2896 int original_height = frame_->height();
2897#endif
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00002898 Comment cmnt(masm_, "[ SharedFunctionInfoLiteral");
2899 InstantiateFunction(node->shared_function_info());
ager@chromium.orgac091b72010-05-05 07:34:42 +00002900 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002901}
2902
2903
ager@chromium.org7c537e22008-10-16 08:43:32 +00002904void CodeGenerator::VisitConditional(Conditional* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002905#ifdef DEBUG
2906 int original_height = frame_->height();
2907#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002908 Comment cmnt(masm_, "[ Conditional");
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002909 JumpTarget then;
2910 JumpTarget else_;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002911 LoadCondition(node->condition(), &then, &else_, true);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00002912 if (has_valid_frame()) {
2913 Branch(false, &else_);
2914 }
2915 if (has_valid_frame() || then.is_linked()) {
2916 then.Bind();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002917 Load(node->then_expression());
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00002918 }
2919 if (else_.is_linked()) {
2920 JumpTarget exit;
2921 if (has_valid_frame()) exit.Jump();
2922 else_.Bind();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002923 Load(node->else_expression());
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00002924 if (exit.is_linked()) exit.Bind();
2925 }
ager@chromium.orgac091b72010-05-05 07:34:42 +00002926 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002927}
2928
2929
ager@chromium.org7c537e22008-10-16 08:43:32 +00002930void CodeGenerator::LoadFromSlot(Slot* slot, TypeofState typeof_state) {
2931 if (slot->type() == Slot::LOOKUP) {
ager@chromium.org381abbb2009-02-25 13:23:22 +00002932 ASSERT(slot->var()->is_dynamic());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002933
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00002934 // JumpTargets do not yet support merging frames so the frame must be
2935 // spilled when jumping to these targets.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002936 JumpTarget slow;
2937 JumpTarget done;
ager@chromium.org381abbb2009-02-25 13:23:22 +00002938
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00002939 // Generate fast case for loading from slots that correspond to
2940 // local/global variables or arguments unless they are shadowed by
2941 // eval-introduced bindings.
2942 EmitDynamicLoadFromSlotFastCase(slot,
2943 typeof_state,
2944 &slow,
2945 &done);
ager@chromium.org381abbb2009-02-25 13:23:22 +00002946
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002947 slow.Bind();
2948 frame_->EmitPush(cp);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00002949 frame_->EmitPush(Operand(slot->var()->name()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002950
ager@chromium.org7c537e22008-10-16 08:43:32 +00002951 if (typeof_state == INSIDE_TYPEOF) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002952 frame_->CallRuntime(Runtime::kLoadContextSlotNoReferenceError, 2);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002953 } else {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002954 frame_->CallRuntime(Runtime::kLoadContextSlot, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002955 }
ager@chromium.org381abbb2009-02-25 13:23:22 +00002956
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002957 done.Bind();
2958 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002959
2960 } else {
ager@chromium.org357bf652010-04-12 11:30:10 +00002961 Register scratch = VirtualFrame::scratch0();
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00002962 TypeInfo info = type_info(slot);
2963 frame_->EmitPush(SlotOperand(slot, scratch), info);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00002964
ager@chromium.org7c537e22008-10-16 08:43:32 +00002965 if (slot->var()->mode() == Variable::CONST) {
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00002966 // Const slots may contain 'the hole' value (the constant hasn't been
2967 // initialized yet) which needs to be converted into the 'undefined'
2968 // value.
2969 Comment cmnt(masm_, "[ Unhole const");
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00002970 Register tos = frame_->PopToRegister();
ager@chromium.orgab99eea2009-08-25 07:05:41 +00002971 __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00002972 __ cmp(tos, ip);
2973 __ LoadRoot(tos, Heap::kUndefinedValueRootIndex, eq);
2974 frame_->EmitPush(tos);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002975 }
2976 }
2977}
2978
2979
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00002980void CodeGenerator::LoadFromSlotCheckForArguments(Slot* slot,
2981 TypeofState state) {
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00002982 VirtualFrame::RegisterAllocationScope scope(this);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00002983 LoadFromSlot(slot, state);
2984
2985 // Bail out quickly if we're not using lazy arguments allocation.
2986 if (ArgumentsMode() != LAZY_ARGUMENTS_ALLOCATION) return;
2987
2988 // ... or if the slot isn't a non-parameter arguments slot.
2989 if (slot->type() == Slot::PARAMETER || !slot->is_arguments()) return;
2990
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00002991 // Load the loaded value from the stack into a register but leave it on the
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00002992 // stack.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00002993 Register tos = frame_->Peek();
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00002994
2995 // If the loaded value is the sentinel that indicates that we
2996 // haven't loaded the arguments object yet, we need to do it now.
2997 JumpTarget exit;
2998 __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00002999 __ cmp(tos, ip);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00003000 exit.Branch(ne);
3001 frame_->Drop();
3002 StoreArgumentsObject(false);
3003 exit.Bind();
3004}
3005
3006
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003007void CodeGenerator::StoreToSlot(Slot* slot, InitState init_state) {
3008 ASSERT(slot != NULL);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003009 VirtualFrame::RegisterAllocationScope scope(this);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003010 if (slot->type() == Slot::LOOKUP) {
3011 ASSERT(slot->var()->is_dynamic());
3012
3013 // For now, just do a runtime call.
3014 frame_->EmitPush(cp);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003015 frame_->EmitPush(Operand(slot->var()->name()));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003016
3017 if (init_state == CONST_INIT) {
3018 // Same as the case for a normal store, but ignores attribute
3019 // (e.g. READ_ONLY) of context slot so that we can initialize
3020 // const properties (introduced via eval("const foo = (some
3021 // expr);")). Also, uses the current function context instead of
3022 // the top context.
3023 //
3024 // Note that we must declare the foo upon entry of eval(), via a
3025 // context slot declaration, but we cannot initialize it at the
3026 // same time, because the const declaration may be at the end of
3027 // the eval code (sigh...) and the const variable may have been
3028 // used before (where its value is 'undefined'). Thus, we can only
3029 // do the initialization when we actually encounter the expression
3030 // and when the expression operands are defined and valid, and
3031 // thus we need the split into 2 operations: declaration of the
3032 // context slot followed by initialization.
3033 frame_->CallRuntime(Runtime::kInitializeConstContextSlot, 3);
3034 } else {
3035 frame_->CallRuntime(Runtime::kStoreContextSlot, 3);
3036 }
3037 // Storing a variable must keep the (new) value on the expression
3038 // stack. This is necessary for compiling assignment expressions.
3039 frame_->EmitPush(r0);
3040
3041 } else {
3042 ASSERT(!slot->var()->is_dynamic());
ager@chromium.org357bf652010-04-12 11:30:10 +00003043 Register scratch = VirtualFrame::scratch0();
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003044 Register scratch2 = VirtualFrame::scratch1();
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003045
ager@chromium.org357bf652010-04-12 11:30:10 +00003046 // The frame must be spilled when branching to this target.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003047 JumpTarget exit;
ager@chromium.org357bf652010-04-12 11:30:10 +00003048
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003049 if (init_state == CONST_INIT) {
3050 ASSERT(slot->var()->mode() == Variable::CONST);
3051 // Only the first const initialization must be executed (the slot
3052 // still contains 'the hole' value). When the assignment is
3053 // executed, the code is identical to a normal store (see below).
3054 Comment cmnt(masm_, "[ Init const");
ager@chromium.org357bf652010-04-12 11:30:10 +00003055 __ ldr(scratch, SlotOperand(slot, scratch));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003056 __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
ager@chromium.org357bf652010-04-12 11:30:10 +00003057 __ cmp(scratch, ip);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003058 exit.Branch(ne);
3059 }
3060
3061 // We must execute the store. Storing a variable must keep the
3062 // (new) value on the stack. This is necessary for compiling
3063 // assignment expressions.
3064 //
3065 // Note: We will reach here even with slot->var()->mode() ==
3066 // Variable::CONST because of const declarations which will
3067 // initialize consts to 'the hole' value and by doing so, end up
3068 // calling this code. r2 may be loaded with context; used below in
3069 // RecordWrite.
ager@chromium.org357bf652010-04-12 11:30:10 +00003070 Register tos = frame_->Peek();
3071 __ str(tos, SlotOperand(slot, scratch));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003072 if (slot->type() == Slot::CONTEXT) {
3073 // Skip write barrier if the written value is a smi.
ager@chromium.org357bf652010-04-12 11:30:10 +00003074 __ tst(tos, Operand(kSmiTagMask));
3075 // We don't use tos any more after here.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003076 exit.Branch(eq);
ager@chromium.org357bf652010-04-12 11:30:10 +00003077 // scratch is loaded with context when calling SlotOperand above.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003078 int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize;
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003079 // We need an extra register. Until we have a way to do that in the
3080 // virtual frame we will cheat and ask for a free TOS register.
3081 Register scratch3 = frame_->GetTOSRegister();
3082 __ RecordWrite(scratch, Operand(offset), scratch2, scratch3);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003083 }
3084 // If we definitely did not jump over the assignment, we do not need
3085 // to bind the exit label. Doing so can defeat peephole
3086 // optimization.
3087 if (init_state == CONST_INIT || slot->type() == Slot::CONTEXT) {
3088 exit.Bind();
3089 }
3090 }
3091}
3092
3093
ager@chromium.org381abbb2009-02-25 13:23:22 +00003094void CodeGenerator::LoadFromGlobalSlotCheckExtensions(Slot* slot,
3095 TypeofState typeof_state,
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003096 JumpTarget* slow) {
ager@chromium.org381abbb2009-02-25 13:23:22 +00003097 // Check that no extension objects have been created by calls to
3098 // eval from the current scope to the global scope.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003099 Register tmp = frame_->scratch0();
3100 Register tmp2 = frame_->scratch1();
ager@chromium.org381abbb2009-02-25 13:23:22 +00003101 Register context = cp;
3102 Scope* s = scope();
3103 while (s != NULL) {
3104 if (s->num_heap_slots() > 0) {
3105 if (s->calls_eval()) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003106 frame_->SpillAll();
ager@chromium.org381abbb2009-02-25 13:23:22 +00003107 // Check that extension is NULL.
3108 __ ldr(tmp2, ContextOperand(context, Context::EXTENSION_INDEX));
3109 __ tst(tmp2, tmp2);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003110 slow->Branch(ne);
ager@chromium.org381abbb2009-02-25 13:23:22 +00003111 }
3112 // Load next context in chain.
3113 __ ldr(tmp, ContextOperand(context, Context::CLOSURE_INDEX));
3114 __ ldr(tmp, FieldMemOperand(tmp, JSFunction::kContextOffset));
3115 context = tmp;
3116 }
3117 // If no outer scope calls eval, we do not need to check more
3118 // context extensions.
3119 if (!s->outer_scope_calls_eval() || s->is_eval_scope()) break;
3120 s = s->outer_scope();
3121 }
3122
3123 if (s->is_eval_scope()) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003124 frame_->SpillAll();
ager@chromium.org381abbb2009-02-25 13:23:22 +00003125 Label next, fast;
ager@chromium.org357bf652010-04-12 11:30:10 +00003126 __ Move(tmp, context);
ager@chromium.org381abbb2009-02-25 13:23:22 +00003127 __ bind(&next);
3128 // Terminate at global context.
3129 __ ldr(tmp2, FieldMemOperand(tmp, HeapObject::kMapOffset));
ager@chromium.orgab99eea2009-08-25 07:05:41 +00003130 __ LoadRoot(ip, Heap::kGlobalContextMapRootIndex);
3131 __ cmp(tmp2, ip);
ager@chromium.org381abbb2009-02-25 13:23:22 +00003132 __ b(eq, &fast);
3133 // Check that extension is NULL.
3134 __ ldr(tmp2, ContextOperand(tmp, Context::EXTENSION_INDEX));
3135 __ tst(tmp2, tmp2);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003136 slow->Branch(ne);
ager@chromium.org381abbb2009-02-25 13:23:22 +00003137 // Load next context in chain.
3138 __ ldr(tmp, ContextOperand(tmp, Context::CLOSURE_INDEX));
3139 __ ldr(tmp, FieldMemOperand(tmp, JSFunction::kContextOffset));
3140 __ b(&next);
3141 __ bind(&fast);
3142 }
3143
ager@chromium.org381abbb2009-02-25 13:23:22 +00003144 // Load the global object.
3145 LoadGlobal();
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00003146 // Setup the name register and call load IC.
ager@chromium.orgac091b72010-05-05 07:34:42 +00003147 frame_->CallLoadIC(slot->var()->name(),
3148 typeof_state == INSIDE_TYPEOF
3149 ? RelocInfo::CODE_TARGET
3150 : RelocInfo::CODE_TARGET_CONTEXT);
ager@chromium.org381abbb2009-02-25 13:23:22 +00003151}
3152
3153
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00003154void CodeGenerator::EmitDynamicLoadFromSlotFastCase(Slot* slot,
3155 TypeofState typeof_state,
3156 JumpTarget* slow,
3157 JumpTarget* done) {
3158 // Generate fast-case code for variables that might be shadowed by
3159 // eval-introduced variables. Eval is used a lot without
3160 // introducing variables. In those cases, we do not want to
3161 // perform a runtime call for all variables in the scope
3162 // containing the eval.
3163 if (slot->var()->mode() == Variable::DYNAMIC_GLOBAL) {
3164 LoadFromGlobalSlotCheckExtensions(slot, typeof_state, slow);
3165 frame_->SpillAll();
3166 done->Jump();
3167
3168 } else if (slot->var()->mode() == Variable::DYNAMIC_LOCAL) {
3169 frame_->SpillAll();
3170 Slot* potential_slot = slot->var()->local_if_not_shadowed()->slot();
3171 Expression* rewrite = slot->var()->local_if_not_shadowed()->rewrite();
3172 if (potential_slot != NULL) {
3173 // Generate fast case for locals that rewrite to slots.
3174 __ ldr(r0,
3175 ContextSlotOperandCheckExtensions(potential_slot,
3176 r1,
3177 r2,
3178 slow));
3179 if (potential_slot->var()->mode() == Variable::CONST) {
3180 __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
3181 __ cmp(r0, ip);
3182 __ LoadRoot(r0, Heap::kUndefinedValueRootIndex, eq);
3183 }
3184 done->Jump();
3185 } else if (rewrite != NULL) {
3186 // Generate fast case for argument loads.
3187 Property* property = rewrite->AsProperty();
3188 if (property != NULL) {
3189 VariableProxy* obj_proxy = property->obj()->AsVariableProxy();
3190 Literal* key_literal = property->key()->AsLiteral();
3191 if (obj_proxy != NULL &&
3192 key_literal != NULL &&
3193 obj_proxy->IsArguments() &&
3194 key_literal->handle()->IsSmi()) {
3195 // Load arguments object if there are no eval-introduced
3196 // variables. Then load the argument from the arguments
3197 // object using keyed load.
3198 __ ldr(r0,
3199 ContextSlotOperandCheckExtensions(obj_proxy->var()->slot(),
3200 r1,
3201 r2,
3202 slow));
3203 frame_->EmitPush(r0);
3204 __ mov(r1, Operand(key_literal->handle()));
3205 frame_->EmitPush(r1);
3206 EmitKeyedLoad();
3207 done->Jump();
3208 }
3209 }
3210 }
3211 }
3212}
3213
3214
ager@chromium.org7c537e22008-10-16 08:43:32 +00003215void CodeGenerator::VisitSlot(Slot* 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_, "[ Slot");
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00003220 LoadFromSlotCheckForArguments(node, NOT_INSIDE_TYPEOF);
ager@chromium.orgac091b72010-05-05 07:34:42 +00003221 ASSERT_EQ(original_height + 1, frame_->height());
ager@chromium.org7c537e22008-10-16 08:43:32 +00003222}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003223
ager@chromium.org7c537e22008-10-16 08:43:32 +00003224
3225void CodeGenerator::VisitVariableProxy(VariableProxy* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003226#ifdef DEBUG
3227 int original_height = frame_->height();
3228#endif
ager@chromium.org7c537e22008-10-16 08:43:32 +00003229 Comment cmnt(masm_, "[ VariableProxy");
3230
3231 Variable* var = node->var();
3232 Expression* expr = var->rewrite();
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00003233 if (expr != NULL) {
3234 Visit(expr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003235 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +00003236 ASSERT(var->is_global());
3237 Reference ref(this, node);
ager@chromium.org357bf652010-04-12 11:30:10 +00003238 ref.GetValue();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003239 }
ager@chromium.orgac091b72010-05-05 07:34:42 +00003240 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003241}
3242
3243
ager@chromium.org7c537e22008-10-16 08:43:32 +00003244void CodeGenerator::VisitLiteral(Literal* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003245#ifdef DEBUG
3246 int original_height = frame_->height();
3247#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003248 Comment cmnt(masm_, "[ Literal");
ager@chromium.org357bf652010-04-12 11:30:10 +00003249 Register reg = frame_->GetTOSRegister();
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00003250 bool is_smi = node->handle()->IsSmi();
ager@chromium.org357bf652010-04-12 11:30:10 +00003251 __ mov(reg, Operand(node->handle()));
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00003252 frame_->EmitPush(reg, is_smi ? TypeInfo::Smi() : TypeInfo::Unknown());
ager@chromium.orgac091b72010-05-05 07:34:42 +00003253 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003254}
3255
3256
ager@chromium.org7c537e22008-10-16 08:43:32 +00003257void CodeGenerator::VisitRegExpLiteral(RegExpLiteral* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003258#ifdef DEBUG
3259 int original_height = frame_->height();
3260#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003261 Comment cmnt(masm_, "[ RexExp Literal");
3262
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003263 Register tmp = VirtualFrame::scratch0();
3264 // Free up a TOS register that can be used to push the literal.
3265 Register literal = frame_->GetTOSRegister();
3266
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003267 // Retrieve the literal array and check the allocated entry.
3268
3269 // Load the function of this activation.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003270 __ ldr(tmp, frame_->Function());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003271
3272 // Load the literals array of the function.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003273 __ ldr(tmp, FieldMemOperand(tmp, JSFunction::kLiteralsOffset));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003274
3275 // Load the literal at the ast saved index.
3276 int literal_offset =
3277 FixedArray::kHeaderSize + node->literal_index() * kPointerSize;
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003278 __ ldr(literal, FieldMemOperand(tmp, literal_offset));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003279
kasperl@chromium.org71affb52009-05-26 05:44:31 +00003280 JumpTarget done;
ager@chromium.orgab99eea2009-08-25 07:05:41 +00003281 __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003282 __ cmp(literal, ip);
3283 // This branch locks the virtual frame at the done label to match the
3284 // one we have here, where the literal register is not on the stack and
3285 // nothing is spilled.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003286 done.Branch(ne);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003287
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003288 // If the entry is undefined we call the runtime system to compute
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003289 // the literal.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003290 // literal array (0)
3291 frame_->EmitPush(tmp);
3292 // literal index (1)
3293 frame_->EmitPush(Operand(Smi::FromInt(node->literal_index())));
3294 // RegExp pattern (2)
3295 frame_->EmitPush(Operand(node->pattern()));
3296 // RegExp flags (3)
3297 frame_->EmitPush(Operand(node->flags()));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003298 frame_->CallRuntime(Runtime::kMaterializeRegExpLiteral, 4);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003299 __ Move(literal, r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003300
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003301 // This call to bind will get us back to the virtual frame we had before
3302 // where things are not spilled and the literal register is not on the stack.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003303 done.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003304 // Push the literal.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003305 frame_->EmitPush(literal);
ager@chromium.orgac091b72010-05-05 07:34:42 +00003306 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003307}
3308
3309
ager@chromium.org7c537e22008-10-16 08:43:32 +00003310void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003311#ifdef DEBUG
3312 int original_height = frame_->height();
3313#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003314 Comment cmnt(masm_, "[ ObjectLiteral");
3315
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003316 Register literal = frame_->GetTOSRegister();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003317 // Load the function of this activation.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003318 __ ldr(literal, frame_->Function());
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003319 // Literal array.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003320 __ ldr(literal, FieldMemOperand(literal, JSFunction::kLiteralsOffset));
3321 frame_->EmitPush(literal);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003322 // Literal index.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003323 frame_->EmitPush(Operand(Smi::FromInt(node->literal_index())));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003324 // Constant properties.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003325 frame_->EmitPush(Operand(node->constant_properties()));
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00003326 // Should the object literal have fast elements?
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003327 frame_->EmitPush(Operand(Smi::FromInt(node->fast_elements() ? 1 : 0)));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003328 if (node->depth() > 1) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00003329 frame_->CallRuntime(Runtime::kCreateObjectLiteral, 4);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003330 } else {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00003331 frame_->CallRuntime(Runtime::kCreateObjectLiteralShallow, 4);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003332 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003333 frame_->EmitPush(r0); // save the result
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003334 for (int i = 0; i < node->properties()->length(); i++) {
ager@chromium.org5c838252010-02-19 08:53:10 +00003335 // At the start of each iteration, the top of stack contains
3336 // the newly created object literal.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003337 ObjectLiteral::Property* property = node->properties()->at(i);
3338 Literal* key = property->key();
3339 Expression* value = property->value();
3340 switch (property->kind()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003341 case ObjectLiteral::Property::CONSTANT:
3342 break;
3343 case ObjectLiteral::Property::MATERIALIZED_LITERAL:
3344 if (CompileTimeValue::IsCompileTimeValue(property->value())) break;
3345 // else fall through
ager@chromium.org5c838252010-02-19 08:53:10 +00003346 case ObjectLiteral::Property::COMPUTED:
3347 if (key->handle()->IsSymbol()) {
3348 Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003349 Load(value);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003350 frame_->PopToR0();
3351 // Fetch the object literal.
3352 frame_->SpillAllButCopyTOSToR1();
ager@chromium.org5c838252010-02-19 08:53:10 +00003353 __ mov(r2, Operand(key->handle()));
ager@chromium.org5c838252010-02-19 08:53:10 +00003354 frame_->CallCodeObject(ic, RelocInfo::CODE_TARGET, 0);
3355 break;
3356 }
3357 // else fall through
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003358 case ObjectLiteral::Property::PROTOTYPE: {
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003359 frame_->Dup();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003360 Load(key);
3361 Load(value);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003362 frame_->CallRuntime(Runtime::kSetProperty, 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003363 break;
3364 }
3365 case ObjectLiteral::Property::SETTER: {
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003366 frame_->Dup();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003367 Load(key);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003368 frame_->EmitPush(Operand(Smi::FromInt(1)));
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003369 Load(value);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003370 frame_->CallRuntime(Runtime::kDefineAccessor, 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003371 break;
3372 }
3373 case ObjectLiteral::Property::GETTER: {
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003374 frame_->Dup();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003375 Load(key);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003376 frame_->EmitPush(Operand(Smi::FromInt(0)));
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003377 Load(value);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003378 frame_->CallRuntime(Runtime::kDefineAccessor, 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003379 break;
3380 }
3381 }
3382 }
ager@chromium.orgac091b72010-05-05 07:34:42 +00003383 ASSERT_EQ(original_height + 1, frame_->height());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003384}
3385
3386
ager@chromium.org7c537e22008-10-16 08:43:32 +00003387void CodeGenerator::VisitArrayLiteral(ArrayLiteral* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003388#ifdef DEBUG
3389 int original_height = frame_->height();
3390#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003391 Comment cmnt(masm_, "[ ArrayLiteral");
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003392
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003393 Register tos = frame_->GetTOSRegister();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003394 // Load the function of this activation.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003395 __ ldr(tos, frame_->Function());
ager@chromium.org5c838252010-02-19 08:53:10 +00003396 // Load the literals array of the function.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003397 __ ldr(tos, FieldMemOperand(tos, JSFunction::kLiteralsOffset));
3398 frame_->EmitPush(tos);
3399 frame_->EmitPush(Operand(Smi::FromInt(node->literal_index())));
3400 frame_->EmitPush(Operand(node->constant_elements()));
ager@chromium.org5c838252010-02-19 08:53:10 +00003401 int length = node->values()->length();
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003402 if (node->depth() > 1) {
3403 frame_->CallRuntime(Runtime::kCreateArrayLiteral, 3);
ager@chromium.org5c838252010-02-19 08:53:10 +00003404 } else if (length > FastCloneShallowArrayStub::kMaximumLength) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003405 frame_->CallRuntime(Runtime::kCreateArrayLiteralShallow, 3);
ager@chromium.org5c838252010-02-19 08:53:10 +00003406 } else {
3407 FastCloneShallowArrayStub stub(length);
3408 frame_->CallStub(&stub, 3);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003409 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003410 frame_->EmitPush(r0); // save the result
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003411 // r0: created object literal
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003412
3413 // Generate code to set the elements in the array that are not
3414 // literals.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003415 for (int i = 0; i < node->values()->length(); i++) {
3416 Expression* value = node->values()->at(i);
3417
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003418 // If value is a literal the property value is already set in the
3419 // boilerplate object.
3420 if (value->AsLiteral() != NULL) continue;
3421 // If value is a materialized literal the property value is already set
3422 // in the boilerplate object if it is simple.
3423 if (CompileTimeValue::IsCompileTimeValue(value)) continue;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003424
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003425 // The property must be set by generated code.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003426 Load(value);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003427 frame_->PopToR0();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003428 // Fetch the object literal.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003429 frame_->SpillAllButCopyTOSToR1();
3430
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003431 // Get the elements array.
3432 __ ldr(r1, FieldMemOperand(r1, JSObject::kElementsOffset));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003433
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003434 // Write to the indexed properties array.
kasperl@chromium.orge959c182009-07-27 08:59:04 +00003435 int offset = i * kPointerSize + FixedArray::kHeaderSize;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003436 __ str(r0, FieldMemOperand(r1, offset));
3437
3438 // Update the write barrier for the array address.
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00003439 __ RecordWrite(r1, Operand(offset), r3, r2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003440 }
ager@chromium.orgac091b72010-05-05 07:34:42 +00003441 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003442}
3443
3444
ager@chromium.org32912102009-01-16 10:38:43 +00003445void CodeGenerator::VisitCatchExtensionObject(CatchExtensionObject* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003446#ifdef DEBUG
3447 int original_height = frame_->height();
3448#endif
ager@chromium.org32912102009-01-16 10:38:43 +00003449 // Call runtime routine to allocate the catch extension object and
3450 // assign the exception value to the catch variable.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003451 Comment cmnt(masm_, "[ CatchExtensionObject");
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003452 Load(node->key());
3453 Load(node->value());
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00003454 frame_->CallRuntime(Runtime::kCreateCatchExtensionObject, 2);
3455 frame_->EmitPush(r0);
ager@chromium.orgac091b72010-05-05 07:34:42 +00003456 ASSERT_EQ(original_height + 1, frame_->height());
3457}
3458
3459
3460void CodeGenerator::EmitSlotAssignment(Assignment* node) {
3461#ifdef DEBUG
3462 int original_height = frame_->height();
3463#endif
3464 Comment cmnt(masm(), "[ Variable Assignment");
3465 Variable* var = node->target()->AsVariableProxy()->AsVariable();
3466 ASSERT(var != NULL);
3467 Slot* slot = var->slot();
3468 ASSERT(slot != NULL);
3469
3470 // Evaluate the right-hand side.
3471 if (node->is_compound()) {
3472 // For a compound assignment the right-hand side is a binary operation
3473 // between the current property value and the actual right-hand side.
3474 LoadFromSlotCheckForArguments(slot, NOT_INSIDE_TYPEOF);
3475
3476 // Perform the binary operation.
3477 Literal* literal = node->value()->AsLiteral();
3478 bool overwrite_value =
3479 (node->value()->AsBinaryOperation() != NULL &&
3480 node->value()->AsBinaryOperation()->ResultOverwriteAllowed());
3481 if (literal != NULL && literal->handle()->IsSmi()) {
3482 SmiOperation(node->binary_op(),
3483 literal->handle(),
3484 false,
3485 overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE);
3486 } else {
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00003487 GenerateInlineSmi inline_smi =
3488 loop_nesting() > 0 ? GENERATE_INLINE_SMI : DONT_GENERATE_INLINE_SMI;
3489 if (literal != NULL) {
3490 ASSERT(!literal->handle()->IsSmi());
3491 inline_smi = DONT_GENERATE_INLINE_SMI;
3492 }
ager@chromium.orgac091b72010-05-05 07:34:42 +00003493 Load(node->value());
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00003494 GenericBinaryOperation(node->binary_op(),
3495 overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE,
3496 inline_smi);
ager@chromium.orgac091b72010-05-05 07:34:42 +00003497 }
3498 } else {
3499 Load(node->value());
3500 }
3501
3502 // Perform the assignment.
3503 if (var->mode() != Variable::CONST || node->op() == Token::INIT_CONST) {
3504 CodeForSourcePosition(node->position());
3505 StoreToSlot(slot,
3506 node->op() == Token::INIT_CONST ? CONST_INIT : NOT_CONST_INIT);
3507 }
3508 ASSERT_EQ(original_height + 1, frame_->height());
3509}
3510
3511
3512void CodeGenerator::EmitNamedPropertyAssignment(Assignment* node) {
3513#ifdef DEBUG
3514 int original_height = frame_->height();
3515#endif
3516 Comment cmnt(masm(), "[ Named Property Assignment");
3517 Variable* var = node->target()->AsVariableProxy()->AsVariable();
3518 Property* prop = node->target()->AsProperty();
3519 ASSERT(var == NULL || (prop == NULL && var->is_global()));
3520
3521 // Initialize name and evaluate the receiver sub-expression if necessary. If
3522 // the receiver is trivial it is not placed on the stack at this point, but
3523 // loaded whenever actually needed.
3524 Handle<String> name;
3525 bool is_trivial_receiver = false;
3526 if (var != NULL) {
3527 name = var->name();
3528 } else {
3529 Literal* lit = prop->key()->AsLiteral();
3530 ASSERT_NOT_NULL(lit);
3531 name = Handle<String>::cast(lit->handle());
3532 // Do not materialize the receiver on the frame if it is trivial.
3533 is_trivial_receiver = prop->obj()->IsTrivial();
3534 if (!is_trivial_receiver) Load(prop->obj());
3535 }
3536
3537 // Change to slow case in the beginning of an initialization block to
3538 // avoid the quadratic behavior of repeatedly adding fast properties.
3539 if (node->starts_initialization_block()) {
3540 // Initialization block consists of assignments of the form expr.x = ..., so
3541 // this will never be an assignment to a variable, so there must be a
3542 // receiver object.
3543 ASSERT_EQ(NULL, var);
3544 if (is_trivial_receiver) {
3545 Load(prop->obj());
3546 } else {
3547 frame_->Dup();
3548 }
3549 frame_->CallRuntime(Runtime::kToSlowProperties, 1);
3550 }
3551
3552 // Change to fast case at the end of an initialization block. To prepare for
3553 // that add an extra copy of the receiver to the frame, so that it can be
3554 // converted back to fast case after the assignment.
3555 if (node->ends_initialization_block() && !is_trivial_receiver) {
3556 frame_->Dup();
3557 }
3558
3559 // Stack layout:
3560 // [tos] : receiver (only materialized if non-trivial)
3561 // [tos+1] : receiver if at the end of an initialization block
3562
3563 // Evaluate the right-hand side.
3564 if (node->is_compound()) {
3565 // For a compound assignment the right-hand side is a binary operation
3566 // between the current property value and the actual right-hand side.
3567 if (is_trivial_receiver) {
3568 Load(prop->obj());
3569 } else if (var != NULL) {
3570 LoadGlobal();
3571 } else {
3572 frame_->Dup();
3573 }
3574 EmitNamedLoad(name, var != NULL);
ager@chromium.orgac091b72010-05-05 07:34:42 +00003575
3576 // Perform the binary operation.
3577 Literal* literal = node->value()->AsLiteral();
3578 bool overwrite_value =
3579 (node->value()->AsBinaryOperation() != NULL &&
3580 node->value()->AsBinaryOperation()->ResultOverwriteAllowed());
3581 if (literal != NULL && literal->handle()->IsSmi()) {
3582 SmiOperation(node->binary_op(),
3583 literal->handle(),
3584 false,
3585 overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE);
3586 } else {
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00003587 GenerateInlineSmi inline_smi =
3588 loop_nesting() > 0 ? GENERATE_INLINE_SMI : DONT_GENERATE_INLINE_SMI;
3589 if (literal != NULL) {
3590 ASSERT(!literal->handle()->IsSmi());
3591 inline_smi = DONT_GENERATE_INLINE_SMI;
3592 }
ager@chromium.orgac091b72010-05-05 07:34:42 +00003593 Load(node->value());
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00003594 GenericBinaryOperation(node->binary_op(),
3595 overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE,
3596 inline_smi);
ager@chromium.orgac091b72010-05-05 07:34:42 +00003597 }
3598 } else {
3599 // For non-compound assignment just load the right-hand side.
3600 Load(node->value());
3601 }
3602
3603 // Stack layout:
3604 // [tos] : value
3605 // [tos+1] : receiver (only materialized if non-trivial)
3606 // [tos+2] : receiver if at the end of an initialization block
3607
3608 // Perform the assignment. It is safe to ignore constants here.
3609 ASSERT(var == NULL || var->mode() != Variable::CONST);
3610 ASSERT_NE(Token::INIT_CONST, node->op());
3611 if (is_trivial_receiver) {
3612 // Load the receiver and swap with the value.
3613 Load(prop->obj());
3614 Register t0 = frame_->PopToRegister();
3615 Register t1 = frame_->PopToRegister(t0);
3616 frame_->EmitPush(t0);
3617 frame_->EmitPush(t1);
3618 }
3619 CodeForSourcePosition(node->position());
3620 bool is_contextual = (var != NULL);
3621 EmitNamedStore(name, is_contextual);
3622 frame_->EmitPush(r0);
3623
3624 // Change to fast case at the end of an initialization block.
3625 if (node->ends_initialization_block()) {
3626 ASSERT_EQ(NULL, var);
3627 // The argument to the runtime call is the receiver.
3628 if (is_trivial_receiver) {
3629 Load(prop->obj());
3630 } else {
3631 // A copy of the receiver is below the value of the assignment. Swap
3632 // the receiver and the value of the assignment expression.
3633 Register t0 = frame_->PopToRegister();
3634 Register t1 = frame_->PopToRegister(t0);
3635 frame_->EmitPush(t0);
3636 frame_->EmitPush(t1);
3637 }
3638 frame_->CallRuntime(Runtime::kToFastProperties, 1);
3639 }
3640
3641 // Stack layout:
3642 // [tos] : result
3643
3644 ASSERT_EQ(original_height + 1, frame_->height());
3645}
3646
3647
3648void CodeGenerator::EmitKeyedPropertyAssignment(Assignment* node) {
3649#ifdef DEBUG
3650 int original_height = frame_->height();
3651#endif
3652 Comment cmnt(masm_, "[ Keyed Property Assignment");
3653 Property* prop = node->target()->AsProperty();
3654 ASSERT_NOT_NULL(prop);
3655
3656 // Evaluate the receiver subexpression.
3657 Load(prop->obj());
3658
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00003659 WriteBarrierCharacter wb_info;
3660
ager@chromium.orgac091b72010-05-05 07:34:42 +00003661 // Change to slow case in the beginning of an initialization block to
3662 // avoid the quadratic behavior of repeatedly adding fast properties.
3663 if (node->starts_initialization_block()) {
3664 frame_->Dup();
3665 frame_->CallRuntime(Runtime::kToSlowProperties, 1);
3666 }
3667
3668 // Change to fast case at the end of an initialization block. To prepare for
3669 // that add an extra copy of the receiver to the frame, so that it can be
3670 // converted back to fast case after the assignment.
3671 if (node->ends_initialization_block()) {
3672 frame_->Dup();
3673 }
3674
3675 // Evaluate the key subexpression.
3676 Load(prop->key());
3677
3678 // Stack layout:
3679 // [tos] : key
3680 // [tos+1] : receiver
3681 // [tos+2] : receiver if at the end of an initialization block
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00003682 //
ager@chromium.orgac091b72010-05-05 07:34:42 +00003683 // Evaluate the right-hand side.
3684 if (node->is_compound()) {
3685 // For a compound assignment the right-hand side is a binary operation
3686 // between the current property value and the actual right-hand side.
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00003687 // Duplicate receiver and key for loading the current property value.
3688 frame_->Dup2();
ager@chromium.orgac091b72010-05-05 07:34:42 +00003689 EmitKeyedLoad();
3690 frame_->EmitPush(r0);
3691
3692 // Perform the binary operation.
3693 Literal* literal = node->value()->AsLiteral();
3694 bool overwrite_value =
3695 (node->value()->AsBinaryOperation() != NULL &&
3696 node->value()->AsBinaryOperation()->ResultOverwriteAllowed());
3697 if (literal != NULL && literal->handle()->IsSmi()) {
3698 SmiOperation(node->binary_op(),
3699 literal->handle(),
3700 false,
3701 overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE);
3702 } else {
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00003703 GenerateInlineSmi inline_smi =
3704 loop_nesting() > 0 ? GENERATE_INLINE_SMI : DONT_GENERATE_INLINE_SMI;
3705 if (literal != NULL) {
3706 ASSERT(!literal->handle()->IsSmi());
3707 inline_smi = DONT_GENERATE_INLINE_SMI;
3708 }
ager@chromium.orgac091b72010-05-05 07:34:42 +00003709 Load(node->value());
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00003710 GenericBinaryOperation(node->binary_op(),
3711 overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE,
3712 inline_smi);
ager@chromium.orgac091b72010-05-05 07:34:42 +00003713 }
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00003714 wb_info = node->type()->IsLikelySmi() ? LIKELY_SMI : UNLIKELY_SMI;
ager@chromium.orgac091b72010-05-05 07:34:42 +00003715 } else {
3716 // For non-compound assignment just load the right-hand side.
3717 Load(node->value());
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00003718 wb_info = node->value()->AsLiteral() != NULL ?
3719 NEVER_NEWSPACE :
3720 (node->value()->type()->IsLikelySmi() ? LIKELY_SMI : UNLIKELY_SMI);
ager@chromium.orgac091b72010-05-05 07:34:42 +00003721 }
3722
3723 // Stack layout:
3724 // [tos] : value
3725 // [tos+1] : key
3726 // [tos+2] : receiver
3727 // [tos+3] : receiver if at the end of an initialization block
3728
3729 // Perform the assignment. It is safe to ignore constants here.
3730 ASSERT(node->op() != Token::INIT_CONST);
3731 CodeForSourcePosition(node->position());
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00003732 EmitKeyedStore(prop->key()->type(), wb_info);
ager@chromium.orgac091b72010-05-05 07:34:42 +00003733 frame_->EmitPush(r0);
3734
3735 // Stack layout:
3736 // [tos] : result
3737 // [tos+1] : receiver if at the end of an initialization block
3738
3739 // Change to fast case at the end of an initialization block.
3740 if (node->ends_initialization_block()) {
3741 // The argument to the runtime call is the extra copy of the receiver,
3742 // which is below the value of the assignment. Swap the receiver and
3743 // the value of the assignment expression.
3744 Register t0 = frame_->PopToRegister();
3745 Register t1 = frame_->PopToRegister(t0);
3746 frame_->EmitPush(t1);
3747 frame_->EmitPush(t0);
3748 frame_->CallRuntime(Runtime::kToFastProperties, 1);
3749 }
3750
3751 // Stack layout:
3752 // [tos] : result
3753
3754 ASSERT_EQ(original_height + 1, frame_->height());
ager@chromium.org32912102009-01-16 10:38:43 +00003755}
3756
3757
ager@chromium.org7c537e22008-10-16 08:43:32 +00003758void CodeGenerator::VisitAssignment(Assignment* node) {
ager@chromium.org357bf652010-04-12 11:30:10 +00003759 VirtualFrame::RegisterAllocationScope scope(this);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003760#ifdef DEBUG
3761 int original_height = frame_->height();
3762#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003763 Comment cmnt(masm_, "[ Assignment");
mads.s.ager31e71382008-08-13 09:32:07 +00003764
ager@chromium.orgac091b72010-05-05 07:34:42 +00003765 Variable* var = node->target()->AsVariableProxy()->AsVariable();
3766 Property* prop = node->target()->AsProperty();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003767
ager@chromium.orgac091b72010-05-05 07:34:42 +00003768 if (var != NULL && !var->is_global()) {
3769 EmitSlotAssignment(node);
mads.s.ager31e71382008-08-13 09:32:07 +00003770
ager@chromium.orgac091b72010-05-05 07:34:42 +00003771 } else if ((prop != NULL && prop->key()->IsPropertyName()) ||
3772 (var != NULL && var->is_global())) {
3773 // Properties whose keys are property names and global variables are
3774 // treated as named property references. We do not need to consider
3775 // global 'this' because it is not a valid left-hand side.
3776 EmitNamedPropertyAssignment(node);
3777
3778 } else if (prop != NULL) {
3779 // Other properties (including rewritten parameters for a function that
3780 // uses arguments) are keyed property assignments.
3781 EmitKeyedPropertyAssignment(node);
3782
3783 } else {
3784 // Invalid left-hand side.
3785 Load(node->target());
3786 frame_->CallRuntime(Runtime::kThrowReferenceError, 1);
3787 // The runtime call doesn't actually return but the code generator will
3788 // still generate code and expects a certain frame height.
3789 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003790 }
ager@chromium.orgac091b72010-05-05 07:34:42 +00003791 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003792}
3793
3794
ager@chromium.org7c537e22008-10-16 08:43:32 +00003795void CodeGenerator::VisitThrow(Throw* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003796#ifdef DEBUG
3797 int original_height = frame_->height();
3798#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003799 Comment cmnt(masm_, "[ Throw");
3800
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003801 Load(node->exception());
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00003802 CodeForSourcePosition(node->position());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003803 frame_->CallRuntime(Runtime::kThrow, 1);
3804 frame_->EmitPush(r0);
ager@chromium.orgac091b72010-05-05 07:34:42 +00003805 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003806}
3807
3808
ager@chromium.org7c537e22008-10-16 08:43:32 +00003809void CodeGenerator::VisitProperty(Property* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003810#ifdef DEBUG
3811 int original_height = frame_->height();
3812#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003813 Comment cmnt(masm_, "[ Property");
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00003814
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003815 { Reference property(this, node);
ager@chromium.org357bf652010-04-12 11:30:10 +00003816 property.GetValue();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003817 }
ager@chromium.orgac091b72010-05-05 07:34:42 +00003818 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003819}
3820
3821
ager@chromium.org7c537e22008-10-16 08:43:32 +00003822void CodeGenerator::VisitCall(Call* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003823#ifdef DEBUG
3824 int original_height = frame_->height();
3825#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003826 Comment cmnt(masm_, "[ Call");
3827
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003828 Expression* function = node->expression();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003829 ZoneList<Expression*>* args = node->arguments();
3830
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003831 // Standard function call.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003832 // Check if the function is a variable or a property.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003833 Variable* var = function->AsVariableProxy()->AsVariable();
3834 Property* property = function->AsProperty();
3835
3836 // ------------------------------------------------------------------------
3837 // Fast-case: Use inline caching.
3838 // ---
3839 // According to ECMA-262, section 11.2.3, page 44, the function to call
3840 // must be resolved after the arguments have been evaluated. The IC code
3841 // automatically handles this by loading the arguments before the function
3842 // is resolved in cache misses (this also holds for megamorphic calls).
3843 // ------------------------------------------------------------------------
3844
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003845 if (var != NULL && var->is_possibly_eval()) {
3846 // ----------------------------------
3847 // JavaScript example: 'eval(arg)' // eval is not known to be shadowed
3848 // ----------------------------------
3849
3850 // In a call to eval, we first call %ResolvePossiblyDirectEval to
3851 // resolve the function we need to call and the receiver of the
3852 // call. Then we call the resolved function using the given
3853 // arguments.
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00003854
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003855 // Prepare stack for call to resolved function.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003856 Load(function);
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00003857
3858 // Allocate a frame slot for the receiver.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003859 frame_->EmitPushRoot(Heap::kUndefinedValueRootIndex);
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00003860
3861 // Load the arguments.
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003862 int arg_count = args->length();
3863 for (int i = 0; i < arg_count; i++) {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003864 Load(args->at(i));
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003865 }
3866
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003867 VirtualFrame::SpilledScope spilled_scope(frame_);
3868
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00003869 // If we know that eval can only be shadowed by eval-introduced
3870 // variables we attempt to load the global eval function directly
3871 // in generated code. If we succeed, there is no need to perform a
3872 // context lookup in the runtime system.
3873 JumpTarget done;
3874 if (var->slot() != NULL && var->mode() == Variable::DYNAMIC_GLOBAL) {
3875 ASSERT(var->slot()->type() == Slot::LOOKUP);
3876 JumpTarget slow;
3877 // Prepare the stack for the call to
3878 // ResolvePossiblyDirectEvalNoLookup by pushing the loaded
3879 // function, the first argument to the eval call and the
3880 // receiver.
3881 LoadFromGlobalSlotCheckExtensions(var->slot(),
3882 NOT_INSIDE_TYPEOF,
3883 &slow);
3884 frame_->EmitPush(r0);
3885 if (arg_count > 0) {
3886 __ ldr(r1, MemOperand(sp, arg_count * kPointerSize));
3887 frame_->EmitPush(r1);
3888 } else {
3889 frame_->EmitPush(r2);
3890 }
3891 __ ldr(r1, frame_->Receiver());
3892 frame_->EmitPush(r1);
3893
3894 frame_->CallRuntime(Runtime::kResolvePossiblyDirectEvalNoLookup, 3);
3895
3896 done.Jump();
3897 slow.Bind();
3898 }
3899
3900 // Prepare the stack for the call to ResolvePossiblyDirectEval by
3901 // pushing the loaded function, the first argument to the eval
3902 // call and the receiver.
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003903 __ ldr(r1, MemOperand(sp, arg_count * kPointerSize + kPointerSize));
3904 frame_->EmitPush(r1);
3905 if (arg_count > 0) {
3906 __ ldr(r1, MemOperand(sp, arg_count * kPointerSize));
3907 frame_->EmitPush(r1);
3908 } else {
3909 frame_->EmitPush(r2);
3910 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003911 __ ldr(r1, frame_->Receiver());
3912 frame_->EmitPush(r1);
3913
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003914 // Resolve the call.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003915 frame_->CallRuntime(Runtime::kResolvePossiblyDirectEval, 3);
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003916
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00003917 // If we generated fast-case code bind the jump-target where fast
3918 // and slow case merge.
3919 if (done.is_linked()) done.Bind();
3920
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003921 // Touch up stack with the right values for the function and the receiver.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003922 __ str(r0, MemOperand(sp, (arg_count + 1) * kPointerSize));
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003923 __ str(r1, MemOperand(sp, arg_count * kPointerSize));
3924
3925 // Call the function.
3926 CodeForSourcePosition(node->position());
3927
3928 InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003929 CallFunctionStub call_function(arg_count, in_loop, RECEIVER_MIGHT_BE_VALUE);
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003930 frame_->CallStub(&call_function, arg_count + 1);
3931
3932 __ ldr(cp, frame_->Context());
3933 // Remove the function from the stack.
3934 frame_->Drop();
3935 frame_->EmitPush(r0);
3936
3937 } else if (var != NULL && !var->is_this() && var->is_global()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003938 // ----------------------------------
3939 // JavaScript example: 'foo(1, 2, 3)' // foo is global
3940 // ----------------------------------
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003941 // Pass the global object as the receiver and let the IC stub
3942 // patch the stack to use the global proxy as 'this' in the
3943 // invoked function.
3944 LoadGlobal();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003945
3946 // Load the arguments.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003947 int arg_count = args->length();
3948 for (int i = 0; i < arg_count; i++) {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003949 Load(args->at(i));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003950 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003951
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003952 VirtualFrame::SpilledScope spilled_scope(frame_);
ager@chromium.org5c838252010-02-19 08:53:10 +00003953 // Setup the name register and call the IC initialization code.
3954 __ mov(r2, Operand(var->name()));
kasperl@chromium.org71affb52009-05-26 05:44:31 +00003955 InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP;
3956 Handle<Code> stub = ComputeCallInitialize(arg_count, in_loop);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00003957 CodeForSourcePosition(node->position());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003958 frame_->CallCodeObject(stub, RelocInfo::CODE_TARGET_CONTEXT,
3959 arg_count + 1);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00003960 __ ldr(cp, frame_->Context());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003961 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003962
3963 } else if (var != NULL && var->slot() != NULL &&
3964 var->slot()->type() == Slot::LOOKUP) {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003965 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003966 // ----------------------------------
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00003967 // JavaScript examples:
3968 //
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00003969 // with (obj) foo(1, 2, 3) // foo may be in obj.
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00003970 //
3971 // function f() {};
3972 // function g() {
3973 // eval(...);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00003974 // f(); // f could be in extension object.
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00003975 // }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003976 // ----------------------------------
3977
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00003978 // JumpTargets do not yet support merging frames so the frame must be
3979 // spilled when jumping to these targets.
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00003980 JumpTarget slow, done;
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00003981
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00003982 // Generate fast case for loading functions from slots that
3983 // correspond to local/global variables or arguments unless they
3984 // are shadowed by eval-introduced bindings.
3985 EmitDynamicLoadFromSlotFastCase(var->slot(),
3986 NOT_INSIDE_TYPEOF,
3987 &slow,
3988 &done);
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00003989
3990 slow.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003991 // Load the function
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003992 frame_->EmitPush(cp);
mads.s.ager31e71382008-08-13 09:32:07 +00003993 __ mov(r0, Operand(var->name()));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003994 frame_->EmitPush(r0);
3995 frame_->CallRuntime(Runtime::kLoadContextSlot, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003996 // r0: slot value; r1: receiver
3997
3998 // Load the receiver.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003999 frame_->EmitPush(r0); // function
4000 frame_->EmitPush(r1); // receiver
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004001
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00004002 // If fast case code has been generated, emit code to push the
4003 // function and receiver and have the slow path jump around this
4004 // code.
4005 if (done.is_linked()) {
4006 JumpTarget call;
4007 call.Jump();
4008 done.Bind();
4009 frame_->EmitPush(r0); // function
4010 LoadGlobalReceiver(r1); // receiver
4011 call.Bind();
4012 }
4013
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00004014 // Call the function. At this point, everything is spilled but the
4015 // function and receiver are in r0 and r1.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004016 CallWithArguments(args, NO_CALL_FUNCTION_FLAGS, node->position());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004017 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004018
4019 } else if (property != NULL) {
4020 // Check if the key is a literal string.
4021 Literal* literal = property->key()->AsLiteral();
4022
4023 if (literal != NULL && literal->handle()->IsSymbol()) {
4024 // ------------------------------------------------------------------
4025 // JavaScript example: 'object.foo(1, 2, 3)' or 'map["key"](1, 2, 3)'
4026 // ------------------------------------------------------------------
4027
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00004028 Handle<String> name = Handle<String>::cast(literal->handle());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004029
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00004030 if (ArgumentsMode() == LAZY_ARGUMENTS_ALLOCATION &&
4031 name->IsEqualTo(CStrVector("apply")) &&
4032 args->length() == 2 &&
4033 args->at(1)->AsVariableProxy() != NULL &&
4034 args->at(1)->AsVariableProxy()->IsArguments()) {
4035 // Use the optimized Function.prototype.apply that avoids
4036 // allocating lazily allocated arguments objects.
4037 CallApplyLazy(property->obj(),
4038 args->at(0),
4039 args->at(1)->AsVariableProxy(),
4040 node->position());
4041
4042 } else {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004043 Load(property->obj()); // Receiver.
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00004044 // Load the arguments.
4045 int arg_count = args->length();
4046 for (int i = 0; i < arg_count; i++) {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004047 Load(args->at(i));
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00004048 }
4049
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004050 VirtualFrame::SpilledScope spilled_scope(frame_);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00004051 // Set the name register and call the IC initialization code.
4052 __ mov(r2, Operand(name));
4053 InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP;
4054 Handle<Code> stub = ComputeCallInitialize(arg_count, in_loop);
4055 CodeForSourcePosition(node->position());
4056 frame_->CallCodeObject(stub, RelocInfo::CODE_TARGET, arg_count + 1);
4057 __ ldr(cp, frame_->Context());
4058 frame_->EmitPush(r0);
4059 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004060
4061 } else {
4062 // -------------------------------------------
4063 // JavaScript example: 'array[index](1, 2, 3)'
4064 // -------------------------------------------
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004065 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004066
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004067 Load(property->obj());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004068 if (property->is_synthetic()) {
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00004069 Load(property->key());
4070 EmitKeyedLoad();
4071 // Put the function below the receiver.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004072 // Use the global receiver.
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00004073 frame_->EmitPush(r0); // Function.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004074 LoadGlobalReceiver(r0);
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00004075 // Call the function.
4076 CallWithArguments(args, RECEIVER_MIGHT_BE_VALUE, node->position());
4077 frame_->EmitPush(r0);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004078 } else {
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00004079 // Load the arguments.
4080 int arg_count = args->length();
4081 for (int i = 0; i < arg_count; i++) {
4082 Load(args->at(i));
4083 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004084
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00004085 // Set the name register and call the IC initialization code.
4086 Load(property->key());
4087 frame_->EmitPop(r2); // Function name.
4088
4089 InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP;
4090 Handle<Code> stub = ComputeKeyedCallInitialize(arg_count, in_loop);
4091 CodeForSourcePosition(node->position());
4092 frame_->CallCodeObject(stub, RelocInfo::CODE_TARGET, arg_count + 1);
4093 __ ldr(cp, frame_->Context());
4094 frame_->EmitPush(r0);
4095 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004096 }
4097
4098 } else {
4099 // ----------------------------------
4100 // JavaScript example: 'foo(1, 2, 3)' // foo is not global
4101 // ----------------------------------
4102
4103 // Load the function.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004104 Load(function);
4105
4106 VirtualFrame::SpilledScope spilled_scope(frame_);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004107
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004108 // Pass the global proxy as the receiver.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004109 LoadGlobalReceiver(r0);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004110
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004111 // Call the function.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004112 CallWithArguments(args, NO_CALL_FUNCTION_FLAGS, node->position());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004113 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004114 }
ager@chromium.orgac091b72010-05-05 07:34:42 +00004115 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004116}
4117
4118
ager@chromium.org7c537e22008-10-16 08:43:32 +00004119void CodeGenerator::VisitCallNew(CallNew* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004120#ifdef DEBUG
4121 int original_height = frame_->height();
4122#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004123 Comment cmnt(masm_, "[ CallNew");
4124
4125 // According to ECMA-262, section 11.2.2, page 44, the function
4126 // expression in new calls must be evaluated before the
4127 // arguments. This is different from ordinary calls, where the
4128 // actual function to call is resolved after the arguments have been
4129 // evaluated.
4130
4131 // Compute function to call and use the global object as the
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004132 // receiver. There is no need to use the global proxy here because
4133 // it will always be replaced with a newly allocated object.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004134 Load(node->expression());
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004135 LoadGlobal();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004136
4137 // Push the arguments ("left-to-right") on the stack.
4138 ZoneList<Expression*>* args = node->arguments();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004139 int arg_count = args->length();
4140 for (int i = 0; i < arg_count; i++) {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004141 Load(args->at(i));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004142 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004143
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004144 VirtualFrame::SpilledScope spilled_scope(frame_);
4145
mads.s.ager31e71382008-08-13 09:32:07 +00004146 // r0: the number of arguments.
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00004147 __ mov(r0, Operand(arg_count));
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00004148 // Load the function into r1 as per calling convention.
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00004149 __ ldr(r1, frame_->ElementAt(arg_count + 1));
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00004150
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004151 // Call the construct call builtin that handles allocation and
4152 // constructor invocation.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004153 CodeForSourcePosition(node->position());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004154 Handle<Code> ic(Builtins::builtin(Builtins::JSConstructCall));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004155 frame_->CallCodeObject(ic, RelocInfo::CONSTRUCT_CALL, arg_count + 1);
mads.s.ager31e71382008-08-13 09:32:07 +00004156
4157 // Discard old TOS value and push r0 on the stack (same as Pop(), push(r0)).
ager@chromium.org3bf7b912008-11-17 09:09:45 +00004158 __ str(r0, frame_->Top());
ager@chromium.orgac091b72010-05-05 07:34:42 +00004159 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004160}
4161
4162
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004163void CodeGenerator::GenerateClassOf(ZoneList<Expression*>* args) {
ager@chromium.org357bf652010-04-12 11:30:10 +00004164 VirtualFrame::SpilledScope spilled_scope(frame_);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004165 ASSERT(args->length() == 1);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00004166 JumpTarget leave, null, function, non_function_constructor;
4167
4168 // Load the object into r0.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004169 Load(args->at(0));
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00004170 frame_->EmitPop(r0);
4171
4172 // If the object is a smi, we return null.
4173 __ tst(r0, Operand(kSmiTagMask));
4174 null.Branch(eq);
4175
4176 // Check that the object is a JS object but take special care of JS
4177 // functions to make sure they have 'Function' as their class.
4178 __ CompareObjectType(r0, r0, r1, FIRST_JS_OBJECT_TYPE);
4179 null.Branch(lt);
4180
4181 // As long as JS_FUNCTION_TYPE is the last instance type and it is
4182 // right after LAST_JS_OBJECT_TYPE, we can avoid checking for
4183 // LAST_JS_OBJECT_TYPE.
4184 ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
4185 ASSERT(JS_FUNCTION_TYPE == LAST_JS_OBJECT_TYPE + 1);
4186 __ cmp(r1, Operand(JS_FUNCTION_TYPE));
4187 function.Branch(eq);
4188
4189 // Check if the constructor in the map is a function.
4190 __ ldr(r0, FieldMemOperand(r0, Map::kConstructorOffset));
4191 __ CompareObjectType(r0, r1, r1, JS_FUNCTION_TYPE);
4192 non_function_constructor.Branch(ne);
4193
4194 // The r0 register now contains the constructor function. Grab the
4195 // instance class name from there.
4196 __ ldr(r0, FieldMemOperand(r0, JSFunction::kSharedFunctionInfoOffset));
4197 __ ldr(r0, FieldMemOperand(r0, SharedFunctionInfo::kInstanceClassNameOffset));
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004198 frame_->EmitPush(r0);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00004199 leave.Jump();
4200
4201 // Functions have class 'Function'.
4202 function.Bind();
4203 __ mov(r0, Operand(Factory::function_class_symbol()));
4204 frame_->EmitPush(r0);
4205 leave.Jump();
4206
4207 // Objects with a non-function constructor have class 'Object'.
4208 non_function_constructor.Bind();
4209 __ mov(r0, Operand(Factory::Object_symbol()));
4210 frame_->EmitPush(r0);
4211 leave.Jump();
4212
4213 // Non-JS objects have class null.
4214 null.Bind();
ager@chromium.orgab99eea2009-08-25 07:05:41 +00004215 __ LoadRoot(r0, Heap::kNullValueRootIndex);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00004216 frame_->EmitPush(r0);
4217
4218 // All done.
4219 leave.Bind();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004220}
4221
4222
ager@chromium.org7c537e22008-10-16 08:43:32 +00004223void CodeGenerator::GenerateValueOf(ZoneList<Expression*>* args) {
ager@chromium.org357bf652010-04-12 11:30:10 +00004224 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004225 ASSERT(args->length() == 1);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004226 JumpTarget leave;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004227 Load(args->at(0));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004228 frame_->EmitPop(r0); // r0 contains object.
mads.s.ager31e71382008-08-13 09:32:07 +00004229 // if (object->IsSmi()) return the object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004230 __ tst(r0, Operand(kSmiTagMask));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004231 leave.Branch(eq);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00004232 // It is a heap object - get map. If (!object->IsJSValue()) return the object.
4233 __ CompareObjectType(r0, r1, r1, JS_VALUE_TYPE);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004234 leave.Branch(ne);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004235 // Load the value.
4236 __ ldr(r0, FieldMemOperand(r0, JSValue::kValueOffset));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004237 leave.Bind();
4238 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004239}
4240
4241
ager@chromium.org7c537e22008-10-16 08:43:32 +00004242void CodeGenerator::GenerateSetValueOf(ZoneList<Expression*>* args) {
ager@chromium.org357bf652010-04-12 11:30:10 +00004243 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004244 ASSERT(args->length() == 2);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004245 JumpTarget leave;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004246 Load(args->at(0)); // Load the object.
4247 Load(args->at(1)); // Load the value.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004248 frame_->EmitPop(r0); // r0 contains value
4249 frame_->EmitPop(r1); // r1 contains object
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004250 // if (object->IsSmi()) return object.
4251 __ tst(r1, Operand(kSmiTagMask));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004252 leave.Branch(eq);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00004253 // It is a heap object - get map. If (!object->IsJSValue()) return the object.
4254 __ CompareObjectType(r1, r2, r2, JS_VALUE_TYPE);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004255 leave.Branch(ne);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004256 // Store the value.
4257 __ str(r0, FieldMemOperand(r1, JSValue::kValueOffset));
4258 // Update the write barrier.
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00004259 __ RecordWrite(r1, Operand(JSValue::kValueOffset - kHeapObjectTag), r2, r3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004260 // Leave.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004261 leave.Bind();
4262 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004263}
4264
4265
ager@chromium.org7c537e22008-10-16 08:43:32 +00004266void CodeGenerator::GenerateIsSmi(ZoneList<Expression*>* args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004267 ASSERT(args->length() == 1);
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004268 Load(args->at(0));
4269 Register reg = frame_->PopToRegister();
4270 __ tst(reg, Operand(kSmiTagMask));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004271 cc_reg_ = eq;
4272}
4273
4274
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004275void CodeGenerator::GenerateLog(ZoneList<Expression*>* args) {
4276 // See comment in CodeGenerator::GenerateLog in codegen-ia32.cc.
4277 ASSERT_EQ(args->length(), 3);
christian.plesner.hansen@gmail.comaca49682009-01-07 14:29:04 +00004278#ifdef ENABLE_LOGGING_AND_PROFILING
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004279 if (ShouldGenerateLog(args->at(0))) {
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004280 Load(args->at(1));
4281 Load(args->at(2));
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004282 frame_->CallRuntime(Runtime::kLog, 2);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004283 }
christian.plesner.hansen@gmail.comaca49682009-01-07 14:29:04 +00004284#endif
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004285 frame_->EmitPushRoot(Heap::kUndefinedValueRootIndex);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004286}
4287
4288
ager@chromium.org7c537e22008-10-16 08:43:32 +00004289void CodeGenerator::GenerateIsNonNegativeSmi(ZoneList<Expression*>* args) {
ager@chromium.orgc27e4e72008-09-04 13:52:27 +00004290 ASSERT(args->length() == 1);
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004291 Load(args->at(0));
4292 Register reg = frame_->PopToRegister();
4293 __ tst(reg, Operand(kSmiTagMask | 0x80000000u));
ager@chromium.orgc27e4e72008-09-04 13:52:27 +00004294 cc_reg_ = eq;
4295}
4296
4297
lrn@chromium.org32d961d2010-06-30 09:09:34 +00004298// Generates the Math.pow method.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004299void CodeGenerator::GenerateMathPow(ZoneList<Expression*>* args) {
4300 ASSERT(args->length() == 2);
4301 Load(args->at(0));
4302 Load(args->at(1));
lrn@chromium.org32d961d2010-06-30 09:09:34 +00004303
4304 if (!CpuFeatures::IsSupported(VFP3)) {
4305 frame_->CallRuntime(Runtime::kMath_pow, 2);
4306 frame_->EmitPush(r0);
4307 } else {
4308 CpuFeatures::Scope scope(VFP3);
4309 JumpTarget runtime, done;
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00004310 Label exponent_nonsmi, base_nonsmi, powi, not_minus_half, allocate_return;
lrn@chromium.org32d961d2010-06-30 09:09:34 +00004311
4312 Register scratch1 = VirtualFrame::scratch0();
4313 Register scratch2 = VirtualFrame::scratch1();
4314
4315 // Get base and exponent to registers.
4316 Register exponent = frame_->PopToRegister();
4317 Register base = frame_->PopToRegister(exponent);
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00004318 Register heap_number_map = no_reg;
lrn@chromium.org32d961d2010-06-30 09:09:34 +00004319
4320 // Set the frame for the runtime jump target. The code below jumps to the
4321 // jump target label so the frame needs to be established before that.
4322 ASSERT(runtime.entry_frame() == NULL);
4323 runtime.set_entry_frame(frame_);
4324
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00004325 __ BranchOnNotSmi(exponent, &exponent_nonsmi);
4326 __ BranchOnNotSmi(base, &base_nonsmi);
lrn@chromium.org32d961d2010-06-30 09:09:34 +00004327
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00004328 heap_number_map = r6;
4329 __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
4330
4331 // Exponent is a smi and base is a smi. Get the smi value into vfp register
4332 // d1.
4333 __ SmiToDoubleVFPRegister(base, d1, scratch1, s0);
4334 __ b(&powi);
4335
4336 __ bind(&base_nonsmi);
4337 // Exponent is smi and base is non smi. Get the double value from the base
4338 // into vfp register d1.
4339 __ ObjectToDoubleVFPRegister(base, d1,
4340 scratch1, scratch2, heap_number_map, s0,
4341 runtime.entry_label());
4342
4343 __ bind(&powi);
4344
4345 // Load 1.0 into d0.
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00004346 __ vmov(d0, 1.0);
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00004347
4348 // Get the absolute untagged value of the exponent and use that for the
4349 // calculation.
4350 __ mov(scratch1, Operand(exponent, ASR, kSmiTagSize), SetCC);
4351 __ rsb(scratch1, scratch1, Operand(0), LeaveCC, mi); // Negate if negative.
4352 __ vmov(d2, d0, mi); // 1.0 needed in d2 later if exponent is negative.
4353
4354 // Run through all the bits in the exponent. The result is calculated in d0
4355 // and d1 holds base^(bit^2).
4356 Label more_bits;
4357 __ bind(&more_bits);
4358 __ mov(scratch1, Operand(scratch1, LSR, 1), SetCC);
4359 __ vmul(d0, d0, d1, cs); // Multiply with base^(bit^2) if bit is set.
4360 __ vmul(d1, d1, d1, ne); // Don't bother calculating next d1 if done.
4361 __ b(ne, &more_bits);
4362
4363 // If exponent is positive we are done.
4364 __ cmp(exponent, Operand(0));
4365 __ b(ge, &allocate_return);
4366
4367 // If exponent is negative result is 1/result (d2 already holds 1.0 in that
4368 // case). However if d0 has reached infinity this will not provide the
4369 // correct result, so call runtime if that is the case.
4370 __ mov(scratch2, Operand(0x7FF00000));
4371 __ mov(scratch1, Operand(0));
4372 __ vmov(d1, scratch1, scratch2); // Load infinity into d1.
4373 __ vcmp(d0, d1);
4374 __ vmrs(pc);
4375 runtime.Branch(eq); // d0 reached infinity.
4376 __ vdiv(d0, d2, d0);
4377 __ b(&allocate_return);
4378
4379 __ bind(&exponent_nonsmi);
lrn@chromium.org32d961d2010-06-30 09:09:34 +00004380 // Special handling of raising to the power of -0.5 and 0.5. First check
4381 // that the value is a heap number and that the lower bits (which for both
4382 // values are zero).
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00004383 heap_number_map = r6;
lrn@chromium.org32d961d2010-06-30 09:09:34 +00004384 __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
4385 __ ldr(scratch1, FieldMemOperand(exponent, HeapObject::kMapOffset));
4386 __ ldr(scratch2, FieldMemOperand(exponent, HeapNumber::kMantissaOffset));
4387 __ cmp(scratch1, heap_number_map);
4388 runtime.Branch(ne);
4389 __ tst(scratch2, scratch2);
4390 runtime.Branch(ne);
4391
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00004392 // Load the higher bits (which contains the floating point exponent).
lrn@chromium.org32d961d2010-06-30 09:09:34 +00004393 __ ldr(scratch1, FieldMemOperand(exponent, HeapNumber::kExponentOffset));
4394
4395 // Compare exponent with -0.5.
4396 __ cmp(scratch1, Operand(0xbfe00000));
4397 __ b(ne, &not_minus_half);
4398
4399 // Get the double value from the base into vfp register d0.
4400 __ ObjectToDoubleVFPRegister(base, d0,
4401 scratch1, scratch2, heap_number_map, s0,
4402 runtime.entry_label(),
4403 AVOID_NANS_AND_INFINITIES);
4404
4405 // Load 1.0 into d2.
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00004406 __ vmov(d2, 1.0);
lrn@chromium.org32d961d2010-06-30 09:09:34 +00004407
4408 // Calculate the reciprocal of the square root. 1/sqrt(x) = sqrt(1/x).
4409 __ vdiv(d0, d2, d0);
4410 __ vsqrt(d0, d0);
4411
4412 __ b(&allocate_return);
4413
4414 __ bind(&not_minus_half);
4415 // Compare exponent with 0.5.
4416 __ cmp(scratch1, Operand(0x3fe00000));
4417 runtime.Branch(ne);
4418
4419 // Get the double value from the base into vfp register d0.
4420 __ ObjectToDoubleVFPRegister(base, d0,
4421 scratch1, scratch2, heap_number_map, s0,
4422 runtime.entry_label(),
4423 AVOID_NANS_AND_INFINITIES);
4424 __ vsqrt(d0, d0);
4425
4426 __ bind(&allocate_return);
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00004427 Register scratch3 = r5;
4428 __ AllocateHeapNumberWithValue(scratch3, d0, scratch1, scratch2,
4429 heap_number_map, runtime.entry_label());
4430 __ mov(base, scratch3);
lrn@chromium.org32d961d2010-06-30 09:09:34 +00004431 done.Jump();
4432
4433 runtime.Bind();
4434
4435 // Push back the arguments again for the runtime call.
4436 frame_->EmitPush(base);
4437 frame_->EmitPush(exponent);
4438 frame_->CallRuntime(Runtime::kMath_pow, 2);
4439 __ Move(base, r0);
4440
4441 done.Bind();
4442 frame_->EmitPush(base);
4443 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004444}
4445
4446
lrn@chromium.org32d961d2010-06-30 09:09:34 +00004447// Generates the Math.sqrt method.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004448void CodeGenerator::GenerateMathSqrt(ZoneList<Expression*>* args) {
4449 ASSERT(args->length() == 1);
4450 Load(args->at(0));
lrn@chromium.org32d961d2010-06-30 09:09:34 +00004451
4452 if (!CpuFeatures::IsSupported(VFP3)) {
4453 frame_->CallRuntime(Runtime::kMath_sqrt, 1);
4454 frame_->EmitPush(r0);
4455 } else {
4456 CpuFeatures::Scope scope(VFP3);
4457 JumpTarget runtime, done;
4458
4459 Register scratch1 = VirtualFrame::scratch0();
4460 Register scratch2 = VirtualFrame::scratch1();
4461
4462 // Get the value from the frame.
4463 Register tos = frame_->PopToRegister();
4464
4465 // Set the frame for the runtime jump target. The code below jumps to the
4466 // jump target label so the frame needs to be established before that.
4467 ASSERT(runtime.entry_frame() == NULL);
4468 runtime.set_entry_frame(frame_);
4469
4470 Register heap_number_map = r6;
4471 __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
4472
4473 // Get the double value from the heap number into vfp register d0.
4474 __ ObjectToDoubleVFPRegister(tos, d0,
4475 scratch1, scratch2, heap_number_map, s0,
4476 runtime.entry_label());
4477
4478 // Calculate the square root of d0 and place result in a heap number object.
4479 __ vsqrt(d0, d0);
4480 __ AllocateHeapNumberWithValue(
4481 tos, d0, scratch1, scratch2, heap_number_map, runtime.entry_label());
4482 done.Jump();
4483
4484 runtime.Bind();
4485 // Push back the argument again for the runtime call.
4486 frame_->EmitPush(tos);
4487 frame_->CallRuntime(Runtime::kMath_sqrt, 1);
4488 __ Move(tos, r0);
4489
4490 done.Bind();
4491 frame_->EmitPush(tos);
4492 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004493}
4494
4495
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004496class DeferredStringCharCodeAt : public DeferredCode {
4497 public:
4498 DeferredStringCharCodeAt(Register object,
4499 Register index,
4500 Register scratch,
4501 Register result)
4502 : result_(result),
4503 char_code_at_generator_(object,
4504 index,
4505 scratch,
4506 result,
4507 &need_conversion_,
4508 &need_conversion_,
4509 &index_out_of_range_,
4510 STRING_INDEX_IS_NUMBER) {}
4511
4512 StringCharCodeAtGenerator* fast_case_generator() {
4513 return &char_code_at_generator_;
4514 }
4515
4516 virtual void Generate() {
4517 VirtualFrameRuntimeCallHelper call_helper(frame_state());
4518 char_code_at_generator_.GenerateSlow(masm(), call_helper);
4519
4520 __ bind(&need_conversion_);
4521 // Move the undefined value into the result register, which will
4522 // trigger conversion.
4523 __ LoadRoot(result_, Heap::kUndefinedValueRootIndex);
4524 __ jmp(exit_label());
4525
4526 __ bind(&index_out_of_range_);
4527 // When the index is out of range, the spec requires us to return
4528 // NaN.
4529 __ LoadRoot(result_, Heap::kNanValueRootIndex);
4530 __ jmp(exit_label());
4531 }
4532
4533 private:
4534 Register result_;
4535
4536 Label need_conversion_;
4537 Label index_out_of_range_;
4538
4539 StringCharCodeAtGenerator char_code_at_generator_;
4540};
4541
4542
4543// This generates code that performs a String.prototype.charCodeAt() call
4544// or returns a smi in order to trigger conversion.
4545void CodeGenerator::GenerateStringCharCodeAt(ZoneList<Expression*>* args) {
4546 VirtualFrame::SpilledScope spilled_scope(frame_);
4547 Comment(masm_, "[ GenerateStringCharCodeAt");
kasper.lund7276f142008-07-30 08:49:36 +00004548 ASSERT(args->length() == 2);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004549
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004550 Load(args->at(0));
4551 Load(args->at(1));
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004552
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004553 Register index = r1;
4554 Register object = r2;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004555
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004556 frame_->EmitPop(r1);
4557 frame_->EmitPop(r2);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004558
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004559 // We need two extra registers.
4560 Register scratch = r3;
4561 Register result = r0;
4562
4563 DeferredStringCharCodeAt* deferred =
4564 new DeferredStringCharCodeAt(object,
4565 index,
4566 scratch,
4567 result);
4568 deferred->fast_case_generator()->GenerateFast(masm_);
4569 deferred->BindExit();
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004570 frame_->EmitPush(result);
kasper.lund7276f142008-07-30 08:49:36 +00004571}
4572
4573
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004574class DeferredStringCharFromCode : public DeferredCode {
4575 public:
4576 DeferredStringCharFromCode(Register code,
4577 Register result)
4578 : char_from_code_generator_(code, result) {}
4579
4580 StringCharFromCodeGenerator* fast_case_generator() {
4581 return &char_from_code_generator_;
4582 }
4583
4584 virtual void Generate() {
4585 VirtualFrameRuntimeCallHelper call_helper(frame_state());
4586 char_from_code_generator_.GenerateSlow(masm(), call_helper);
4587 }
4588
4589 private:
4590 StringCharFromCodeGenerator char_from_code_generator_;
4591};
4592
4593
4594// Generates code for creating a one-char string from a char code.
4595void CodeGenerator::GenerateStringCharFromCode(ZoneList<Expression*>* args) {
4596 VirtualFrame::SpilledScope spilled_scope(frame_);
4597 Comment(masm_, "[ GenerateStringCharFromCode");
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004598 ASSERT(args->length() == 1);
4599
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004600 Load(args->at(0));
4601
ager@chromium.orgac091b72010-05-05 07:34:42 +00004602 Register code = r1;
ager@chromium.orgac091b72010-05-05 07:34:42 +00004603 Register result = r0;
4604
ager@chromium.orgac091b72010-05-05 07:34:42 +00004605 frame_->EmitPop(code);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004606
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004607 DeferredStringCharFromCode* deferred = new DeferredStringCharFromCode(
4608 code, result);
4609 deferred->fast_case_generator()->GenerateFast(masm_);
4610 deferred->BindExit();
4611 frame_->EmitPush(result);
4612}
4613
4614
4615class DeferredStringCharAt : public DeferredCode {
4616 public:
4617 DeferredStringCharAt(Register object,
4618 Register index,
4619 Register scratch1,
4620 Register scratch2,
4621 Register result)
4622 : result_(result),
4623 char_at_generator_(object,
4624 index,
4625 scratch1,
4626 scratch2,
4627 result,
4628 &need_conversion_,
4629 &need_conversion_,
4630 &index_out_of_range_,
4631 STRING_INDEX_IS_NUMBER) {}
4632
4633 StringCharAtGenerator* fast_case_generator() {
4634 return &char_at_generator_;
4635 }
4636
4637 virtual void Generate() {
4638 VirtualFrameRuntimeCallHelper call_helper(frame_state());
4639 char_at_generator_.GenerateSlow(masm(), call_helper);
4640
4641 __ bind(&need_conversion_);
4642 // Move smi zero into the result register, which will trigger
4643 // conversion.
4644 __ mov(result_, Operand(Smi::FromInt(0)));
4645 __ jmp(exit_label());
4646
4647 __ bind(&index_out_of_range_);
4648 // When the index is out of range, the spec requires us to return
4649 // the empty string.
4650 __ LoadRoot(result_, Heap::kEmptyStringRootIndex);
4651 __ jmp(exit_label());
4652 }
4653
4654 private:
4655 Register result_;
4656
4657 Label need_conversion_;
4658 Label index_out_of_range_;
4659
4660 StringCharAtGenerator char_at_generator_;
4661};
4662
4663
4664// This generates code that performs a String.prototype.charAt() call
4665// or returns a smi in order to trigger conversion.
4666void CodeGenerator::GenerateStringCharAt(ZoneList<Expression*>* args) {
4667 VirtualFrame::SpilledScope spilled_scope(frame_);
4668 Comment(masm_, "[ GenerateStringCharAt");
4669 ASSERT(args->length() == 2);
4670
4671 Load(args->at(0));
4672 Load(args->at(1));
4673
4674 Register index = r1;
4675 Register object = r2;
4676
4677 frame_->EmitPop(r1);
4678 frame_->EmitPop(r2);
4679
4680 // We need three extra registers.
4681 Register scratch1 = r3;
4682 Register scratch2 = r4;
4683 Register result = r0;
4684
4685 DeferredStringCharAt* deferred =
4686 new DeferredStringCharAt(object,
4687 index,
4688 scratch1,
4689 scratch2,
4690 result);
4691 deferred->fast_case_generator()->GenerateFast(masm_);
4692 deferred->BindExit();
ager@chromium.orgac091b72010-05-05 07:34:42 +00004693 frame_->EmitPush(result);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004694}
4695
4696
ager@chromium.org7c537e22008-10-16 08:43:32 +00004697void CodeGenerator::GenerateIsArray(ZoneList<Expression*>* args) {
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00004698 ASSERT(args->length() == 1);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004699 Load(args->at(0));
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004700 JumpTarget answer;
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00004701 // We need the CC bits to come out as not_equal in the case where the
4702 // object is a smi. This can't be done with the usual test opcode so
4703 // we use XOR to get the right CC bits.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004704 Register possible_array = frame_->PopToRegister();
4705 Register scratch = VirtualFrame::scratch0();
4706 __ and_(scratch, possible_array, Operand(kSmiTagMask));
4707 __ eor(scratch, scratch, Operand(kSmiTagMask), SetCC);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004708 answer.Branch(ne);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00004709 // It is a heap object - get the map. Check if the object is a JS array.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004710 __ CompareObjectType(possible_array, scratch, scratch, JS_ARRAY_TYPE);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004711 answer.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004712 cc_reg_ = eq;
4713}
4714
4715
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00004716void CodeGenerator::GenerateIsRegExp(ZoneList<Expression*>* args) {
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00004717 ASSERT(args->length() == 1);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004718 Load(args->at(0));
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00004719 JumpTarget answer;
4720 // We need the CC bits to come out as not_equal in the case where the
4721 // object is a smi. This can't be done with the usual test opcode so
4722 // we use XOR to get the right CC bits.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004723 Register possible_regexp = frame_->PopToRegister();
4724 Register scratch = VirtualFrame::scratch0();
4725 __ and_(scratch, possible_regexp, Operand(kSmiTagMask));
4726 __ eor(scratch, scratch, Operand(kSmiTagMask), SetCC);
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00004727 answer.Branch(ne);
4728 // It is a heap object - get the map. Check if the object is a regexp.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004729 __ CompareObjectType(possible_regexp, scratch, scratch, JS_REGEXP_TYPE);
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00004730 answer.Bind();
4731 cc_reg_ = eq;
4732}
4733
4734
ager@chromium.org6141cbe2009-11-20 12:14:52 +00004735void CodeGenerator::GenerateIsObject(ZoneList<Expression*>* args) {
4736 // This generates a fast version of:
4737 // (typeof(arg) === 'object' || %_ClassOf(arg) == 'RegExp')
ager@chromium.org6141cbe2009-11-20 12:14:52 +00004738 ASSERT(args->length() == 1);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004739 Load(args->at(0));
4740 Register possible_object = frame_->PopToRegister();
4741 __ tst(possible_object, Operand(kSmiTagMask));
ager@chromium.org6141cbe2009-11-20 12:14:52 +00004742 false_target()->Branch(eq);
4743
4744 __ LoadRoot(ip, Heap::kNullValueRootIndex);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004745 __ cmp(possible_object, ip);
ager@chromium.org6141cbe2009-11-20 12:14:52 +00004746 true_target()->Branch(eq);
4747
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004748 Register map_reg = VirtualFrame::scratch0();
4749 __ ldr(map_reg, FieldMemOperand(possible_object, HeapObject::kMapOffset));
ager@chromium.org6141cbe2009-11-20 12:14:52 +00004750 // Undetectable objects behave like undefined when tested with typeof.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004751 __ ldrb(possible_object, FieldMemOperand(map_reg, Map::kBitFieldOffset));
4752 __ tst(possible_object, Operand(1 << Map::kIsUndetectable));
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004753 false_target()->Branch(ne);
ager@chromium.org6141cbe2009-11-20 12:14:52 +00004754
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004755 __ ldrb(possible_object, FieldMemOperand(map_reg, Map::kInstanceTypeOffset));
4756 __ cmp(possible_object, Operand(FIRST_JS_OBJECT_TYPE));
ager@chromium.org6141cbe2009-11-20 12:14:52 +00004757 false_target()->Branch(lt);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004758 __ cmp(possible_object, Operand(LAST_JS_OBJECT_TYPE));
ager@chromium.org6141cbe2009-11-20 12:14:52 +00004759 cc_reg_ = le;
4760}
4761
4762
4763void CodeGenerator::GenerateIsFunction(ZoneList<Expression*>* args) {
4764 // This generates a fast version of:
4765 // (%_ClassOf(arg) === 'Function')
ager@chromium.org6141cbe2009-11-20 12:14:52 +00004766 ASSERT(args->length() == 1);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004767 Load(args->at(0));
4768 Register possible_function = frame_->PopToRegister();
4769 __ tst(possible_function, Operand(kSmiTagMask));
ager@chromium.org6141cbe2009-11-20 12:14:52 +00004770 false_target()->Branch(eq);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004771 Register map_reg = VirtualFrame::scratch0();
4772 Register scratch = VirtualFrame::scratch1();
4773 __ CompareObjectType(possible_function, map_reg, scratch, JS_FUNCTION_TYPE);
ager@chromium.org6141cbe2009-11-20 12:14:52 +00004774 cc_reg_ = eq;
4775}
4776
4777
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004778void CodeGenerator::GenerateIsUndetectableObject(ZoneList<Expression*>* args) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004779 ASSERT(args->length() == 1);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004780 Load(args->at(0));
4781 Register possible_undetectable = frame_->PopToRegister();
4782 __ tst(possible_undetectable, Operand(kSmiTagMask));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004783 false_target()->Branch(eq);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004784 Register scratch = VirtualFrame::scratch0();
4785 __ ldr(scratch,
4786 FieldMemOperand(possible_undetectable, HeapObject::kMapOffset));
4787 __ ldrb(scratch, FieldMemOperand(scratch, Map::kBitFieldOffset));
4788 __ tst(scratch, Operand(1 << Map::kIsUndetectable));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004789 cc_reg_ = ne;
4790}
4791
4792
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004793void CodeGenerator::GenerateIsConstructCall(ZoneList<Expression*>* args) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004794 ASSERT(args->length() == 0);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00004795
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004796 Register scratch0 = VirtualFrame::scratch0();
4797 Register scratch1 = VirtualFrame::scratch1();
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00004798 // Get the frame pointer for the calling frame.
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004799 __ ldr(scratch0, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00004800
4801 // Skip the arguments adaptor frame if it exists.
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004802 __ ldr(scratch1,
4803 MemOperand(scratch0, StandardFrameConstants::kContextOffset));
4804 __ cmp(scratch1, Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
4805 __ ldr(scratch0,
4806 MemOperand(scratch0, StandardFrameConstants::kCallerFPOffset), eq);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00004807
4808 // Check the marker in the calling frame.
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004809 __ ldr(scratch1,
4810 MemOperand(scratch0, StandardFrameConstants::kMarkerOffset));
4811 __ cmp(scratch1, Operand(Smi::FromInt(StackFrame::CONSTRUCT)));
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00004812 cc_reg_ = eq;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004813}
4814
4815
ager@chromium.org7c537e22008-10-16 08:43:32 +00004816void CodeGenerator::GenerateArgumentsLength(ZoneList<Expression*>* args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004817 ASSERT(args->length() == 0);
4818
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004819 Register tos = frame_->GetTOSRegister();
4820 Register scratch0 = VirtualFrame::scratch0();
4821 Register scratch1 = VirtualFrame::scratch1();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004822
lrn@chromium.org25156de2010-04-06 13:10:27 +00004823 // Check if the calling frame is an arguments adaptor frame.
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004824 __ ldr(scratch0,
4825 MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
4826 __ ldr(scratch1,
4827 MemOperand(scratch0, StandardFrameConstants::kContextOffset));
4828 __ cmp(scratch1, Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
4829
4830 // Get the number of formal parameters.
4831 __ mov(tos, Operand(Smi::FromInt(scope()->num_parameters())), LeaveCC, ne);
lrn@chromium.org25156de2010-04-06 13:10:27 +00004832
4833 // Arguments adaptor case: Read the arguments length from the
4834 // adaptor frame.
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004835 __ ldr(tos,
4836 MemOperand(scratch0, ArgumentsAdaptorFrameConstants::kLengthOffset),
4837 eq);
lrn@chromium.org25156de2010-04-06 13:10:27 +00004838
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004839 frame_->EmitPush(tos);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004840}
4841
4842
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00004843void CodeGenerator::GenerateArguments(ZoneList<Expression*>* args) {
ager@chromium.org357bf652010-04-12 11:30:10 +00004844 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004845 ASSERT(args->length() == 1);
4846
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00004847 // Satisfy contract with ArgumentsAccessStub:
4848 // Load the key into r1 and the formal parameters count into r0.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004849 Load(args->at(0));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004850 frame_->EmitPop(r1);
ager@chromium.org5c838252010-02-19 08:53:10 +00004851 __ mov(r0, Operand(Smi::FromInt(scope()->num_parameters())));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004852
4853 // Call the shared stub to get to arguments[key].
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004854 ArgumentsAccessStub stub(ArgumentsAccessStub::READ_ELEMENT);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004855 frame_->CallStub(&stub, 0);
4856 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004857}
4858
4859
ager@chromium.org357bf652010-04-12 11:30:10 +00004860void CodeGenerator::GenerateRandomHeapNumber(
4861 ZoneList<Expression*>* args) {
4862 VirtualFrame::SpilledScope spilled_scope(frame_);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00004863 ASSERT(args->length() == 0);
ager@chromium.org357bf652010-04-12 11:30:10 +00004864
4865 Label slow_allocate_heapnumber;
4866 Label heapnumber_allocated;
4867
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00004868 __ LoadRoot(r6, Heap::kHeapNumberMapRootIndex);
4869 __ AllocateHeapNumber(r4, r1, r2, r6, &slow_allocate_heapnumber);
ager@chromium.org357bf652010-04-12 11:30:10 +00004870 __ jmp(&heapnumber_allocated);
4871
4872 __ bind(&slow_allocate_heapnumber);
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00004873 // Allocate a heap number.
4874 __ CallRuntime(Runtime::kNumberAlloc, 0);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00004875 __ mov(r4, Operand(r0));
ager@chromium.org357bf652010-04-12 11:30:10 +00004876
4877 __ bind(&heapnumber_allocated);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00004878
4879 // Convert 32 random bits in r0 to 0.(32 random bits) in a double
4880 // by computing:
4881 // ( 1.(20 0s)(32 random bits) x 2^20 ) - (1.0 x 2^20)).
4882 if (CpuFeatures::IsSupported(VFP3)) {
4883 __ PrepareCallCFunction(0, r1);
4884 __ CallCFunction(ExternalReference::random_uint32_function(), 0);
4885
4886 CpuFeatures::Scope scope(VFP3);
4887 // 0x41300000 is the top half of 1.0 x 2^20 as a double.
4888 // Create this constant using mov/orr to avoid PC relative load.
4889 __ mov(r1, Operand(0x41000000));
4890 __ orr(r1, r1, Operand(0x300000));
4891 // Move 0x41300000xxxxxxxx (x = random bits) to VFP.
4892 __ vmov(d7, r0, r1);
4893 // Move 0x4130000000000000 to VFP.
4894 __ mov(r0, Operand(0));
4895 __ vmov(d8, r0, r1);
4896 // Subtract and store the result in the heap number.
4897 __ vsub(d7, d7, d8);
4898 __ sub(r0, r4, Operand(kHeapObjectTag));
4899 __ vstr(d7, r0, HeapNumber::kValueOffset);
4900 frame_->EmitPush(r4);
4901 } else {
4902 __ mov(r0, Operand(r4));
4903 __ PrepareCallCFunction(1, r1);
4904 __ CallCFunction(
4905 ExternalReference::fill_heap_number_with_random_function(), 1);
4906 frame_->EmitPush(r0);
4907 }
ager@chromium.orgeadaf222009-06-16 09:43:10 +00004908}
4909
4910
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00004911void CodeGenerator::GenerateStringAdd(ZoneList<Expression*>* args) {
4912 ASSERT_EQ(2, args->length());
4913
4914 Load(args->at(0));
4915 Load(args->at(1));
4916
ager@chromium.org5c838252010-02-19 08:53:10 +00004917 StringAddStub stub(NO_STRING_ADD_FLAGS);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004918 frame_->SpillAll();
ager@chromium.org5c838252010-02-19 08:53:10 +00004919 frame_->CallStub(&stub, 2);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00004920 frame_->EmitPush(r0);
4921}
4922
4923
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004924void CodeGenerator::GenerateSubString(ZoneList<Expression*>* args) {
4925 ASSERT_EQ(3, args->length());
4926
4927 Load(args->at(0));
4928 Load(args->at(1));
4929 Load(args->at(2));
4930
ager@chromium.org5c838252010-02-19 08:53:10 +00004931 SubStringStub stub;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004932 frame_->SpillAll();
ager@chromium.org5c838252010-02-19 08:53:10 +00004933 frame_->CallStub(&stub, 3);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004934 frame_->EmitPush(r0);
4935}
4936
4937
4938void CodeGenerator::GenerateStringCompare(ZoneList<Expression*>* args) {
4939 ASSERT_EQ(2, args->length());
4940
4941 Load(args->at(0));
4942 Load(args->at(1));
4943
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004944 StringCompareStub stub;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004945 frame_->SpillAll();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004946 frame_->CallStub(&stub, 2);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004947 frame_->EmitPush(r0);
4948}
4949
4950
4951void CodeGenerator::GenerateRegExpExec(ZoneList<Expression*>* args) {
4952 ASSERT_EQ(4, args->length());
4953
4954 Load(args->at(0));
4955 Load(args->at(1));
4956 Load(args->at(2));
4957 Load(args->at(3));
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00004958 RegExpExecStub stub;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004959 frame_->SpillAll();
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00004960 frame_->CallStub(&stub, 4);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004961 frame_->EmitPush(r0);
4962}
4963
4964
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00004965void CodeGenerator::GenerateRegExpConstructResult(ZoneList<Expression*>* args) {
4966 // No stub. This code only occurs a few times in regexp.js.
4967 const int kMaxInlineLength = 100;
4968 ASSERT_EQ(3, args->length());
4969 Load(args->at(0)); // Size of array, smi.
4970 Load(args->at(1)); // "index" property value.
4971 Load(args->at(2)); // "input" property value.
4972 {
4973 VirtualFrame::SpilledScope spilled_scope(frame_);
4974 Label slowcase;
4975 Label done;
4976 __ ldr(r1, MemOperand(sp, kPointerSize * 2));
4977 STATIC_ASSERT(kSmiTag == 0);
4978 STATIC_ASSERT(kSmiTagSize == 1);
4979 __ tst(r1, Operand(kSmiTagMask));
4980 __ b(ne, &slowcase);
4981 __ cmp(r1, Operand(Smi::FromInt(kMaxInlineLength)));
4982 __ b(hi, &slowcase);
4983 // Smi-tagging is equivalent to multiplying by 2.
4984 // Allocate RegExpResult followed by FixedArray with size in ebx.
4985 // JSArray: [Map][empty properties][Elements][Length-smi][index][input]
4986 // Elements: [Map][Length][..elements..]
4987 // Size of JSArray with two in-object properties and the header of a
4988 // FixedArray.
4989 int objects_size =
4990 (JSRegExpResult::kSize + FixedArray::kHeaderSize) / kPointerSize;
4991 __ mov(r5, Operand(r1, LSR, kSmiTagSize + kSmiShiftSize));
4992 __ add(r2, r5, Operand(objects_size));
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00004993 __ AllocateInNewSpace(
4994 r2, // In: Size, in words.
4995 r0, // Out: Start of allocation (tagged).
4996 r3, // Scratch register.
4997 r4, // Scratch register.
4998 &slowcase,
4999 static_cast<AllocationFlags>(TAG_OBJECT | SIZE_IN_WORDS));
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00005000 // r0: Start of allocated area, object-tagged.
5001 // r1: Number of elements in array, as smi.
5002 // r5: Number of elements, untagged.
5003
5004 // Set JSArray map to global.regexp_result_map().
5005 // Set empty properties FixedArray.
5006 // Set elements to point to FixedArray allocated right after the JSArray.
5007 // Interleave operations for better latency.
5008 __ ldr(r2, ContextOperand(cp, Context::GLOBAL_INDEX));
5009 __ add(r3, r0, Operand(JSRegExpResult::kSize));
5010 __ mov(r4, Operand(Factory::empty_fixed_array()));
5011 __ ldr(r2, FieldMemOperand(r2, GlobalObject::kGlobalContextOffset));
5012 __ str(r3, FieldMemOperand(r0, JSObject::kElementsOffset));
5013 __ ldr(r2, ContextOperand(r2, Context::REGEXP_RESULT_MAP_INDEX));
5014 __ str(r4, FieldMemOperand(r0, JSObject::kPropertiesOffset));
5015 __ str(r2, FieldMemOperand(r0, HeapObject::kMapOffset));
5016
5017 // Set input, index and length fields from arguments.
5018 __ ldm(ia_w, sp, static_cast<RegList>(r2.bit() | r4.bit()));
5019 __ str(r1, FieldMemOperand(r0, JSArray::kLengthOffset));
5020 __ add(sp, sp, Operand(kPointerSize));
5021 __ str(r4, FieldMemOperand(r0, JSRegExpResult::kIndexOffset));
5022 __ str(r2, FieldMemOperand(r0, JSRegExpResult::kInputOffset));
5023
5024 // Fill out the elements FixedArray.
5025 // r0: JSArray, tagged.
5026 // r3: FixedArray, tagged.
5027 // r5: Number of elements in array, untagged.
5028
5029 // Set map.
5030 __ mov(r2, Operand(Factory::fixed_array_map()));
5031 __ str(r2, FieldMemOperand(r3, HeapObject::kMapOffset));
5032 // Set FixedArray length.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005033 __ mov(r6, Operand(r5, LSL, kSmiTagSize));
5034 __ str(r6, FieldMemOperand(r3, FixedArray::kLengthOffset));
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00005035 // Fill contents of fixed-array with the-hole.
5036 __ mov(r2, Operand(Factory::the_hole_value()));
5037 __ add(r3, r3, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
5038 // Fill fixed array elements with hole.
5039 // r0: JSArray, tagged.
5040 // r2: the hole.
5041 // r3: Start of elements in FixedArray.
5042 // r5: Number of elements to fill.
5043 Label loop;
5044 __ tst(r5, Operand(r5));
5045 __ bind(&loop);
5046 __ b(le, &done); // Jump if r1 is negative or zero.
5047 __ sub(r5, r5, Operand(1), SetCC);
5048 __ str(r2, MemOperand(r3, r5, LSL, kPointerSizeLog2));
5049 __ jmp(&loop);
5050
5051 __ bind(&slowcase);
5052 __ CallRuntime(Runtime::kRegExpConstructResult, 3);
5053
5054 __ bind(&done);
5055 }
5056 frame_->Forget(3);
5057 frame_->EmitPush(r0);
5058}
5059
5060
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005061class DeferredSearchCache: public DeferredCode {
5062 public:
5063 DeferredSearchCache(Register dst, Register cache, Register key)
5064 : dst_(dst), cache_(cache), key_(key) {
5065 set_comment("[ DeferredSearchCache");
5066 }
5067
5068 virtual void Generate();
5069
5070 private:
5071 Register dst_, cache_, key_;
5072};
5073
5074
5075void DeferredSearchCache::Generate() {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005076 __ Push(cache_, key_);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005077 __ CallRuntime(Runtime::kGetFromCache, 2);
5078 if (!dst_.is(r0)) {
5079 __ mov(dst_, r0);
5080 }
5081}
5082
5083
5084void CodeGenerator::GenerateGetFromCache(ZoneList<Expression*>* args) {
5085 ASSERT_EQ(2, args->length());
5086
5087 ASSERT_NE(NULL, args->at(0)->AsLiteral());
5088 int cache_id = Smi::cast(*(args->at(0)->AsLiteral()->handle()))->value();
5089
5090 Handle<FixedArray> jsfunction_result_caches(
5091 Top::global_context()->jsfunction_result_caches());
5092 if (jsfunction_result_caches->length() <= cache_id) {
5093 __ Abort("Attempt to use undefined cache.");
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005094 frame_->EmitPushRoot(Heap::kUndefinedValueRootIndex);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005095 return;
5096 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005097
5098 Load(args->at(1));
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005099
5100 VirtualFrame::SpilledScope spilled_scope(frame_);
5101
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005102 frame_->EmitPop(r2);
5103
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005104 __ ldr(r1, ContextOperand(cp, Context::GLOBAL_INDEX));
5105 __ ldr(r1, FieldMemOperand(r1, GlobalObject::kGlobalContextOffset));
5106 __ ldr(r1, ContextOperand(r1, Context::JSFUNCTION_RESULT_CACHES_INDEX));
5107 __ ldr(r1, FieldMemOperand(r1, FixedArray::OffsetOfElementAt(cache_id)));
5108
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005109 DeferredSearchCache* deferred = new DeferredSearchCache(r0, r1, r2);
5110
5111 const int kFingerOffset =
5112 FixedArray::OffsetOfElementAt(JSFunctionResultCache::kFingerIndex);
5113 ASSERT(kSmiTag == 0 && kSmiTagSize == 1);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005114 __ ldr(r0, FieldMemOperand(r1, kFingerOffset));
5115 // r0 now holds finger offset as a smi.
5116 __ add(r3, r1, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
5117 // r3 now points to the start of fixed array elements.
5118 __ ldr(r0, MemOperand(r3, r0, LSL, kPointerSizeLog2 - kSmiTagSize, PreIndex));
5119 // Note side effect of PreIndex: r3 now points to the key of the pair.
5120 __ cmp(r2, r0);
5121 deferred->Branch(ne);
5122
5123 __ ldr(r0, MemOperand(r3, kPointerSize));
5124
5125 deferred->BindExit();
5126 frame_->EmitPush(r0);
5127}
5128
5129
ager@chromium.org5c838252010-02-19 08:53:10 +00005130void CodeGenerator::GenerateNumberToString(ZoneList<Expression*>* args) {
5131 ASSERT_EQ(args->length(), 1);
5132
5133 // Load the argument on the stack and jump to the runtime.
5134 Load(args->at(0));
5135
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005136 NumberToStringStub stub;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005137 frame_->SpillAll();
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005138 frame_->CallStub(&stub, 1);
ager@chromium.org5c838252010-02-19 08:53:10 +00005139 frame_->EmitPush(r0);
5140}
5141
5142
ager@chromium.orgac091b72010-05-05 07:34:42 +00005143class DeferredSwapElements: public DeferredCode {
5144 public:
5145 DeferredSwapElements(Register object, Register index1, Register index2)
5146 : object_(object), index1_(index1), index2_(index2) {
5147 set_comment("[ DeferredSwapElements");
5148 }
5149
5150 virtual void Generate();
5151
5152 private:
5153 Register object_, index1_, index2_;
5154};
5155
5156
5157void DeferredSwapElements::Generate() {
5158 __ push(object_);
5159 __ push(index1_);
5160 __ push(index2_);
5161 __ CallRuntime(Runtime::kSwapElements, 3);
5162}
5163
5164
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00005165void CodeGenerator::GenerateSwapElements(ZoneList<Expression*>* args) {
5166 Comment cmnt(masm_, "[ GenerateSwapElements");
5167
5168 ASSERT_EQ(3, args->length());
5169
5170 Load(args->at(0));
5171 Load(args->at(1));
5172 Load(args->at(2));
5173
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005174 VirtualFrame::SpilledScope spilled_scope(frame_);
5175
ager@chromium.orgac091b72010-05-05 07:34:42 +00005176 Register index2 = r2;
5177 Register index1 = r1;
5178 Register object = r0;
5179 Register tmp1 = r3;
5180 Register tmp2 = r4;
5181
5182 frame_->EmitPop(index2);
5183 frame_->EmitPop(index1);
5184 frame_->EmitPop(object);
5185
5186 DeferredSwapElements* deferred =
5187 new DeferredSwapElements(object, index1, index2);
5188
5189 // Fetch the map and check if array is in fast case.
5190 // Check that object doesn't require security checks and
5191 // has no indexed interceptor.
5192 __ CompareObjectType(object, tmp1, tmp2, FIRST_JS_OBJECT_TYPE);
5193 deferred->Branch(lt);
5194 __ ldrb(tmp2, FieldMemOperand(tmp1, Map::kBitFieldOffset));
5195 __ tst(tmp2, Operand(KeyedLoadIC::kSlowCaseBitFieldMask));
5196 deferred->Branch(nz);
5197
5198 // Check the object's elements are in fast case.
5199 __ ldr(tmp1, FieldMemOperand(object, JSObject::kElementsOffset));
5200 __ ldr(tmp2, FieldMemOperand(tmp1, HeapObject::kMapOffset));
5201 __ LoadRoot(ip, Heap::kFixedArrayMapRootIndex);
5202 __ cmp(tmp2, ip);
5203 deferred->Branch(ne);
5204
5205 // Smi-tagging is equivalent to multiplying by 2.
5206 STATIC_ASSERT(kSmiTag == 0);
5207 STATIC_ASSERT(kSmiTagSize == 1);
5208
5209 // Check that both indices are smis.
5210 __ mov(tmp2, index1);
5211 __ orr(tmp2, tmp2, index2);
5212 __ tst(tmp2, Operand(kSmiTagMask));
5213 deferred->Branch(nz);
5214
5215 // Bring the offsets into the fixed array in tmp1 into index1 and
5216 // index2.
5217 __ mov(tmp2, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
5218 __ add(index1, tmp2, Operand(index1, LSL, kPointerSizeLog2 - kSmiTagSize));
5219 __ add(index2, tmp2, Operand(index2, LSL, kPointerSizeLog2 - kSmiTagSize));
5220
5221 // Swap elements.
5222 Register tmp3 = object;
5223 object = no_reg;
5224 __ ldr(tmp3, MemOperand(tmp1, index1));
5225 __ ldr(tmp2, MemOperand(tmp1, index2));
5226 __ str(tmp3, MemOperand(tmp1, index2));
5227 __ str(tmp2, MemOperand(tmp1, index1));
5228
5229 Label done;
5230 __ InNewSpace(tmp1, tmp2, eq, &done);
5231 // Possible optimization: do a check that both values are Smis
5232 // (or them and test against Smi mask.)
5233
5234 __ mov(tmp2, tmp1);
5235 RecordWriteStub recordWrite1(tmp1, index1, tmp3);
5236 __ CallStub(&recordWrite1);
5237
5238 RecordWriteStub recordWrite2(tmp2, index2, tmp3);
5239 __ CallStub(&recordWrite2);
5240
5241 __ bind(&done);
5242
5243 deferred->BindExit();
5244 __ LoadRoot(tmp1, Heap::kUndefinedValueRootIndex);
5245 frame_->EmitPush(tmp1);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00005246}
5247
5248
ager@chromium.org357bf652010-04-12 11:30:10 +00005249void CodeGenerator::GenerateCallFunction(ZoneList<Expression*>* args) {
5250 Comment cmnt(masm_, "[ GenerateCallFunction");
5251
5252 ASSERT(args->length() >= 2);
5253
5254 int n_args = args->length() - 2; // for receiver and function.
5255 Load(args->at(0)); // receiver
5256 for (int i = 0; i < n_args; i++) {
5257 Load(args->at(i + 1));
5258 }
5259 Load(args->at(n_args + 1)); // function
5260 frame_->CallJSFunction(n_args);
5261 frame_->EmitPush(r0);
5262}
5263
5264
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005265void CodeGenerator::GenerateMathSin(ZoneList<Expression*>* args) {
5266 ASSERT_EQ(args->length(), 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005267 Load(args->at(0));
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00005268 if (CpuFeatures::IsSupported(VFP3)) {
5269 TranscendentalCacheStub stub(TranscendentalCache::SIN);
5270 frame_->SpillAllButCopyTOSToR0();
5271 frame_->CallStub(&stub, 1);
5272 } else {
5273 frame_->CallRuntime(Runtime::kMath_sin, 1);
5274 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005275 frame_->EmitPush(r0);
5276}
5277
5278
5279void CodeGenerator::GenerateMathCos(ZoneList<Expression*>* args) {
5280 ASSERT_EQ(args->length(), 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005281 Load(args->at(0));
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00005282 if (CpuFeatures::IsSupported(VFP3)) {
5283 TranscendentalCacheStub stub(TranscendentalCache::COS);
5284 frame_->SpillAllButCopyTOSToR0();
5285 frame_->CallStub(&stub, 1);
5286 } else {
5287 frame_->CallRuntime(Runtime::kMath_cos, 1);
5288 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005289 frame_->EmitPush(r0);
5290}
5291
5292
ager@chromium.org7c537e22008-10-16 08:43:32 +00005293void CodeGenerator::GenerateObjectEquals(ZoneList<Expression*>* args) {
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005294 ASSERT(args->length() == 2);
5295
5296 // Load the two objects into registers and perform the comparison.
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00005297 Load(args->at(0));
5298 Load(args->at(1));
5299 Register lhs = frame_->PopToRegister();
5300 Register rhs = frame_->PopToRegister(lhs);
5301 __ cmp(lhs, rhs);
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005302 cc_reg_ = eq;
5303}
5304
5305
ager@chromium.org7c537e22008-10-16 08:43:32 +00005306void CodeGenerator::VisitCallRuntime(CallRuntime* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005307#ifdef DEBUG
5308 int original_height = frame_->height();
5309#endif
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005310 if (CheckForInlineRuntimeCall(node)) {
5311 ASSERT((has_cc() && frame_->height() == original_height) ||
5312 (!has_cc() && frame_->height() == original_height + 1));
5313 return;
5314 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005315
5316 ZoneList<Expression*>* args = node->arguments();
5317 Comment cmnt(masm_, "[ CallRuntime");
5318 Runtime::Function* function = node->function();
5319
ager@chromium.org41826e72009-03-30 13:30:57 +00005320 if (function == NULL) {
mads.s.ager31e71382008-08-13 09:32:07 +00005321 // Prepare stack for calling JS runtime function.
mads.s.ager31e71382008-08-13 09:32:07 +00005322 // Push the builtins object found in the current global object.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005323 Register scratch = VirtualFrame::scratch0();
5324 __ ldr(scratch, GlobalObject());
5325 Register builtins = frame_->GetTOSRegister();
5326 __ ldr(builtins, FieldMemOperand(scratch, GlobalObject::kBuiltinsOffset));
5327 frame_->EmitPush(builtins);
ager@chromium.org41826e72009-03-30 13:30:57 +00005328 }
mads.s.ager31e71382008-08-13 09:32:07 +00005329
ager@chromium.org41826e72009-03-30 13:30:57 +00005330 // Push the arguments ("left-to-right").
5331 int arg_count = args->length();
5332 for (int i = 0; i < arg_count; i++) {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005333 Load(args->at(i));
ager@chromium.org41826e72009-03-30 13:30:57 +00005334 }
mads.s.ager31e71382008-08-13 09:32:07 +00005335
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005336 VirtualFrame::SpilledScope spilled_scope(frame_);
5337
ager@chromium.org41826e72009-03-30 13:30:57 +00005338 if (function == NULL) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005339 // Call the JS runtime function.
ager@chromium.org5c838252010-02-19 08:53:10 +00005340 __ mov(r2, Operand(node->name()));
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005341 InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP;
5342 Handle<Code> stub = ComputeCallInitialize(arg_count, in_loop);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005343 frame_->CallCodeObject(stub, RelocInfo::CODE_TARGET, arg_count + 1);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005344 __ ldr(cp, frame_->Context());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005345 frame_->EmitPush(r0);
ager@chromium.org41826e72009-03-30 13:30:57 +00005346 } else {
5347 // Call the C runtime function.
5348 frame_->CallRuntime(function, arg_count);
5349 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005350 }
ager@chromium.orgac091b72010-05-05 07:34:42 +00005351 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005352}
5353
5354
ager@chromium.org7c537e22008-10-16 08:43:32 +00005355void CodeGenerator::VisitUnaryOperation(UnaryOperation* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005356#ifdef DEBUG
5357 int original_height = frame_->height();
5358#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005359 Comment cmnt(masm_, "[ UnaryOperation");
5360
5361 Token::Value op = node->op();
5362
5363 if (op == Token::NOT) {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005364 LoadCondition(node->expression(), false_target(), true_target(), true);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00005365 // LoadCondition may (and usually does) leave a test and branch to
5366 // be emitted by the caller. In that case, negate the condition.
5367 if (has_cc()) cc_reg_ = NegateCondition(cc_reg_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005368
5369 } else if (op == Token::DELETE) {
5370 Property* property = node->expression()->AsProperty();
mads.s.ager31e71382008-08-13 09:32:07 +00005371 Variable* variable = node->expression()->AsVariableProxy()->AsVariable();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005372 if (property != NULL) {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005373 Load(property->obj());
5374 Load(property->key());
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00005375 frame_->InvokeBuiltin(Builtins::DELETE, CALL_JS, 2);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005376 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005377
mads.s.ager31e71382008-08-13 09:32:07 +00005378 } else if (variable != NULL) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005379 Slot* slot = variable->slot();
5380 if (variable->is_global()) {
5381 LoadGlobal();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005382 frame_->EmitPush(Operand(variable->name()));
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00005383 frame_->InvokeBuiltin(Builtins::DELETE, CALL_JS, 2);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005384 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005385
5386 } else if (slot != NULL && slot->type() == Slot::LOOKUP) {
5387 // lookup the context holding the named variable
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005388 frame_->EmitPush(cp);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005389 frame_->EmitPush(Operand(variable->name()));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005390 frame_->CallRuntime(Runtime::kLookupContext, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005391 // r0: context
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005392 frame_->EmitPush(r0);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005393 frame_->EmitPush(Operand(variable->name()));
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00005394 frame_->InvokeBuiltin(Builtins::DELETE, CALL_JS, 2);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005395 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005396
mads.s.ager31e71382008-08-13 09:32:07 +00005397 } else {
5398 // Default: Result of deleting non-global, not dynamically
5399 // introduced variables is false.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005400 frame_->EmitPushRoot(Heap::kFalseValueRootIndex);
mads.s.ager31e71382008-08-13 09:32:07 +00005401 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005402
5403 } else {
5404 // Default: Result of deleting expressions is true.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005405 Load(node->expression()); // may have side-effects
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005406 frame_->Drop();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005407 frame_->EmitPushRoot(Heap::kTrueValueRootIndex);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005408 }
5409
5410 } else if (op == Token::TYPEOF) {
5411 // Special case for loading the typeof expression; see comment on
5412 // LoadTypeofExpression().
5413 LoadTypeofExpression(node->expression());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005414 frame_->CallRuntime(Runtime::kTypeof, 1);
5415 frame_->EmitPush(r0); // r0 has result
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005416
5417 } else {
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00005418 bool can_overwrite =
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005419 (node->expression()->AsBinaryOperation() != NULL &&
5420 node->expression()->AsBinaryOperation()->ResultOverwriteAllowed());
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00005421 UnaryOverwriteMode overwrite =
5422 can_overwrite ? UNARY_OVERWRITE : UNARY_NO_OVERWRITE;
5423
5424 bool no_negative_zero = node->expression()->no_negative_zero();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005425 Load(node->expression());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005426 switch (op) {
5427 case Token::NOT:
5428 case Token::DELETE:
5429 case Token::TYPEOF:
5430 UNREACHABLE(); // handled above
5431 break;
5432
5433 case Token::SUB: {
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00005434 frame_->PopToR0();
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00005435 GenericUnaryOpStub stub(
5436 Token::SUB,
5437 overwrite,
5438 no_negative_zero ? kIgnoreNegativeZero : kStrictNegativeZero);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005439 frame_->CallStub(&stub, 0);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005440 frame_->EmitPush(r0); // r0 has result
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005441 break;
5442 }
5443
5444 case Token::BIT_NOT: {
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00005445 Register tos = frame_->PopToRegister();
5446 JumpTarget not_smi_label;
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005447 JumpTarget continue_label;
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00005448 // Smi check.
5449 __ tst(tos, Operand(kSmiTagMask));
5450 not_smi_label.Branch(ne);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005451
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00005452 __ mvn(tos, Operand(tos));
5453 __ bic(tos, tos, Operand(kSmiTagMask)); // Bit-clear inverted smi-tag.
5454 frame_->EmitPush(tos);
5455 // The fast case is the first to jump to the continue label, so it gets
5456 // to decide the virtual frame layout.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005457 continue_label.Jump();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005458
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00005459 not_smi_label.Bind();
5460 frame_->SpillAll();
5461 __ Move(r0, tos);
5462 GenericUnaryOpStub stub(Token::BIT_NOT, overwrite);
5463 frame_->CallStub(&stub, 0);
5464 frame_->EmitPush(r0);
5465
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005466 continue_label.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005467 break;
5468 }
5469
5470 case Token::VOID:
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005471 frame_->Drop();
5472 frame_->EmitPushRoot(Heap::kUndefinedValueRootIndex);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005473 break;
5474
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005475 case Token::ADD: {
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00005476 Register tos = frame_->Peek();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005477 // Smi check.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005478 JumpTarget continue_label;
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00005479 __ tst(tos, Operand(kSmiTagMask));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005480 continue_label.Branch(eq);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00005481
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00005482 frame_->InvokeBuiltin(Builtins::TO_NUMBER, CALL_JS, 1);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00005483 frame_->EmitPush(r0);
5484
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005485 continue_label.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005486 break;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005487 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005488 default:
5489 UNREACHABLE();
5490 }
5491 }
kasperl@chromium.orge959c182009-07-27 08:59:04 +00005492 ASSERT(!has_valid_frame() ||
5493 (has_cc() && frame_->height() == original_height) ||
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005494 (!has_cc() && frame_->height() == original_height + 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005495}
5496
5497
ager@chromium.org7c537e22008-10-16 08:43:32 +00005498void CodeGenerator::VisitCountOperation(CountOperation* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005499#ifdef DEBUG
5500 int original_height = frame_->height();
5501#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005502 Comment cmnt(masm_, "[ CountOperation");
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00005503 VirtualFrame::RegisterAllocationScope scope(this);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005504
5505 bool is_postfix = node->is_postfix();
5506 bool is_increment = node->op() == Token::INC;
5507
5508 Variable* var = node->expression()->AsVariableProxy()->AsVariable();
5509 bool is_const = (var != NULL && var->mode() == Variable::CONST);
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00005510 bool is_slot = (var != NULL && var->mode() == Variable::VAR);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005511
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00005512 if (!is_const && is_slot && type_info(var->slot()).IsSmi()) {
5513 // The type info declares that this variable is always a Smi. That
5514 // means it is a Smi both before and after the increment/decrement.
5515 // Lets make use of that to make a very minimal count.
5516 Reference target(this, node->expression(), !is_const);
5517 ASSERT(!target.is_illegal());
5518 target.GetValue(); // Pushes the value.
5519 Register value = frame_->PopToRegister();
5520 if (is_postfix) frame_->EmitPush(value);
5521 if (is_increment) {
5522 __ add(value, value, Operand(Smi::FromInt(1)));
5523 } else {
5524 __ sub(value, value, Operand(Smi::FromInt(1)));
5525 }
5526 frame_->EmitPush(value);
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00005527 target.SetValue(NOT_CONST_INIT, LIKELY_SMI);
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00005528 if (is_postfix) frame_->Pop();
5529 ASSERT_EQ(original_height + 1, frame_->height());
5530 return;
5531 }
5532
5533 // If it's a postfix expression and its result is not ignored and the
5534 // reference is non-trivial, then push a placeholder on the stack now
5535 // to hold the result of the expression.
5536 bool placeholder_pushed = false;
5537 if (!is_slot && is_postfix) {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005538 frame_->EmitPush(Operand(Smi::FromInt(0)));
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00005539 placeholder_pushed = true;
mads.s.ager31e71382008-08-13 09:32:07 +00005540 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005541
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005542 // A constant reference is not saved to, so a constant reference is not a
5543 // compound assignment reference.
5544 { Reference target(this, node->expression(), !is_const);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005545 if (target.is_illegal()) {
5546 // Spoof the virtual frame to have the expected height (one higher
5547 // than on entry).
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00005548 if (!placeholder_pushed) frame_->EmitPush(Operand(Smi::FromInt(0)));
ager@chromium.orgac091b72010-05-05 07:34:42 +00005549 ASSERT_EQ(original_height + 1, frame_->height());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005550 return;
5551 }
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00005552
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005553 // This pushes 0, 1 or 2 words on the object to be used later when updating
5554 // the target. It also pushes the current value of the target.
ager@chromium.org357bf652010-04-12 11:30:10 +00005555 target.GetValue();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005556
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005557 JumpTarget slow;
5558 JumpTarget exit;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005559
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005560 Register value = frame_->PopToRegister();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005561
5562 // Postfix: Store the old value as the result.
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00005563 if (placeholder_pushed) {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005564 frame_->SetElementAt(value, target.size());
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00005565 } else if (is_postfix) {
5566 frame_->EmitPush(value);
5567 __ mov(VirtualFrame::scratch0(), value);
5568 value = VirtualFrame::scratch0();
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005569 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005570
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00005571 // Check for smi operand.
5572 __ tst(value, Operand(kSmiTagMask));
5573 slow.Branch(ne);
5574
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005575 // Perform optimistic increment/decrement.
5576 if (is_increment) {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005577 __ add(value, value, Operand(Smi::FromInt(1)), SetCC);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005578 } else {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005579 __ sub(value, value, Operand(Smi::FromInt(1)), SetCC);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005580 }
5581
5582 // If the increment/decrement didn't overflow, we're done.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005583 exit.Branch(vc);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005584
5585 // Revert optimistic increment/decrement.
5586 if (is_increment) {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005587 __ sub(value, value, Operand(Smi::FromInt(1)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005588 } else {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005589 __ add(value, value, Operand(Smi::FromInt(1)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005590 }
5591
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005592 // Slow case: Convert to number. At this point the
5593 // value to be incremented is in the value register..
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005594 slow.Bind();
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005595
5596 // Convert the operand to a number.
5597 frame_->EmitPush(value);
5598
kasperl@chromium.org8ccb0be2009-04-07 07:21:39 +00005599 {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005600 VirtualFrame::SpilledScope spilled(frame_);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00005601 frame_->InvokeBuiltin(Builtins::TO_NUMBER, CALL_JS, 1);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005602
5603 if (is_postfix) {
5604 // Postfix: store to result (on the stack).
5605 __ str(r0, frame_->ElementAt(target.size()));
5606 }
5607
5608 // Compute the new value.
5609 frame_->EmitPush(r0);
5610 frame_->EmitPush(Operand(Smi::FromInt(1)));
5611 if (is_increment) {
5612 frame_->CallRuntime(Runtime::kNumberAdd, 2);
5613 } else {
5614 frame_->CallRuntime(Runtime::kNumberSub, 2);
5615 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005616 }
5617
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005618 __ Move(value, r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005619 // Store the new value in the target if not const.
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005620 // At this point the answer is in the value register.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005621 exit.Bind();
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005622 frame_->EmitPush(value);
5623 // Set the target with the result, leaving the result on
5624 // top of the stack. Removes the target from the stack if
5625 // it has a non-zero size.
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00005626 if (!is_const) target.SetValue(NOT_CONST_INIT, LIKELY_SMI);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005627 }
5628
5629 // Postfix: Discard the new value and use the old.
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005630 if (is_postfix) frame_->Pop();
ager@chromium.orgac091b72010-05-05 07:34:42 +00005631 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005632}
5633
5634
ager@chromium.orgb26c50a2010-03-26 09:27:16 +00005635void CodeGenerator::GenerateLogicalBooleanOperation(BinaryOperation* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005636 // According to ECMA-262 section 11.11, page 58, the binary logical
5637 // operators must yield the result of one of the two expressions
5638 // before any ToBoolean() conversions. This means that the value
5639 // produced by a && or || operator is not necessarily a boolean.
5640
5641 // NOTE: If the left hand side produces a materialized value (not in
5642 // the CC register), we force the right hand side to do the
5643 // same. This is necessary because we may have to branch to the exit
5644 // after evaluating the left hand side (due to the shortcut
5645 // semantics), but the compiler must (statically) know if the result
5646 // of compiling the binary operation is materialized or not.
ager@chromium.orgb26c50a2010-03-26 09:27:16 +00005647 if (node->op() == Token::AND) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005648 JumpTarget is_true;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005649 LoadCondition(node->left(), &is_true, false_target(), false);
kasperl@chromium.orge959c182009-07-27 08:59:04 +00005650 if (has_valid_frame() && !has_cc()) {
5651 // The left-hand side result is on top of the virtual frame.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005652 JumpTarget pop_and_continue;
5653 JumpTarget exit;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005654
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00005655 frame_->Dup();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005656 // Avoid popping the result if it converts to 'false' using the
5657 // standard ToBoolean() conversion as described in ECMA-262,
5658 // section 9.2, page 30.
mads.s.ager31e71382008-08-13 09:32:07 +00005659 ToBoolean(&pop_and_continue, &exit);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005660 Branch(false, &exit);
5661
5662 // Pop the result of evaluating the first part.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005663 pop_and_continue.Bind();
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00005664 frame_->Pop();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005665
5666 // Evaluate right side expression.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005667 is_true.Bind();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005668 Load(node->right());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005669
5670 // Exit (always with a materialized value).
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005671 exit.Bind();
kasperl@chromium.orge959c182009-07-27 08:59:04 +00005672 } else if (has_cc() || is_true.is_linked()) {
5673 // The left-hand side is either (a) partially compiled to
5674 // control flow with a final branch left to emit or (b) fully
5675 // compiled to control flow and possibly true.
5676 if (has_cc()) {
5677 Branch(false, false_target());
5678 }
5679 is_true.Bind();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005680 LoadCondition(node->right(), true_target(), false_target(), false);
kasperl@chromium.orge959c182009-07-27 08:59:04 +00005681 } else {
5682 // Nothing to do.
5683 ASSERT(!has_valid_frame() && !has_cc() && !is_true.is_linked());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005684 }
5685
ager@chromium.orgb26c50a2010-03-26 09:27:16 +00005686 } else {
5687 ASSERT(node->op() == Token::OR);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005688 JumpTarget is_false;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005689 LoadCondition(node->left(), true_target(), &is_false, false);
kasperl@chromium.orge959c182009-07-27 08:59:04 +00005690 if (has_valid_frame() && !has_cc()) {
5691 // The left-hand side result is on top of the virtual frame.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005692 JumpTarget pop_and_continue;
5693 JumpTarget exit;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005694
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00005695 frame_->Dup();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005696 // Avoid popping the result if it converts to 'true' using the
5697 // standard ToBoolean() conversion as described in ECMA-262,
5698 // section 9.2, page 30.
mads.s.ager31e71382008-08-13 09:32:07 +00005699 ToBoolean(&exit, &pop_and_continue);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005700 Branch(true, &exit);
5701
5702 // Pop the result of evaluating the first part.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005703 pop_and_continue.Bind();
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00005704 frame_->Pop();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005705
5706 // Evaluate right side expression.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005707 is_false.Bind();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005708 Load(node->right());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005709
5710 // Exit (always with a materialized value).
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005711 exit.Bind();
kasperl@chromium.orge959c182009-07-27 08:59:04 +00005712 } else if (has_cc() || is_false.is_linked()) {
5713 // The left-hand side is either (a) partially compiled to
5714 // control flow with a final branch left to emit or (b) fully
5715 // compiled to control flow and possibly false.
5716 if (has_cc()) {
5717 Branch(true, true_target());
5718 }
5719 is_false.Bind();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005720 LoadCondition(node->right(), true_target(), false_target(), false);
kasperl@chromium.orge959c182009-07-27 08:59:04 +00005721 } else {
5722 // Nothing to do.
5723 ASSERT(!has_valid_frame() && !has_cc() && !is_false.is_linked());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005724 }
ager@chromium.orgb26c50a2010-03-26 09:27:16 +00005725 }
5726}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005727
ager@chromium.orgb26c50a2010-03-26 09:27:16 +00005728
5729void CodeGenerator::VisitBinaryOperation(BinaryOperation* node) {
5730#ifdef DEBUG
5731 int original_height = frame_->height();
5732#endif
ager@chromium.orgb26c50a2010-03-26 09:27:16 +00005733 Comment cmnt(masm_, "[ BinaryOperation");
5734
5735 if (node->op() == Token::AND || node->op() == Token::OR) {
5736 GenerateLogicalBooleanOperation(node);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005737 } else {
5738 // Optimize for the case where (at least) one of the expressions
5739 // is a literal small integer.
5740 Literal* lliteral = node->left()->AsLiteral();
5741 Literal* rliteral = node->right()->AsLiteral();
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005742 // NOTE: The code below assumes that the slow cases (calls to runtime)
5743 // never return a constant/immutable object.
5744 bool overwrite_left =
5745 (node->left()->AsBinaryOperation() != NULL &&
5746 node->left()->AsBinaryOperation()->ResultOverwriteAllowed());
5747 bool overwrite_right =
5748 (node->right()->AsBinaryOperation() != NULL &&
5749 node->right()->AsBinaryOperation()->ResultOverwriteAllowed());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005750
5751 if (rliteral != NULL && rliteral->handle()->IsSmi()) {
ager@chromium.org357bf652010-04-12 11:30:10 +00005752 VirtualFrame::RegisterAllocationScope scope(this);
5753 Load(node->left());
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00005754 if (frame_->KnownSmiAt(0)) overwrite_left = false;
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00005755 SmiOperation(node->op(),
5756 rliteral->handle(),
5757 false,
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00005758 overwrite_left ? OVERWRITE_LEFT : NO_OVERWRITE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005759 } else if (lliteral != NULL && lliteral->handle()->IsSmi()) {
ager@chromium.org357bf652010-04-12 11:30:10 +00005760 VirtualFrame::RegisterAllocationScope scope(this);
5761 Load(node->right());
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00005762 if (frame_->KnownSmiAt(0)) overwrite_right = false;
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00005763 SmiOperation(node->op(),
5764 lliteral->handle(),
5765 true,
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00005766 overwrite_right ? OVERWRITE_RIGHT : NO_OVERWRITE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005767 } else {
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00005768 GenerateInlineSmi inline_smi =
5769 loop_nesting() > 0 ? GENERATE_INLINE_SMI : DONT_GENERATE_INLINE_SMI;
5770 if (lliteral != NULL) {
5771 ASSERT(!lliteral->handle()->IsSmi());
5772 inline_smi = DONT_GENERATE_INLINE_SMI;
5773 }
5774 if (rliteral != NULL) {
5775 ASSERT(!rliteral->handle()->IsSmi());
5776 inline_smi = DONT_GENERATE_INLINE_SMI;
5777 }
ager@chromium.org357bf652010-04-12 11:30:10 +00005778 VirtualFrame::RegisterAllocationScope scope(this);
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005779 OverwriteMode overwrite_mode = NO_OVERWRITE;
5780 if (overwrite_left) {
5781 overwrite_mode = OVERWRITE_LEFT;
5782 } else if (overwrite_right) {
5783 overwrite_mode = OVERWRITE_RIGHT;
5784 }
ager@chromium.org357bf652010-04-12 11:30:10 +00005785 Load(node->left());
5786 Load(node->right());
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00005787 GenericBinaryOperation(node->op(), overwrite_mode, inline_smi);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005788 }
5789 }
kasperl@chromium.orge959c182009-07-27 08:59:04 +00005790 ASSERT(!has_valid_frame() ||
5791 (has_cc() && frame_->height() == original_height) ||
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005792 (!has_cc() && frame_->height() == original_height + 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005793}
5794
5795
ager@chromium.org7c537e22008-10-16 08:43:32 +00005796void CodeGenerator::VisitThisFunction(ThisFunction* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005797#ifdef DEBUG
5798 int original_height = frame_->height();
5799#endif
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00005800 frame_->EmitPush(MemOperand(frame_->Function()));
ager@chromium.orgac091b72010-05-05 07:34:42 +00005801 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005802}
5803
5804
ager@chromium.org7c537e22008-10-16 08:43:32 +00005805void CodeGenerator::VisitCompareOperation(CompareOperation* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005806#ifdef DEBUG
5807 int original_height = frame_->height();
5808#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005809 Comment cmnt(masm_, "[ CompareOperation");
5810
ager@chromium.org357bf652010-04-12 11:30:10 +00005811 VirtualFrame::RegisterAllocationScope nonspilled_scope(this);
5812
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005813 // Get the expressions from the node.
5814 Expression* left = node->left();
5815 Expression* right = node->right();
5816 Token::Value op = node->op();
5817
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005818 // To make null checks efficient, we check if either left or right is the
5819 // literal 'null'. If so, we optimize the code by inlining a null check
5820 // instead of calling the (very) general runtime routine for checking
5821 // equality.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005822 if (op == Token::EQ || op == Token::EQ_STRICT) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005823 bool left_is_null =
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005824 left->AsLiteral() != NULL && left->AsLiteral()->IsNull();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005825 bool right_is_null =
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005826 right->AsLiteral() != NULL && right->AsLiteral()->IsNull();
5827 // The 'null' value can only be equal to 'null' or 'undefined'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005828 if (left_is_null || right_is_null) {
ager@chromium.org357bf652010-04-12 11:30:10 +00005829 Load(left_is_null ? right : left);
5830 Register tos = frame_->PopToRegister();
ager@chromium.orgab99eea2009-08-25 07:05:41 +00005831 __ LoadRoot(ip, Heap::kNullValueRootIndex);
ager@chromium.org357bf652010-04-12 11:30:10 +00005832 __ cmp(tos, ip);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005833
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005834 // The 'null' value is only equal to 'undefined' if using non-strict
5835 // comparisons.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005836 if (op != Token::EQ_STRICT) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005837 true_target()->Branch(eq);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005838
ager@chromium.orgab99eea2009-08-25 07:05:41 +00005839 __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
ager@chromium.org357bf652010-04-12 11:30:10 +00005840 __ cmp(tos, Operand(ip));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005841 true_target()->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005842
ager@chromium.org357bf652010-04-12 11:30:10 +00005843 __ tst(tos, Operand(kSmiTagMask));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005844 false_target()->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005845
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005846 // It can be an undetectable object.
ager@chromium.org357bf652010-04-12 11:30:10 +00005847 __ ldr(tos, FieldMemOperand(tos, HeapObject::kMapOffset));
5848 __ ldrb(tos, FieldMemOperand(tos, Map::kBitFieldOffset));
5849 __ and_(tos, tos, Operand(1 << Map::kIsUndetectable));
5850 __ cmp(tos, Operand(1 << Map::kIsUndetectable));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005851 }
5852
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005853 cc_reg_ = eq;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005854 ASSERT(has_cc() && frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005855 return;
5856 }
5857 }
5858
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005859 // To make typeof testing for natives implemented in JavaScript really
5860 // efficient, we generate special code for expressions of the form:
5861 // 'typeof <expression> == <string>'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005862 UnaryOperation* operation = left->AsUnaryOperation();
5863 if ((op == Token::EQ || op == Token::EQ_STRICT) &&
5864 (operation != NULL && operation->op() == Token::TYPEOF) &&
5865 (right->AsLiteral() != NULL &&
5866 right->AsLiteral()->handle()->IsString())) {
5867 Handle<String> check(String::cast(*right->AsLiteral()->handle()));
5868
ager@chromium.org357bf652010-04-12 11:30:10 +00005869 // Load the operand, move it to a register.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005870 LoadTypeofExpression(operation->expression());
ager@chromium.org357bf652010-04-12 11:30:10 +00005871 Register tos = frame_->PopToRegister();
5872
ager@chromium.org357bf652010-04-12 11:30:10 +00005873 Register scratch = VirtualFrame::scratch0();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005874
5875 if (check->Equals(Heap::number_symbol())) {
ager@chromium.org357bf652010-04-12 11:30:10 +00005876 __ tst(tos, Operand(kSmiTagMask));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005877 true_target()->Branch(eq);
ager@chromium.org357bf652010-04-12 11:30:10 +00005878 __ ldr(tos, FieldMemOperand(tos, HeapObject::kMapOffset));
ager@chromium.orgab99eea2009-08-25 07:05:41 +00005879 __ LoadRoot(ip, Heap::kHeapNumberMapRootIndex);
ager@chromium.org357bf652010-04-12 11:30:10 +00005880 __ cmp(tos, ip);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005881 cc_reg_ = eq;
5882
5883 } else if (check->Equals(Heap::string_symbol())) {
ager@chromium.org357bf652010-04-12 11:30:10 +00005884 __ tst(tos, Operand(kSmiTagMask));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005885 false_target()->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005886
ager@chromium.org357bf652010-04-12 11:30:10 +00005887 __ ldr(tos, FieldMemOperand(tos, HeapObject::kMapOffset));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005888
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005889 // It can be an undetectable string object.
ager@chromium.org357bf652010-04-12 11:30:10 +00005890 __ ldrb(scratch, FieldMemOperand(tos, Map::kBitFieldOffset));
5891 __ and_(scratch, scratch, Operand(1 << Map::kIsUndetectable));
5892 __ cmp(scratch, Operand(1 << Map::kIsUndetectable));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005893 false_target()->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005894
ager@chromium.org357bf652010-04-12 11:30:10 +00005895 __ ldrb(scratch, FieldMemOperand(tos, Map::kInstanceTypeOffset));
5896 __ cmp(scratch, Operand(FIRST_NONSTRING_TYPE));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005897 cc_reg_ = lt;
5898
5899 } else if (check->Equals(Heap::boolean_symbol())) {
ager@chromium.orgab99eea2009-08-25 07:05:41 +00005900 __ LoadRoot(ip, Heap::kTrueValueRootIndex);
ager@chromium.org357bf652010-04-12 11:30:10 +00005901 __ cmp(tos, ip);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005902 true_target()->Branch(eq);
ager@chromium.orgab99eea2009-08-25 07:05:41 +00005903 __ LoadRoot(ip, Heap::kFalseValueRootIndex);
ager@chromium.org357bf652010-04-12 11:30:10 +00005904 __ cmp(tos, ip);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005905 cc_reg_ = eq;
5906
5907 } else if (check->Equals(Heap::undefined_symbol())) {
ager@chromium.orgab99eea2009-08-25 07:05:41 +00005908 __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
ager@chromium.org357bf652010-04-12 11:30:10 +00005909 __ cmp(tos, ip);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005910 true_target()->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005911
ager@chromium.org357bf652010-04-12 11:30:10 +00005912 __ tst(tos, Operand(kSmiTagMask));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005913 false_target()->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005914
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005915 // It can be an undetectable object.
ager@chromium.org357bf652010-04-12 11:30:10 +00005916 __ ldr(tos, FieldMemOperand(tos, HeapObject::kMapOffset));
5917 __ ldrb(scratch, FieldMemOperand(tos, Map::kBitFieldOffset));
5918 __ and_(scratch, scratch, Operand(1 << Map::kIsUndetectable));
5919 __ cmp(scratch, Operand(1 << Map::kIsUndetectable));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005920
5921 cc_reg_ = eq;
5922
5923 } else if (check->Equals(Heap::function_symbol())) {
ager@chromium.org357bf652010-04-12 11:30:10 +00005924 __ tst(tos, Operand(kSmiTagMask));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005925 false_target()->Branch(eq);
ager@chromium.org357bf652010-04-12 11:30:10 +00005926 Register map_reg = scratch;
5927 __ CompareObjectType(tos, map_reg, tos, JS_FUNCTION_TYPE);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005928 true_target()->Branch(eq);
5929 // Regular expressions are callable so typeof == 'function'.
ager@chromium.org357bf652010-04-12 11:30:10 +00005930 __ CompareInstanceType(map_reg, tos, JS_REGEXP_TYPE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005931 cc_reg_ = eq;
5932
5933 } else if (check->Equals(Heap::object_symbol())) {
ager@chromium.org357bf652010-04-12 11:30:10 +00005934 __ tst(tos, Operand(kSmiTagMask));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005935 false_target()->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005936
ager@chromium.orgab99eea2009-08-25 07:05:41 +00005937 __ LoadRoot(ip, Heap::kNullValueRootIndex);
ager@chromium.org357bf652010-04-12 11:30:10 +00005938 __ cmp(tos, ip);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005939 true_target()->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005940
ager@chromium.org357bf652010-04-12 11:30:10 +00005941 Register map_reg = scratch;
5942 __ CompareObjectType(tos, map_reg, tos, JS_REGEXP_TYPE);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005943 false_target()->Branch(eq);
5944
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005945 // It can be an undetectable object.
ager@chromium.org357bf652010-04-12 11:30:10 +00005946 __ ldrb(tos, FieldMemOperand(map_reg, Map::kBitFieldOffset));
5947 __ and_(tos, tos, Operand(1 << Map::kIsUndetectable));
5948 __ cmp(tos, Operand(1 << Map::kIsUndetectable));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005949 false_target()->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005950
ager@chromium.org357bf652010-04-12 11:30:10 +00005951 __ ldrb(tos, FieldMemOperand(map_reg, Map::kInstanceTypeOffset));
5952 __ cmp(tos, Operand(FIRST_JS_OBJECT_TYPE));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005953 false_target()->Branch(lt);
ager@chromium.org357bf652010-04-12 11:30:10 +00005954 __ cmp(tos, Operand(LAST_JS_OBJECT_TYPE));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005955 cc_reg_ = le;
5956
5957 } else {
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005958 // Uncommon case: typeof testing against a string literal that is
5959 // never returned from the typeof operator.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005960 false_target()->Jump();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005961 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005962 ASSERT(!has_valid_frame() ||
5963 (has_cc() && frame_->height() == original_height));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005964 return;
5965 }
5966
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005967 switch (op) {
5968 case Token::EQ:
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005969 Comparison(eq, left, right, false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005970 break;
5971
5972 case Token::LT:
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005973 Comparison(lt, left, right);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005974 break;
5975
5976 case Token::GT:
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005977 Comparison(gt, left, right);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005978 break;
5979
5980 case Token::LTE:
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005981 Comparison(le, left, right);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005982 break;
5983
5984 case Token::GTE:
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005985 Comparison(ge, left, right);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005986 break;
5987
5988 case Token::EQ_STRICT:
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005989 Comparison(eq, left, right, true);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005990 break;
5991
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005992 case Token::IN: {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005993 Load(left);
5994 Load(right);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00005995 frame_->InvokeBuiltin(Builtins::IN, CALL_JS, 2);
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00005996 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005997 break;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005998 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005999
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006000 case Token::INSTANCEOF: {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006001 Load(left);
6002 Load(right);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006003 InstanceofStub stub;
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00006004 frame_->CallStub(&stub, 2);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006005 // At this point if instanceof succeeded then r0 == 0.
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00006006 __ tst(r0, Operand(r0));
ager@chromium.org7c537e22008-10-16 08:43:32 +00006007 cc_reg_ = eq;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006008 break;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006009 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006010
6011 default:
6012 UNREACHABLE();
6013 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006014 ASSERT((has_cc() && frame_->height() == original_height) ||
6015 (!has_cc() && frame_->height() == original_height + 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006016}
6017
6018
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006019class DeferredReferenceGetNamedValue: public DeferredCode {
6020 public:
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006021 explicit DeferredReferenceGetNamedValue(Register receiver,
6022 Handle<String> name)
6023 : receiver_(receiver), name_(name) {
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006024 set_comment("[ DeferredReferenceGetNamedValue");
6025 }
6026
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006027 virtual void Generate();
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006028
6029 private:
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006030 Register receiver_;
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006031 Handle<String> name_;
6032};
6033
6034
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006035// Convention for this is that on entry the receiver is in a register that
6036// is not used by the stack. On exit the answer is found in that same
6037// register and the stack has the same height.
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006038void DeferredReferenceGetNamedValue::Generate() {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006039#ifdef DEBUG
6040 int expected_height = frame_state()->frame()->height();
6041#endif
6042 VirtualFrame copied_frame(*frame_state()->frame());
6043 copied_frame.SpillAll();
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006044
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006045 Register scratch1 = VirtualFrame::scratch0();
6046 Register scratch2 = VirtualFrame::scratch1();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006047 ASSERT(!receiver_.is(scratch1) && !receiver_.is(scratch2));
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006048 __ DecrementCounter(&Counters::named_load_inline, 1, scratch1, scratch2);
6049 __ IncrementCounter(&Counters::named_load_inline_miss, 1, scratch1, scratch2);
6050
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006051 // Ensure receiver in r0 and name in r2 to match load ic calling convention.
6052 __ Move(r0, receiver_);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006053 __ mov(r2, Operand(name_));
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006054
6055 // The rest of the instructions in the deferred code must be together.
6056 { Assembler::BlockConstPoolScope block_const_pool(masm_);
6057 Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize));
6058 __ Call(ic, RelocInfo::CODE_TARGET);
6059 // The call must be followed by a nop(1) instruction to indicate that the
6060 // in-object has been inlined.
6061 __ nop(PROPERTY_ACCESS_INLINED);
6062
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006063 // At this point the answer is in r0. We move it to the expected register
6064 // if necessary.
6065 __ Move(receiver_, r0);
6066
6067 // Now go back to the frame that we entered with. This will not overwrite
6068 // the receiver register since that register was not in use when we came
6069 // in. The instructions emitted by this merge are skipped over by the
6070 // inline load patching mechanism when looking for the branch instruction
6071 // that tells it where the code to patch is.
6072 copied_frame.MergeTo(frame_state()->frame());
6073
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006074 // Block the constant pool for one more instruction after leaving this
6075 // constant pool block scope to include the branch instruction ending the
6076 // deferred code.
6077 __ BlockConstPoolFor(1);
6078 }
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006079 ASSERT_EQ(expected_height, frame_state()->frame()->height());
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006080}
6081
6082
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006083class DeferredReferenceGetKeyedValue: public DeferredCode {
6084 public:
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00006085 DeferredReferenceGetKeyedValue(Register key, Register receiver)
6086 : key_(key), receiver_(receiver) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006087 set_comment("[ DeferredReferenceGetKeyedValue");
6088 }
6089
6090 virtual void Generate();
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00006091
6092 private:
6093 Register key_;
6094 Register receiver_;
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006095};
6096
6097
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00006098// Takes key and register in r0 and r1 or vice versa. Returns result
6099// in r0.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006100void DeferredReferenceGetKeyedValue::Generate() {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00006101 ASSERT((key_.is(r0) && receiver_.is(r1)) ||
6102 (key_.is(r1) && receiver_.is(r0)));
6103
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00006104 VirtualFrame copied_frame(*frame_state()->frame());
6105 copied_frame.SpillAll();
6106
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006107 Register scratch1 = VirtualFrame::scratch0();
6108 Register scratch2 = VirtualFrame::scratch1();
6109 __ DecrementCounter(&Counters::keyed_load_inline, 1, scratch1, scratch2);
6110 __ IncrementCounter(&Counters::keyed_load_inline_miss, 1, scratch1, scratch2);
6111
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00006112 // Ensure key in r0 and receiver in r1 to match keyed load ic calling
6113 // convention.
6114 if (key_.is(r1)) {
6115 __ Swap(r0, r1, ip);
6116 }
6117
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006118 // The rest of the instructions in the deferred code must be together.
6119 { Assembler::BlockConstPoolScope block_const_pool(masm_);
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00006120 // Call keyed load IC. It has the arguments key and receiver in r0 and r1.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006121 Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
6122 __ Call(ic, RelocInfo::CODE_TARGET);
6123 // The call must be followed by a nop instruction to indicate that the
6124 // keyed load has been inlined.
6125 __ nop(PROPERTY_ACCESS_INLINED);
6126
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00006127 // Now go back to the frame that we entered with. This will not overwrite
6128 // the receiver or key registers since they were not in use when we came
6129 // in. The instructions emitted by this merge are skipped over by the
6130 // inline load patching mechanism when looking for the branch instruction
6131 // that tells it where the code to patch is.
6132 copied_frame.MergeTo(frame_state()->frame());
6133
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006134 // Block the constant pool for one more instruction after leaving this
6135 // constant pool block scope to include the branch instruction ending the
6136 // deferred code.
6137 __ BlockConstPoolFor(1);
6138 }
6139}
6140
6141
6142class DeferredReferenceSetKeyedValue: public DeferredCode {
6143 public:
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006144 DeferredReferenceSetKeyedValue(Register value,
6145 Register key,
6146 Register receiver)
6147 : value_(value), key_(key), receiver_(receiver) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006148 set_comment("[ DeferredReferenceSetKeyedValue");
6149 }
6150
6151 virtual void Generate();
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006152
6153 private:
6154 Register value_;
6155 Register key_;
6156 Register receiver_;
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006157};
6158
6159
6160void DeferredReferenceSetKeyedValue::Generate() {
6161 Register scratch1 = VirtualFrame::scratch0();
6162 Register scratch2 = VirtualFrame::scratch1();
6163 __ DecrementCounter(&Counters::keyed_store_inline, 1, scratch1, scratch2);
6164 __ IncrementCounter(
6165 &Counters::keyed_store_inline_miss, 1, scratch1, scratch2);
6166
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006167 // Ensure value in r0, key in r1 and receiver in r2 to match keyed store ic
6168 // calling convention.
6169 if (value_.is(r1)) {
6170 __ Swap(r0, r1, ip);
6171 }
6172 ASSERT(receiver_.is(r2));
6173
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006174 // The rest of the instructions in the deferred code must be together.
6175 { Assembler::BlockConstPoolScope block_const_pool(masm_);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006176 // Call keyed store IC. It has the arguments value, key and receiver in r0,
6177 // r1 and r2.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006178 Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize));
6179 __ Call(ic, RelocInfo::CODE_TARGET);
6180 // The call must be followed by a nop instruction to indicate that the
6181 // keyed store has been inlined.
6182 __ nop(PROPERTY_ACCESS_INLINED);
6183
6184 // Block the constant pool for one more instruction after leaving this
6185 // constant pool block scope to include the branch instruction ending the
6186 // deferred code.
6187 __ BlockConstPoolFor(1);
6188 }
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006189}
6190
6191
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006192// Consumes the top of stack (the receiver) and pushes the result instead.
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006193void CodeGenerator::EmitNamedLoad(Handle<String> name, bool is_contextual) {
6194 if (is_contextual || scope()->is_global_scope() || loop_nesting() == 0) {
6195 Comment cmnt(masm(), "[ Load from named Property");
6196 // Setup the name register and call load IC.
ager@chromium.orgac091b72010-05-05 07:34:42 +00006197 frame_->CallLoadIC(name,
6198 is_contextual
6199 ? RelocInfo::CODE_TARGET_CONTEXT
6200 : RelocInfo::CODE_TARGET);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006201 frame_->EmitPush(r0); // Push answer.
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006202 } else {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006203 // Inline the in-object property case.
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006204 Comment cmnt(masm(), "[ Inlined named property load");
6205
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006206 // Counter will be decremented in the deferred code. Placed here to avoid
6207 // having it in the instruction stream below where patching will occur.
6208 __ IncrementCounter(&Counters::named_load_inline, 1,
6209 frame_->scratch0(), frame_->scratch1());
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006210
6211 // The following instructions are the inlined load of an in-object property.
6212 // Parts of this code is patched, so the exact instructions generated needs
6213 // to be fixed. Therefore the instruction pool is blocked when generating
6214 // this code
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006215
6216 // Load the receiver from the stack.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006217 Register receiver = frame_->PopToRegister();
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006218
6219 DeferredReferenceGetNamedValue* deferred =
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006220 new DeferredReferenceGetNamedValue(receiver, name);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006221
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006222#ifdef DEBUG
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006223 int kInlinedNamedLoadInstructions = 7;
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006224 Label check_inlined_codesize;
6225 masm_->bind(&check_inlined_codesize);
6226#endif
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006227
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006228 { Assembler::BlockConstPoolScope block_const_pool(masm_);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006229 // Check that the receiver is a heap object.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006230 __ tst(receiver, Operand(kSmiTagMask));
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006231 deferred->Branch(eq);
6232
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006233 Register scratch = VirtualFrame::scratch0();
6234 Register scratch2 = VirtualFrame::scratch1();
6235
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006236 // Check the map. The null map used below is patched by the inline cache
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006237 // code. Therefore we can't use a LoadRoot call.
6238 __ ldr(scratch, FieldMemOperand(receiver, HeapObject::kMapOffset));
6239 __ mov(scratch2, Operand(Factory::null_value()));
6240 __ cmp(scratch, scratch2);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006241 deferred->Branch(ne);
6242
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006243 // Initially use an invalid index. The index will be patched by the
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006244 // inline cache code.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006245 __ ldr(receiver, MemOperand(receiver, 0));
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006246
6247 // Make sure that the expected number of instructions are generated.
6248 ASSERT_EQ(kInlinedNamedLoadInstructions,
6249 masm_->InstructionsGeneratedSince(&check_inlined_codesize));
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006250 }
6251
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006252 deferred->BindExit();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006253 // At this point the receiver register has the result, either from the
6254 // deferred code or from the inlined code.
6255 frame_->EmitPush(receiver);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006256 }
6257}
6258
6259
ager@chromium.orgac091b72010-05-05 07:34:42 +00006260void CodeGenerator::EmitNamedStore(Handle<String> name, bool is_contextual) {
6261#ifdef DEBUG
6262 int expected_height = frame_->height() - (is_contextual ? 1 : 2);
6263#endif
6264 frame_->CallStoreIC(name, is_contextual);
6265
6266 ASSERT_EQ(expected_height, frame_->height());
6267}
6268
6269
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006270void CodeGenerator::EmitKeyedLoad() {
6271 if (loop_nesting() == 0) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006272 Comment cmnt(masm_, "[ Load from keyed property");
6273 frame_->CallKeyedLoadIC();
6274 } else {
6275 // Inline the keyed load.
6276 Comment cmnt(masm_, "[ Inlined load from keyed property");
6277
6278 // Counter will be decremented in the deferred code. Placed here to avoid
6279 // having it in the instruction stream below where patching will occur.
6280 __ IncrementCounter(&Counters::keyed_load_inline, 1,
6281 frame_->scratch0(), frame_->scratch1());
6282
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00006283 // Load the key and receiver from the stack.
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00006284 bool key_is_known_smi = frame_->KnownSmiAt(0);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00006285 Register key = frame_->PopToRegister();
6286 Register receiver = frame_->PopToRegister(key);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006287
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00006288 // The deferred code expects key and receiver in registers.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006289 DeferredReferenceGetKeyedValue* deferred =
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00006290 new DeferredReferenceGetKeyedValue(key, receiver);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006291
6292 // Check that the receiver is a heap object.
6293 __ tst(receiver, Operand(kSmiTagMask));
6294 deferred->Branch(eq);
6295
6296 // The following instructions are the part of the inlined load keyed
6297 // property code which can be patched. Therefore the exact number of
6298 // instructions generated need to be fixed, so the constant pool is blocked
6299 // while generating this code.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006300 { Assembler::BlockConstPoolScope block_const_pool(masm_);
6301 Register scratch1 = VirtualFrame::scratch0();
6302 Register scratch2 = VirtualFrame::scratch1();
6303 // Check the map. The null map used below is patched by the inline cache
6304 // code.
6305 __ ldr(scratch1, FieldMemOperand(receiver, HeapObject::kMapOffset));
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00006306
6307 // Check that the key is a smi.
6308 if (!key_is_known_smi) {
6309 __ tst(key, Operand(kSmiTagMask));
6310 deferred->Branch(ne);
6311 }
6312
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00006313#ifdef DEBUG
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00006314 Label check_inlined_codesize;
6315 masm_->bind(&check_inlined_codesize);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00006316#endif
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006317 __ mov(scratch2, Operand(Factory::null_value()));
6318 __ cmp(scratch1, scratch2);
6319 deferred->Branch(ne);
6320
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006321 // Get the elements array from the receiver and check that it
6322 // is not a dictionary.
6323 __ ldr(scratch1, FieldMemOperand(receiver, JSObject::kElementsOffset));
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00006324 if (FLAG_debug_code) {
6325 __ ldr(scratch2, FieldMemOperand(scratch1, JSObject::kMapOffset));
6326 __ LoadRoot(ip, Heap::kFixedArrayMapRootIndex);
6327 __ cmp(scratch2, ip);
6328 __ Assert(eq, "JSObject with fast elements map has slow elements");
6329 }
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006330
6331 // Check that key is within bounds. Use unsigned comparison to handle
6332 // negative keys.
6333 __ ldr(scratch2, FieldMemOperand(scratch1, FixedArray::kLengthOffset));
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006334 __ cmp(scratch2, key);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006335 deferred->Branch(ls); // Unsigned less equal.
6336
6337 // Load and check that the result is not the hole (key is a smi).
6338 __ LoadRoot(scratch2, Heap::kTheHoleValueRootIndex);
6339 __ add(scratch1,
6340 scratch1,
6341 Operand(FixedArray::kHeaderSize - kHeapObjectTag));
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00006342 __ ldr(scratch1,
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006343 MemOperand(scratch1, key, LSL,
6344 kPointerSizeLog2 - (kSmiTagSize + kSmiShiftSize)));
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00006345 __ cmp(scratch1, scratch2);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006346 deferred->Branch(eq);
6347
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00006348 __ mov(r0, scratch1);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006349 // Make sure that the expected number of instructions are generated.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00006350 ASSERT_EQ(GetInlinedKeyedLoadInstructionsAfterPatch(),
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006351 masm_->InstructionsGeneratedSince(&check_inlined_codesize));
6352 }
6353
6354 deferred->BindExit();
6355 }
6356}
6357
6358
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00006359void CodeGenerator::EmitKeyedStore(StaticType* key_type,
6360 WriteBarrierCharacter wb_info) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006361 // Generate inlined version of the keyed store if the code is in a loop
6362 // and the key is likely to be a smi.
6363 if (loop_nesting() > 0 && key_type->IsLikelySmi()) {
6364 // Inline the keyed store.
6365 Comment cmnt(masm_, "[ Inlined store to keyed property");
6366
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006367 Register scratch1 = VirtualFrame::scratch0();
6368 Register scratch2 = VirtualFrame::scratch1();
6369 Register scratch3 = r3;
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006370
6371 // Counter will be decremented in the deferred code. Placed here to avoid
6372 // having it in the instruction stream below where patching will occur.
6373 __ IncrementCounter(&Counters::keyed_store_inline, 1,
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006374 scratch1, scratch2);
6375
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00006376
6377
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006378 // Load the value, key and receiver from the stack.
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00006379 bool value_is_harmless = frame_->KnownSmiAt(0);
6380 if (wb_info == NEVER_NEWSPACE) value_is_harmless = true;
6381 bool key_is_smi = frame_->KnownSmiAt(1);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006382 Register value = frame_->PopToRegister();
6383 Register key = frame_->PopToRegister(value);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00006384 VirtualFrame::SpilledScope spilled(frame_);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006385 Register receiver = r2;
6386 frame_->EmitPop(receiver);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006387
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00006388#ifdef DEBUG
6389 bool we_remembered_the_write_barrier = value_is_harmless;
6390#endif
6391
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006392 // The deferred code expects value, key and receiver in registers.
6393 DeferredReferenceSetKeyedValue* deferred =
6394 new DeferredReferenceSetKeyedValue(value, key, receiver);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006395
6396 // Check that the value is a smi. As this inlined code does not set the
6397 // write barrier it is only possible to store smi values.
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00006398 if (!value_is_harmless) {
6399 // If the value is not likely to be a Smi then let's test the fixed array
6400 // for new space instead. See below.
6401 if (wb_info == LIKELY_SMI) {
6402 __ tst(value, Operand(kSmiTagMask));
6403 deferred->Branch(ne);
6404#ifdef DEBUG
6405 we_remembered_the_write_barrier = true;
6406#endif
6407 }
6408 }
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006409
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00006410 if (!key_is_smi) {
6411 // Check that the key is a smi.
6412 __ tst(key, Operand(kSmiTagMask));
6413 deferred->Branch(ne);
6414 }
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006415
6416 // Check that the receiver is a heap object.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006417 __ tst(receiver, Operand(kSmiTagMask));
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006418 deferred->Branch(eq);
6419
6420 // Check that the receiver is a JSArray.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006421 __ CompareObjectType(receiver, scratch1, scratch1, JS_ARRAY_TYPE);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006422 deferred->Branch(ne);
6423
6424 // Check that the key is within bounds. Both the key and the length of
6425 // the JSArray are smis. Use unsigned comparison to handle negative keys.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006426 __ ldr(scratch1, FieldMemOperand(receiver, JSArray::kLengthOffset));
6427 __ cmp(scratch1, key);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006428 deferred->Branch(ls); // Unsigned less equal.
6429
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00006430 // Get the elements array from the receiver.
6431 __ ldr(scratch1, FieldMemOperand(receiver, JSObject::kElementsOffset));
6432 if (!value_is_harmless && wb_info != LIKELY_SMI) {
6433 Label ok;
6434 __ and_(scratch2, scratch1, Operand(ExternalReference::new_space_mask()));
6435 __ cmp(scratch2, Operand(ExternalReference::new_space_start()));
6436 __ tst(value, Operand(kSmiTagMask), ne);
6437 deferred->Branch(ne);
6438#ifdef DEBUG
6439 we_remembered_the_write_barrier = true;
6440#endif
6441 }
6442 // Check that the elements array is not a dictionary.
6443 __ ldr(scratch2, FieldMemOperand(scratch1, JSObject::kMapOffset));
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006444 // The following instructions are the part of the inlined store keyed
6445 // property code which can be patched. Therefore the exact number of
6446 // instructions generated need to be fixed, so the constant pool is blocked
6447 // while generating this code.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006448 { Assembler::BlockConstPoolScope block_const_pool(masm_);
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00006449#ifdef DEBUG
6450 Label check_inlined_codesize;
6451 masm_->bind(&check_inlined_codesize);
6452#endif
6453
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006454 // Read the fixed array map from the constant pool (not from the root
6455 // array) so that the value can be patched. When debugging, we patch this
6456 // comparison to always fail so that we will hit the IC call in the
6457 // deferred code which will allow the debugger to break for fast case
6458 // stores.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006459 __ mov(scratch3, Operand(Factory::fixed_array_map()));
6460 __ cmp(scratch2, scratch3);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006461 deferred->Branch(ne);
6462
6463 // Store the value.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006464 __ add(scratch1, scratch1,
6465 Operand(FixedArray::kHeaderSize - kHeapObjectTag));
6466 __ str(value,
6467 MemOperand(scratch1, key, LSL,
6468 kPointerSizeLog2 - (kSmiTagSize + kSmiShiftSize)));
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006469
6470 // Make sure that the expected number of instructions are generated.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006471 ASSERT_EQ(kInlinedKeyedStoreInstructionsAfterPatch,
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006472 masm_->InstructionsGeneratedSince(&check_inlined_codesize));
6473 }
6474
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00006475 ASSERT(we_remembered_the_write_barrier);
6476
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006477 deferred->BindExit();
6478 } else {
6479 frame()->CallKeyedStoreIC();
6480 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006481}
6482
6483
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006484#ifdef DEBUG
6485bool CodeGenerator::HasValidEntryRegisters() { return true; }
6486#endif
6487
6488
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006489#undef __
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006490#define __ ACCESS_MASM(masm)
6491
ager@chromium.org7c537e22008-10-16 08:43:32 +00006492Handle<String> Reference::GetName() {
6493 ASSERT(type_ == NAMED);
6494 Property* property = expression_->AsProperty();
6495 if (property == NULL) {
6496 // Global variable reference treated as a named property reference.
6497 VariableProxy* proxy = expression_->AsVariableProxy();
6498 ASSERT(proxy->AsVariable() != NULL);
6499 ASSERT(proxy->AsVariable()->is_global());
6500 return proxy->name();
6501 } else {
6502 Literal* raw_name = property->key()->AsLiteral();
6503 ASSERT(raw_name != NULL);
6504 return Handle<String>(String::cast(*raw_name->handle()));
6505 }
6506}
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006507
ager@chromium.org7c537e22008-10-16 08:43:32 +00006508
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006509void Reference::DupIfPersist() {
6510 if (persist_after_get_) {
6511 switch (type_) {
6512 case KEYED:
6513 cgen_->frame()->Dup2();
6514 break;
6515 case NAMED:
6516 cgen_->frame()->Dup();
6517 // Fall through.
6518 case UNLOADED:
6519 case ILLEGAL:
6520 case SLOT:
6521 // Do nothing.
6522 ;
6523 }
6524 } else {
6525 set_unloaded();
6526 }
6527}
6528
6529
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006530void Reference::GetValue() {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006531 ASSERT(cgen_->HasValidEntryRegisters());
ager@chromium.org7c537e22008-10-16 08:43:32 +00006532 ASSERT(!is_illegal());
6533 ASSERT(!cgen_->has_cc());
6534 MacroAssembler* masm = cgen_->masm();
6535 Property* property = expression_->AsProperty();
6536 if (property != NULL) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006537 cgen_->CodeForSourcePosition(property->position());
ager@chromium.org7c537e22008-10-16 08:43:32 +00006538 }
6539
6540 switch (type_) {
6541 case SLOT: {
6542 Comment cmnt(masm, "[ Load from Slot");
6543 Slot* slot = expression_->AsVariableProxy()->AsVariable()->slot();
6544 ASSERT(slot != NULL);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006545 DupIfPersist();
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006546 cgen_->LoadFromSlotCheckForArguments(slot, NOT_INSIDE_TYPEOF);
ager@chromium.org7c537e22008-10-16 08:43:32 +00006547 break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006548 }
6549
ager@chromium.org7c537e22008-10-16 08:43:32 +00006550 case NAMED: {
ager@chromium.org7c537e22008-10-16 08:43:32 +00006551 Variable* var = expression_->AsVariableProxy()->AsVariable();
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006552 bool is_global = var != NULL;
6553 ASSERT(!is_global || var->is_global());
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006554 Handle<String> name = GetName();
6555 DupIfPersist();
6556 cgen_->EmitNamedLoad(name, is_global);
ager@chromium.org7c537e22008-10-16 08:43:32 +00006557 break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006558 }
6559
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006560 case KEYED: {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006561 ASSERT(property != NULL);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006562 DupIfPersist();
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006563 cgen_->EmitKeyedLoad();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006564 cgen_->frame()->EmitPush(r0);
6565 break;
6566 }
6567
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006568 default:
6569 UNREACHABLE();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006570 }
6571}
6572
6573
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00006574void Reference::SetValue(InitState init_state, WriteBarrierCharacter wb_info) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00006575 ASSERT(!is_illegal());
6576 ASSERT(!cgen_->has_cc());
6577 MacroAssembler* masm = cgen_->masm();
ager@chromium.org3bf7b912008-11-17 09:09:45 +00006578 VirtualFrame* frame = cgen_->frame();
ager@chromium.org7c537e22008-10-16 08:43:32 +00006579 Property* property = expression_->AsProperty();
6580 if (property != NULL) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006581 cgen_->CodeForSourcePosition(property->position());
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006582 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006583
ager@chromium.org7c537e22008-10-16 08:43:32 +00006584 switch (type_) {
6585 case SLOT: {
6586 Comment cmnt(masm, "[ Store to Slot");
6587 Slot* slot = expression_->AsVariableProxy()->AsVariable()->slot();
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006588 cgen_->StoreToSlot(slot, init_state);
ager@chromium.orgac091b72010-05-05 07:34:42 +00006589 set_unloaded();
ager@chromium.org7c537e22008-10-16 08:43:32 +00006590 break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006591 }
6592
ager@chromium.org7c537e22008-10-16 08:43:32 +00006593 case NAMED: {
6594 Comment cmnt(masm, "[ Store to named Property");
ager@chromium.orgac091b72010-05-05 07:34:42 +00006595 cgen_->EmitNamedStore(GetName(), false);
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00006596 frame->EmitPush(r0);
ager@chromium.org5c838252010-02-19 08:53:10 +00006597 set_unloaded();
ager@chromium.org7c537e22008-10-16 08:43:32 +00006598 break;
6599 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006600
ager@chromium.org7c537e22008-10-16 08:43:32 +00006601 case KEYED: {
6602 Comment cmnt(masm, "[ Store to keyed Property");
6603 Property* property = expression_->AsProperty();
6604 ASSERT(property != NULL);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006605 cgen_->CodeForSourcePosition(property->position());
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00006606 cgen_->EmitKeyedStore(property->key()->type(), wb_info);
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00006607 frame->EmitPush(r0);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006608 set_unloaded();
ager@chromium.org7c537e22008-10-16 08:43:32 +00006609 break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006610 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00006611
6612 default:
6613 UNREACHABLE();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006614 }
6615}
6616
6617
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006618void FastNewClosureStub::Generate(MacroAssembler* masm) {
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006619 // Create a new closure from the given function info in new
6620 // space. Set the context to the current context in cp.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006621 Label gc;
6622
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006623 // Pop the function info from the stack.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006624 __ pop(r3);
6625
6626 // Attempt to allocate new JSFunction in new space.
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00006627 __ AllocateInNewSpace(JSFunction::kSize,
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006628 r0,
6629 r1,
6630 r2,
6631 &gc,
6632 TAG_OBJECT);
6633
6634 // Compute the function map in the current global context and set that
6635 // as the map of the allocated object.
6636 __ ldr(r2, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_INDEX)));
6637 __ ldr(r2, FieldMemOperand(r2, GlobalObject::kGlobalContextOffset));
6638 __ ldr(r2, MemOperand(r2, Context::SlotOffset(Context::FUNCTION_MAP_INDEX)));
6639 __ str(r2, FieldMemOperand(r0, HeapObject::kMapOffset));
6640
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006641 // Initialize the rest of the function. We don't have to update the
6642 // write barrier because the allocated object is in new space.
6643 __ LoadRoot(r1, Heap::kEmptyFixedArrayRootIndex);
6644 __ LoadRoot(r2, Heap::kTheHoleValueRootIndex);
6645 __ str(r1, FieldMemOperand(r0, JSObject::kPropertiesOffset));
6646 __ str(r1, FieldMemOperand(r0, JSObject::kElementsOffset));
6647 __ str(r2, FieldMemOperand(r0, JSFunction::kPrototypeOrInitialMapOffset));
6648 __ str(r3, FieldMemOperand(r0, JSFunction::kSharedFunctionInfoOffset));
6649 __ str(cp, FieldMemOperand(r0, JSFunction::kContextOffset));
6650 __ str(r1, FieldMemOperand(r0, JSFunction::kLiteralsOffset));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006651
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006652 // Return result. The argument function info has been popped already.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006653 __ Ret();
6654
6655 // Create a new closure through the slower runtime call.
6656 __ bind(&gc);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006657 __ Push(cp, r3);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006658 __ TailCallRuntime(Runtime::kNewClosure, 2, 1);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006659}
6660
6661
6662void FastNewContextStub::Generate(MacroAssembler* masm) {
6663 // Try to allocate the context in new space.
6664 Label gc;
6665 int length = slots_ + Context::MIN_CONTEXT_SLOTS;
6666
6667 // Attempt to allocate the context in new space.
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00006668 __ AllocateInNewSpace(FixedArray::SizeFor(length),
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006669 r0,
6670 r1,
6671 r2,
6672 &gc,
6673 TAG_OBJECT);
6674
6675 // Load the function from the stack.
ager@chromium.org5c838252010-02-19 08:53:10 +00006676 __ ldr(r3, MemOperand(sp, 0));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006677
6678 // Setup the object header.
6679 __ LoadRoot(r2, Heap::kContextMapRootIndex);
6680 __ str(r2, FieldMemOperand(r0, HeapObject::kMapOffset));
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006681 __ mov(r2, Operand(Smi::FromInt(length)));
6682 __ str(r2, FieldMemOperand(r0, FixedArray::kLengthOffset));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006683
6684 // Setup the fixed slots.
6685 __ mov(r1, Operand(Smi::FromInt(0)));
6686 __ str(r3, MemOperand(r0, Context::SlotOffset(Context::CLOSURE_INDEX)));
6687 __ str(r0, MemOperand(r0, Context::SlotOffset(Context::FCONTEXT_INDEX)));
6688 __ str(r1, MemOperand(r0, Context::SlotOffset(Context::PREVIOUS_INDEX)));
6689 __ str(r1, MemOperand(r0, Context::SlotOffset(Context::EXTENSION_INDEX)));
6690
6691 // Copy the global object from the surrounding context.
6692 __ ldr(r1, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_INDEX)));
6693 __ str(r1, MemOperand(r0, Context::SlotOffset(Context::GLOBAL_INDEX)));
6694
6695 // Initialize the rest of the slots to undefined.
6696 __ LoadRoot(r1, Heap::kUndefinedValueRootIndex);
6697 for (int i = Context::MIN_CONTEXT_SLOTS; i < length; i++) {
6698 __ str(r1, MemOperand(r0, Context::SlotOffset(i)));
6699 }
6700
6701 // Remove the on-stack argument and return.
6702 __ mov(cp, r0);
6703 __ pop();
6704 __ Ret();
6705
6706 // Need to collect. Call into runtime system.
6707 __ bind(&gc);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006708 __ TailCallRuntime(Runtime::kNewContext, 1, 1);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006709}
6710
6711
ager@chromium.org5c838252010-02-19 08:53:10 +00006712void FastCloneShallowArrayStub::Generate(MacroAssembler* masm) {
6713 // Stack layout on entry:
6714 //
6715 // [sp]: constant elements.
6716 // [sp + kPointerSize]: literal index.
6717 // [sp + (2 * kPointerSize)]: literals array.
6718
6719 // All sizes here are multiples of kPointerSize.
6720 int elements_size = (length_ > 0) ? FixedArray::SizeFor(length_) : 0;
6721 int size = JSArray::kSize + elements_size;
6722
6723 // Load boilerplate object into r3 and check if we need to create a
6724 // boilerplate.
6725 Label slow_case;
6726 __ ldr(r3, MemOperand(sp, 2 * kPointerSize));
6727 __ ldr(r0, MemOperand(sp, 1 * kPointerSize));
6728 __ add(r3, r3, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
6729 __ ldr(r3, MemOperand(r3, r0, LSL, kPointerSizeLog2 - kSmiTagSize));
6730 __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
6731 __ cmp(r3, ip);
6732 __ b(eq, &slow_case);
6733
6734 // Allocate both the JS array and the elements array in one big
6735 // allocation. This avoids multiple limit checks.
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00006736 __ AllocateInNewSpace(size,
ager@chromium.org5c838252010-02-19 08:53:10 +00006737 r0,
6738 r1,
6739 r2,
6740 &slow_case,
6741 TAG_OBJECT);
6742
6743 // Copy the JS array part.
6744 for (int i = 0; i < JSArray::kSize; i += kPointerSize) {
6745 if ((i != JSArray::kElementsOffset) || (length_ == 0)) {
6746 __ ldr(r1, FieldMemOperand(r3, i));
6747 __ str(r1, FieldMemOperand(r0, i));
6748 }
6749 }
6750
6751 if (length_ > 0) {
6752 // Get hold of the elements array of the boilerplate and setup the
6753 // elements pointer in the resulting object.
6754 __ ldr(r3, FieldMemOperand(r3, JSArray::kElementsOffset));
6755 __ add(r2, r0, Operand(JSArray::kSize));
6756 __ str(r2, FieldMemOperand(r0, JSArray::kElementsOffset));
6757
6758 // Copy the elements array.
6759 for (int i = 0; i < elements_size; i += kPointerSize) {
6760 __ ldr(r1, FieldMemOperand(r3, i));
6761 __ str(r1, FieldMemOperand(r2, i));
6762 }
6763 }
6764
6765 // Return and remove the on-stack parameters.
6766 __ add(sp, sp, Operand(3 * kPointerSize));
6767 __ Ret();
6768
6769 __ bind(&slow_case);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006770 __ TailCallRuntime(Runtime::kCreateArrayLiteralShallow, 3, 1);
ager@chromium.org5c838252010-02-19 08:53:10 +00006771}
6772
6773
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006774// Takes a Smi and converts to an IEEE 64 bit floating point value in two
6775// registers. The format is 1 sign bit, 11 exponent bits (biased 1023) and
6776// 52 fraction bits (20 in the first word, 32 in the second). Zeros is a
6777// scratch register. Destroys the source register. No GC occurs during this
6778// stub so you don't have to set up the frame.
6779class ConvertToDoubleStub : public CodeStub {
6780 public:
6781 ConvertToDoubleStub(Register result_reg_1,
6782 Register result_reg_2,
6783 Register source_reg,
6784 Register scratch_reg)
6785 : result1_(result_reg_1),
6786 result2_(result_reg_2),
6787 source_(source_reg),
6788 zeros_(scratch_reg) { }
6789
6790 private:
6791 Register result1_;
6792 Register result2_;
6793 Register source_;
6794 Register zeros_;
6795
6796 // Minor key encoding in 16 bits.
6797 class ModeBits: public BitField<OverwriteMode, 0, 2> {};
6798 class OpBits: public BitField<Token::Value, 2, 14> {};
6799
6800 Major MajorKey() { return ConvertToDouble; }
6801 int MinorKey() {
6802 // Encode the parameters in a unique 16 bit value.
6803 return result1_.code() +
6804 (result2_.code() << 4) +
6805 (source_.code() << 8) +
6806 (zeros_.code() << 12);
6807 }
6808
6809 void Generate(MacroAssembler* masm);
6810
6811 const char* GetName() { return "ConvertToDoubleStub"; }
6812
6813#ifdef DEBUG
6814 void Print() { PrintF("ConvertToDoubleStub\n"); }
6815#endif
6816};
6817
6818
6819void ConvertToDoubleStub::Generate(MacroAssembler* masm) {
6820#ifndef BIG_ENDIAN_FLOATING_POINT
6821 Register exponent = result1_;
6822 Register mantissa = result2_;
6823#else
6824 Register exponent = result2_;
6825 Register mantissa = result1_;
6826#endif
6827 Label not_special;
6828 // Convert from Smi to integer.
6829 __ mov(source_, Operand(source_, ASR, kSmiTagSize));
6830 // Move sign bit from source to destination. This works because the sign bit
6831 // in the exponent word of the double has the same position and polarity as
6832 // the 2's complement sign bit in a Smi.
6833 ASSERT(HeapNumber::kSignMask == 0x80000000u);
6834 __ and_(exponent, source_, Operand(HeapNumber::kSignMask), SetCC);
6835 // Subtract from 0 if source was negative.
6836 __ rsb(source_, source_, Operand(0), LeaveCC, ne);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006837
6838 // We have -1, 0 or 1, which we treat specially. Register source_ contains
6839 // absolute value: it is either equal to 1 (special case of -1 and 1),
6840 // greater than 1 (not a special case) or less than 1 (special case of 0).
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006841 __ cmp(source_, Operand(1));
6842 __ b(gt, &not_special);
6843
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006844 // For 1 or -1 we need to or in the 0 exponent (biased to 1023).
6845 static const uint32_t exponent_word_for_1 =
6846 HeapNumber::kExponentBias << HeapNumber::kExponentShift;
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006847 __ orr(exponent, exponent, Operand(exponent_word_for_1), LeaveCC, eq);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006848 // 1, 0 and -1 all have 0 for the second word.
6849 __ mov(mantissa, Operand(0));
6850 __ Ret();
6851
6852 __ bind(&not_special);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006853 // Count leading zeros. Uses mantissa for a scratch register on pre-ARM5.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006854 // Gets the wrong answer for 0, but we already checked for that case above.
lrn@chromium.org32d961d2010-06-30 09:09:34 +00006855 __ CountLeadingZeros(zeros_, source_, mantissa);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006856 // Compute exponent and or it into the exponent register.
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00006857 // We use mantissa as a scratch register here. Use a fudge factor to
6858 // divide the constant 31 + HeapNumber::kExponentBias, 0x41d, into two parts
6859 // that fit in the ARM's constant field.
6860 int fudge = 0x400;
6861 __ rsb(mantissa, zeros_, Operand(31 + HeapNumber::kExponentBias - fudge));
6862 __ add(mantissa, mantissa, Operand(fudge));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006863 __ orr(exponent,
6864 exponent,
6865 Operand(mantissa, LSL, HeapNumber::kExponentShift));
6866 // Shift up the source chopping the top bit off.
6867 __ add(zeros_, zeros_, Operand(1));
6868 // This wouldn't work for 1.0 or -1.0 as the shift would be 32 which means 0.
6869 __ mov(source_, Operand(source_, LSL, zeros_));
6870 // Compute lower part of fraction (last 12 bits).
6871 __ mov(mantissa, Operand(source_, LSL, HeapNumber::kMantissaBitsInTopWord));
6872 // And the top (top 20 bits).
6873 __ orr(exponent,
6874 exponent,
6875 Operand(source_, LSR, 32 - HeapNumber::kMantissaBitsInTopWord));
6876 __ Ret();
6877}
6878
6879
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006880// See comment for class.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006881void WriteInt32ToHeapNumberStub::Generate(MacroAssembler* masm) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006882 Label max_negative_int;
6883 // the_int_ has the answer which is a signed int32 but not a Smi.
6884 // We test for the special value that has a different exponent. This test
6885 // has the neat side effect of setting the flags according to the sign.
6886 ASSERT(HeapNumber::kSignMask == 0x80000000u);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00006887 __ cmp(the_int_, Operand(0x80000000u));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006888 __ b(eq, &max_negative_int);
6889 // Set up the correct exponent in scratch_. All non-Smi int32s have the same.
6890 // A non-Smi integer is 1.xxx * 2^30 so the exponent is 30 (biased).
6891 uint32_t non_smi_exponent =
6892 (HeapNumber::kExponentBias + 30) << HeapNumber::kExponentShift;
6893 __ mov(scratch_, Operand(non_smi_exponent));
6894 // Set the sign bit in scratch_ if the value was negative.
6895 __ orr(scratch_, scratch_, Operand(HeapNumber::kSignMask), LeaveCC, cs);
6896 // Subtract from 0 if the value was negative.
6897 __ rsb(the_int_, the_int_, Operand(0), LeaveCC, cs);
6898 // We should be masking the implict first digit of the mantissa away here,
6899 // but it just ends up combining harmlessly with the last digit of the
6900 // exponent that happens to be 1. The sign bit is 0 so we shift 10 to get
6901 // the most significant 1 to hit the last bit of the 12 bit sign and exponent.
6902 ASSERT(((1 << HeapNumber::kExponentShift) & non_smi_exponent) != 0);
6903 const int shift_distance = HeapNumber::kNonMantissaBitsInTopWord - 2;
6904 __ orr(scratch_, scratch_, Operand(the_int_, LSR, shift_distance));
6905 __ str(scratch_, FieldMemOperand(the_heap_number_,
6906 HeapNumber::kExponentOffset));
6907 __ mov(scratch_, Operand(the_int_, LSL, 32 - shift_distance));
6908 __ str(scratch_, FieldMemOperand(the_heap_number_,
6909 HeapNumber::kMantissaOffset));
6910 __ Ret();
6911
6912 __ bind(&max_negative_int);
6913 // The max negative int32 is stored as a positive number in the mantissa of
6914 // a double because it uses a sign bit instead of using two's complement.
6915 // The actual mantissa bits stored are all 0 because the implicit most
6916 // significant 1 bit is not stored.
6917 non_smi_exponent += 1 << HeapNumber::kExponentShift;
6918 __ mov(ip, Operand(HeapNumber::kSignMask | non_smi_exponent));
6919 __ str(ip, FieldMemOperand(the_heap_number_, HeapNumber::kExponentOffset));
6920 __ mov(ip, Operand(0));
6921 __ str(ip, FieldMemOperand(the_heap_number_, HeapNumber::kMantissaOffset));
6922 __ Ret();
6923}
6924
6925
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006926// Handle the case where the lhs and rhs are the same object.
6927// Equality is almost reflexive (everything but NaN), so this is a test
6928// for "identity and not NaN".
6929static void EmitIdenticalObjectComparison(MacroAssembler* masm,
6930 Label* slow,
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006931 Condition cc,
6932 bool never_nan_nan) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006933 Label not_identical;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006934 Label heap_number, return_equal;
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006935 __ cmp(r0, r1);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006936 __ b(ne, &not_identical);
6937
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006938 // The two objects are identical. If we know that one of them isn't NaN then
6939 // we now know they test equal.
6940 if (cc != eq || !never_nan_nan) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006941 // Test for NaN. Sadly, we can't just compare to Factory::nan_value(),
6942 // so we do the second best thing - test it ourselves.
6943 // They are both equal and they are not both Smis so both of them are not
6944 // Smis. If it's not a heap number, then return equal.
6945 if (cc == lt || cc == gt) {
6946 __ CompareObjectType(r0, r4, r4, FIRST_JS_OBJECT_TYPE);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006947 __ b(ge, slow);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006948 } else {
6949 __ CompareObjectType(r0, r4, r4, HEAP_NUMBER_TYPE);
6950 __ b(eq, &heap_number);
6951 // Comparing JS objects with <=, >= is complicated.
6952 if (cc != eq) {
6953 __ cmp(r4, Operand(FIRST_JS_OBJECT_TYPE));
6954 __ b(ge, slow);
6955 // Normally here we fall through to return_equal, but undefined is
6956 // special: (undefined == undefined) == true, but
6957 // (undefined <= undefined) == false! See ECMAScript 11.8.5.
6958 if (cc == le || cc == ge) {
6959 __ cmp(r4, Operand(ODDBALL_TYPE));
6960 __ b(ne, &return_equal);
6961 __ LoadRoot(r2, Heap::kUndefinedValueRootIndex);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006962 __ cmp(r0, r2);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006963 __ b(ne, &return_equal);
6964 if (cc == le) {
6965 // undefined <= undefined should fail.
6966 __ mov(r0, Operand(GREATER));
6967 } else {
6968 // undefined >= undefined should fail.
6969 __ mov(r0, Operand(LESS));
6970 }
6971 __ mov(pc, Operand(lr)); // Return.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006972 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006973 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006974 }
6975 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006976
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006977 __ bind(&return_equal);
6978 if (cc == lt) {
6979 __ mov(r0, Operand(GREATER)); // Things aren't less than themselves.
6980 } else if (cc == gt) {
6981 __ mov(r0, Operand(LESS)); // Things aren't greater than themselves.
6982 } else {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006983 __ mov(r0, Operand(EQUAL)); // Things are <=, >=, ==, === themselves.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006984 }
6985 __ mov(pc, Operand(lr)); // Return.
6986
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006987 if (cc != eq || !never_nan_nan) {
6988 // For less and greater we don't have to check for NaN since the result of
6989 // x < x is false regardless. For the others here is some code to check
6990 // for NaN.
6991 if (cc != lt && cc != gt) {
6992 __ bind(&heap_number);
6993 // It is a heap number, so return non-equal if it's NaN and equal if it's
6994 // not NaN.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006995
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006996 // The representation of NaN values has all exponent bits (52..62) set,
6997 // and not all mantissa bits (0..51) clear.
6998 // Read top bits of double representation (second word of value).
6999 __ ldr(r2, FieldMemOperand(r0, HeapNumber::kExponentOffset));
7000 // Test that exponent bits are all set.
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00007001 __ Sbfx(r3, r2, HeapNumber::kExponentShift, HeapNumber::kExponentBits);
7002 // NaNs have all-one exponents so they sign extend to -1.
7003 __ cmp(r3, Operand(-1));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007004 __ b(ne, &return_equal);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007005
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007006 // Shift out flag and all exponent bits, retaining only mantissa.
7007 __ mov(r2, Operand(r2, LSL, HeapNumber::kNonMantissaBitsInTopWord));
7008 // Or with all low-bits of mantissa.
7009 __ ldr(r3, FieldMemOperand(r0, HeapNumber::kMantissaOffset));
7010 __ orr(r0, r3, Operand(r2), SetCC);
7011 // For equal we already have the right value in r0: Return zero (equal)
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007012 // if all bits in mantissa are zero (it's an Infinity) and non-zero if
7013 // not (it's a NaN). For <= and >= we need to load r0 with the failing
7014 // value if it's a NaN.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007015 if (cc != eq) {
7016 // All-zero means Infinity means equal.
7017 __ mov(pc, Operand(lr), LeaveCC, eq); // Return equal
7018 if (cc == le) {
7019 __ mov(r0, Operand(GREATER)); // NaN <= NaN should fail.
7020 } else {
7021 __ mov(r0, Operand(LESS)); // NaN >= NaN should fail.
7022 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007023 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007024 __ mov(pc, Operand(lr)); // Return.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007025 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007026 // No fall through here.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007027 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007028
7029 __ bind(&not_identical);
7030}
7031
7032
7033// See comment at call site.
7034static void EmitSmiNonsmiComparison(MacroAssembler* masm,
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007035 Label* lhs_not_nan,
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007036 Label* slow,
7037 bool strict) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007038 Label rhs_is_smi;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007039 __ tst(r0, Operand(kSmiTagMask));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007040 __ b(eq, &rhs_is_smi);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007041
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007042 // Lhs is a Smi. Check whether the rhs is a heap number.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007043 __ CompareObjectType(r0, r4, r4, HEAP_NUMBER_TYPE);
7044 if (strict) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007045 // If rhs is not a number and lhs is a Smi then strict equality cannot
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007046 // succeed. Return non-equal (r0 is already not zero)
7047 __ mov(pc, Operand(lr), LeaveCC, ne); // Return.
7048 } else {
7049 // Smi compared non-strictly with a non-Smi non-heap-number. Call
7050 // the runtime.
7051 __ b(ne, slow);
7052 }
7053
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007054 // Lhs (r1) is a smi, rhs (r0) is a number.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007055 if (CpuFeatures::IsSupported(VFP3)) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007056 // Convert lhs to a double in d7 .
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007057 CpuFeatures::Scope scope(VFP3);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007058 __ mov(r7, Operand(r1, ASR, kSmiTagSize));
7059 __ vmov(s15, r7);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007060 __ vcvt_f64_s32(d7, s15);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007061 // Load the double from rhs, tagged HeapNumber r0, to d6.
7062 __ sub(r7, r0, Operand(kHeapObjectTag));
7063 __ vldr(d6, r7, HeapNumber::kValueOffset);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007064 } else {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007065 __ push(lr);
7066 // Convert lhs to a double in r2, r3.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007067 __ mov(r7, Operand(r1));
7068 ConvertToDoubleStub stub1(r3, r2, r7, r6);
7069 __ Call(stub1.GetCode(), RelocInfo::CODE_TARGET);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007070 // Load rhs to a double in r0, r1.
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00007071 __ Ldrd(r0, r1, FieldMemOperand(r0, HeapNumber::kValueOffset));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007072 __ pop(lr);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007073 }
7074
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007075 // We now have both loaded as doubles but we can skip the lhs nan check
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007076 // since it's a smi.
7077 __ jmp(lhs_not_nan);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007078
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007079 __ bind(&rhs_is_smi);
7080 // Rhs is a smi. Check whether the non-smi lhs is a heap number.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007081 __ CompareObjectType(r1, r4, r4, HEAP_NUMBER_TYPE);
7082 if (strict) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007083 // If lhs is not a number and rhs is a smi then strict equality cannot
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007084 // succeed. Return non-equal.
7085 __ mov(r0, Operand(1), LeaveCC, ne); // Non-zero indicates not equal.
7086 __ mov(pc, Operand(lr), LeaveCC, ne); // Return.
7087 } else {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007088 // Smi compared non-strictly with a non-smi non-heap-number. Call
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007089 // the runtime.
7090 __ b(ne, slow);
7091 }
7092
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007093 // Rhs (r0) is a smi, lhs (r1) is a heap number.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007094 if (CpuFeatures::IsSupported(VFP3)) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007095 // Convert rhs to a double in d6 .
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007096 CpuFeatures::Scope scope(VFP3);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007097 // Load the double from lhs, tagged HeapNumber r1, to d7.
7098 __ sub(r7, r1, Operand(kHeapObjectTag));
7099 __ vldr(d7, r7, HeapNumber::kValueOffset);
7100 __ mov(r7, Operand(r0, ASR, kSmiTagSize));
7101 __ vmov(s13, r7);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007102 __ vcvt_f64_s32(d6, s13);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007103 } else {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007104 __ push(lr);
7105 // Load lhs to a double in r2, r3.
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00007106 __ Ldrd(r2, r3, FieldMemOperand(r1, HeapNumber::kValueOffset));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007107 // Convert rhs to a double in r0, r1.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007108 __ mov(r7, Operand(r0));
7109 ConvertToDoubleStub stub2(r1, r0, r7, r6);
7110 __ Call(stub2.GetCode(), RelocInfo::CODE_TARGET);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007111 __ pop(lr);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007112 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007113 // Fall through to both_loaded_as_doubles.
7114}
7115
7116
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007117void EmitNanCheck(MacroAssembler* masm, Label* lhs_not_nan, Condition cc) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007118 bool exp_first = (HeapNumber::kExponentOffset == HeapNumber::kValueOffset);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007119 Register rhs_exponent = exp_first ? r0 : r1;
7120 Register lhs_exponent = exp_first ? r2 : r3;
7121 Register rhs_mantissa = exp_first ? r1 : r0;
7122 Register lhs_mantissa = exp_first ? r3 : r2;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007123 Label one_is_nan, neither_is_nan;
7124
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00007125 __ Sbfx(r4,
7126 lhs_exponent,
7127 HeapNumber::kExponentShift,
7128 HeapNumber::kExponentBits);
7129 // NaNs have all-one exponents so they sign extend to -1.
7130 __ cmp(r4, Operand(-1));
7131 __ b(ne, lhs_not_nan);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007132 __ mov(r4,
7133 Operand(lhs_exponent, LSL, HeapNumber::kNonMantissaBitsInTopWord),
7134 SetCC);
7135 __ b(ne, &one_is_nan);
7136 __ cmp(lhs_mantissa, Operand(0));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007137 __ b(ne, &one_is_nan);
7138
7139 __ bind(lhs_not_nan);
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00007140 __ Sbfx(r4,
7141 rhs_exponent,
7142 HeapNumber::kExponentShift,
7143 HeapNumber::kExponentBits);
7144 // NaNs have all-one exponents so they sign extend to -1.
7145 __ cmp(r4, Operand(-1));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007146 __ b(ne, &neither_is_nan);
7147 __ mov(r4,
7148 Operand(rhs_exponent, LSL, HeapNumber::kNonMantissaBitsInTopWord),
7149 SetCC);
7150 __ b(ne, &one_is_nan);
7151 __ cmp(rhs_mantissa, Operand(0));
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007152 __ b(eq, &neither_is_nan);
7153
7154 __ bind(&one_is_nan);
7155 // NaN comparisons always fail.
7156 // Load whatever we need in r0 to make the comparison fail.
7157 if (cc == lt || cc == le) {
7158 __ mov(r0, Operand(GREATER));
7159 } else {
7160 __ mov(r0, Operand(LESS));
7161 }
7162 __ mov(pc, Operand(lr)); // Return.
7163
7164 __ bind(&neither_is_nan);
7165}
7166
7167
7168// See comment at call site.
7169static void EmitTwoNonNanDoubleComparison(MacroAssembler* masm, Condition cc) {
7170 bool exp_first = (HeapNumber::kExponentOffset == HeapNumber::kValueOffset);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007171 Register rhs_exponent = exp_first ? r0 : r1;
7172 Register lhs_exponent = exp_first ? r2 : r3;
7173 Register rhs_mantissa = exp_first ? r1 : r0;
7174 Register lhs_mantissa = exp_first ? r3 : r2;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007175
7176 // r0, r1, r2, r3 have the two doubles. Neither is a NaN.
7177 if (cc == eq) {
7178 // Doubles are not equal unless they have the same bit pattern.
7179 // Exception: 0 and -0.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007180 __ cmp(rhs_mantissa, Operand(lhs_mantissa));
7181 __ orr(r0, rhs_mantissa, Operand(lhs_mantissa), LeaveCC, ne);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007182 // Return non-zero if the numbers are unequal.
7183 __ mov(pc, Operand(lr), LeaveCC, ne);
7184
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007185 __ sub(r0, rhs_exponent, Operand(lhs_exponent), SetCC);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007186 // If exponents are equal then return 0.
7187 __ mov(pc, Operand(lr), LeaveCC, eq);
7188
7189 // Exponents are unequal. The only way we can return that the numbers
7190 // are equal is if one is -0 and the other is 0. We already dealt
7191 // with the case where both are -0 or both are 0.
7192 // We start by seeing if the mantissas (that are equal) or the bottom
7193 // 31 bits of the rhs exponent are non-zero. If so we return not
7194 // equal.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007195 __ orr(r4, lhs_mantissa, Operand(lhs_exponent, LSL, kSmiTagSize), SetCC);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007196 __ mov(r0, Operand(r4), LeaveCC, ne);
7197 __ mov(pc, Operand(lr), LeaveCC, ne); // Return conditionally.
7198 // Now they are equal if and only if the lhs exponent is zero in its
7199 // low 31 bits.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007200 __ mov(r0, Operand(rhs_exponent, LSL, kSmiTagSize));
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007201 __ mov(pc, Operand(lr));
7202 } else {
7203 // Call a native function to do a comparison between two non-NaNs.
7204 // Call C routine that may not cause GC or other trouble.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00007205 __ push(lr);
7206 __ PrepareCallCFunction(4, r5); // Two doubles count as 4 arguments.
7207 __ CallCFunction(ExternalReference::compare_doubles(), 4);
7208 __ pop(pc); // Return.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007209 }
7210}
7211
7212
7213// See comment at call site.
7214static void EmitStrictTwoHeapObjectCompare(MacroAssembler* masm) {
7215 // If either operand is a JSObject or an oddball value, then they are
7216 // not equal since their pointers are different.
7217 // There is no test for undetectability in strict equality.
7218 ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
7219 Label first_non_object;
7220 // Get the type of the first operand into r2 and compare it with
7221 // FIRST_JS_OBJECT_TYPE.
7222 __ CompareObjectType(r0, r2, r2, FIRST_JS_OBJECT_TYPE);
7223 __ b(lt, &first_non_object);
7224
7225 // Return non-zero (r0 is not zero)
7226 Label return_not_equal;
7227 __ bind(&return_not_equal);
7228 __ mov(pc, Operand(lr)); // Return.
7229
7230 __ bind(&first_non_object);
7231 // Check for oddballs: true, false, null, undefined.
7232 __ cmp(r2, Operand(ODDBALL_TYPE));
7233 __ b(eq, &return_not_equal);
7234
7235 __ CompareObjectType(r1, r3, r3, FIRST_JS_OBJECT_TYPE);
7236 __ b(ge, &return_not_equal);
7237
7238 // Check for oddballs: true, false, null, undefined.
7239 __ cmp(r3, Operand(ODDBALL_TYPE));
7240 __ b(eq, &return_not_equal);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007241
7242 // Now that we have the types we might as well check for symbol-symbol.
7243 // Ensure that no non-strings have the symbol bit set.
7244 ASSERT(kNotStringTag + kIsSymbolMask > LAST_TYPE);
7245 ASSERT(kSymbolTag != 0);
7246 __ and_(r2, r2, Operand(r3));
7247 __ tst(r2, Operand(kIsSymbolMask));
7248 __ b(ne, &return_not_equal);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007249}
7250
7251
7252// See comment at call site.
7253static void EmitCheckForTwoHeapNumbers(MacroAssembler* masm,
7254 Label* both_loaded_as_doubles,
7255 Label* not_heap_numbers,
7256 Label* slow) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007257 __ CompareObjectType(r0, r3, r2, HEAP_NUMBER_TYPE);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007258 __ b(ne, not_heap_numbers);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007259 __ ldr(r2, FieldMemOperand(r1, HeapObject::kMapOffset));
7260 __ cmp(r2, r3);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007261 __ b(ne, slow); // First was a heap number, second wasn't. Go slow case.
7262
7263 // Both are heap numbers. Load them up then jump to the code we have
7264 // for that.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007265 if (CpuFeatures::IsSupported(VFP3)) {
7266 CpuFeatures::Scope scope(VFP3);
7267 __ sub(r7, r0, Operand(kHeapObjectTag));
7268 __ vldr(d6, r7, HeapNumber::kValueOffset);
7269 __ sub(r7, r1, Operand(kHeapObjectTag));
7270 __ vldr(d7, r7, HeapNumber::kValueOffset);
7271 } else {
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00007272 __ Ldrd(r2, r3, FieldMemOperand(r1, HeapNumber::kValueOffset));
7273 __ Ldrd(r0, r1, FieldMemOperand(r0, HeapNumber::kValueOffset));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007274 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007275 __ jmp(both_loaded_as_doubles);
7276}
7277
7278
7279// Fast negative check for symbol-to-symbol equality.
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00007280static void EmitCheckForSymbolsOrObjects(MacroAssembler* masm,
7281 Label* possible_strings,
7282 Label* not_both_strings) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007283 // r2 is object type of r0.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007284 // Ensure that no non-strings have the symbol bit set.
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00007285 Label object_test;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007286 ASSERT(kSymbolTag != 0);
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00007287 __ tst(r2, Operand(kIsNotStringMask));
7288 __ b(ne, &object_test);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007289 __ tst(r2, Operand(kIsSymbolMask));
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00007290 __ b(eq, possible_strings);
7291 __ CompareObjectType(r1, r3, r3, FIRST_NONSTRING_TYPE);
7292 __ b(ge, not_both_strings);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007293 __ tst(r3, Operand(kIsSymbolMask));
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00007294 __ b(eq, possible_strings);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007295
7296 // Both are symbols. We already checked they weren't the same pointer
7297 // so they are not equal.
7298 __ mov(r0, Operand(1)); // Non-zero indicates not equal.
7299 __ mov(pc, Operand(lr)); // Return.
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00007300
7301 __ bind(&object_test);
7302 __ cmp(r2, Operand(FIRST_JS_OBJECT_TYPE));
7303 __ b(lt, not_both_strings);
7304 __ CompareObjectType(r1, r2, r3, FIRST_JS_OBJECT_TYPE);
7305 __ b(lt, not_both_strings);
7306 // If both objects are undetectable, they are equal. Otherwise, they
7307 // are not equal, since they are different objects and an object is not
7308 // equal to undefined.
7309 __ ldr(r3, FieldMemOperand(r0, HeapObject::kMapOffset));
7310 __ ldrb(r2, FieldMemOperand(r2, Map::kBitFieldOffset));
7311 __ ldrb(r3, FieldMemOperand(r3, Map::kBitFieldOffset));
7312 __ and_(r0, r2, Operand(r3));
7313 __ and_(r0, r0, Operand(1 << Map::kIsUndetectable));
7314 __ eor(r0, r0, Operand(1 << Map::kIsUndetectable));
7315 __ mov(pc, Operand(lr)); // Return.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007316}
7317
7318
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007319void NumberToStringStub::GenerateLookupNumberStringCache(MacroAssembler* masm,
7320 Register object,
7321 Register result,
7322 Register scratch1,
7323 Register scratch2,
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00007324 Register scratch3,
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007325 bool object_is_smi,
7326 Label* not_found) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007327 // Use of registers. Register result is used as a temporary.
7328 Register number_string_cache = result;
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00007329 Register mask = scratch3;
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007330
7331 // Load the number string cache.
7332 __ LoadRoot(number_string_cache, Heap::kNumberStringCacheRootIndex);
7333
7334 // Make the hash mask from the length of the number string cache. It
7335 // contains two elements (number and string) for each cache entry.
7336 __ ldr(mask, FieldMemOperand(number_string_cache, FixedArray::kLengthOffset));
ricow@chromium.org30ce4112010-05-31 10:38:25 +00007337 // Divide length by two (length is a smi).
7338 __ mov(mask, Operand(mask, ASR, kSmiTagSize + 1));
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007339 __ sub(mask, mask, Operand(1)); // Make mask.
7340
7341 // Calculate the entry in the number string cache. The hash value in the
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00007342 // number string cache for smis is just the smi value, and the hash for
7343 // doubles is the xor of the upper and lower words. See
7344 // Heap::GetNumberStringCache.
7345 Label is_smi;
7346 Label load_result_from_cache;
7347 if (!object_is_smi) {
7348 __ BranchOnSmi(object, &is_smi);
7349 if (CpuFeatures::IsSupported(VFP3)) {
7350 CpuFeatures::Scope scope(VFP3);
7351 __ CheckMap(object,
7352 scratch1,
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00007353 Heap::kHeapNumberMapRootIndex,
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00007354 not_found,
7355 true);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007356
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00007357 ASSERT_EQ(8, kDoubleSize);
7358 __ add(scratch1,
7359 object,
7360 Operand(HeapNumber::kValueOffset - kHeapObjectTag));
7361 __ ldm(ia, scratch1, scratch1.bit() | scratch2.bit());
7362 __ eor(scratch1, scratch1, Operand(scratch2));
7363 __ and_(scratch1, scratch1, Operand(mask));
7364
7365 // Calculate address of entry in string cache: each entry consists
7366 // of two pointer sized fields.
7367 __ add(scratch1,
7368 number_string_cache,
7369 Operand(scratch1, LSL, kPointerSizeLog2 + 1));
7370
7371 Register probe = mask;
7372 __ ldr(probe,
7373 FieldMemOperand(scratch1, FixedArray::kHeaderSize));
7374 __ BranchOnSmi(probe, not_found);
7375 __ sub(scratch2, object, Operand(kHeapObjectTag));
7376 __ vldr(d0, scratch2, HeapNumber::kValueOffset);
7377 __ sub(probe, probe, Operand(kHeapObjectTag));
7378 __ vldr(d1, probe, HeapNumber::kValueOffset);
7379 __ vcmp(d0, d1);
7380 __ vmrs(pc);
7381 __ b(ne, not_found); // The cache did not contain this value.
7382 __ b(&load_result_from_cache);
7383 } else {
7384 __ b(not_found);
7385 }
7386 }
7387
7388 __ bind(&is_smi);
7389 Register scratch = scratch1;
7390 __ and_(scratch, mask, Operand(object, ASR, 1));
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007391 // Calculate address of entry in string cache: each entry consists
7392 // of two pointer sized fields.
7393 __ add(scratch,
7394 number_string_cache,
7395 Operand(scratch, LSL, kPointerSizeLog2 + 1));
7396
7397 // Check if the entry is the smi we are looking for.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00007398 Register probe = mask;
7399 __ ldr(probe, FieldMemOperand(scratch, FixedArray::kHeaderSize));
7400 __ cmp(object, probe);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007401 __ b(ne, not_found);
7402
7403 // Get the result from the cache.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00007404 __ bind(&load_result_from_cache);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007405 __ ldr(result,
7406 FieldMemOperand(scratch, FixedArray::kHeaderSize + kPointerSize));
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007407 __ IncrementCounter(&Counters::number_to_string_native,
7408 1,
7409 scratch1,
7410 scratch2);
7411}
7412
7413
7414void NumberToStringStub::Generate(MacroAssembler* masm) {
7415 Label runtime;
7416
7417 __ ldr(r1, MemOperand(sp, 0));
7418
7419 // Generate code to lookup number in the number string cache.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00007420 GenerateLookupNumberStringCache(masm, r1, r0, r2, r3, r4, false, &runtime);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007421 __ add(sp, sp, Operand(1 * kPointerSize));
7422 __ Ret();
7423
7424 __ bind(&runtime);
7425 // Handle number to string in the runtime system if not found in the cache.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00007426 __ TailCallRuntime(Runtime::kNumberToStringSkipCache, 1, 1);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007427}
7428
7429
ager@chromium.orgac091b72010-05-05 07:34:42 +00007430void RecordWriteStub::Generate(MacroAssembler* masm) {
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00007431 __ add(offset_, object_, Operand(offset_));
7432 __ RecordWriteHelper(object_, offset_, scratch_);
ager@chromium.orgac091b72010-05-05 07:34:42 +00007433 __ Ret();
7434}
7435
7436
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007437// On entry r0 (rhs) and r1 (lhs) are the values to be compared.
7438// On exit r0 is 0, positive or negative to indicate the result of
7439// the comparison.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007440void CompareStub::Generate(MacroAssembler* masm) {
7441 Label slow; // Call builtin.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007442 Label not_smis, both_loaded_as_doubles, lhs_not_nan;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007443
7444 // NOTICE! This code is only reached after a smi-fast-case check, so
7445 // it is certain that at least one operand isn't a smi.
7446
7447 // Handle the case where the objects are identical. Either returns the answer
7448 // or goes to slow. Only falls through if the objects were not identical.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007449 EmitIdenticalObjectComparison(masm, &slow, cc_, never_nan_nan_);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007450
7451 // If either is a Smi (we know that not both are), then they can only
7452 // be strictly equal if the other is a HeapNumber.
7453 ASSERT_EQ(0, kSmiTag);
7454 ASSERT_EQ(0, Smi::FromInt(0));
7455 __ and_(r2, r0, Operand(r1));
7456 __ tst(r2, Operand(kSmiTagMask));
7457 __ b(ne, &not_smis);
7458 // One operand is a smi. EmitSmiNonsmiComparison generates code that can:
7459 // 1) Return the answer.
7460 // 2) Go to slow.
7461 // 3) Fall through to both_loaded_as_doubles.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007462 // 4) Jump to lhs_not_nan.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007463 // In cases 3 and 4 we have found out we were dealing with a number-number
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007464 // comparison. If VFP3 is supported the double values of the numbers have
7465 // been loaded into d7 and d6. Otherwise, the double values have been loaded
7466 // into r0, r1, r2, and r3.
7467 EmitSmiNonsmiComparison(masm, &lhs_not_nan, &slow, strict_);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007468
7469 __ bind(&both_loaded_as_doubles);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007470 // The arguments have been converted to doubles and stored in d6 and d7, if
7471 // VFP3 is supported, or in r0, r1, r2, and r3.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007472 if (CpuFeatures::IsSupported(VFP3)) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007473 __ bind(&lhs_not_nan);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007474 CpuFeatures::Scope scope(VFP3);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007475 Label no_nan;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007476 // ARMv7 VFP3 instructions to implement double precision comparison.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007477 __ vcmp(d7, d6);
7478 __ vmrs(pc); // Move vector status bits to normal status bits.
7479 Label nan;
7480 __ b(vs, &nan);
7481 __ mov(r0, Operand(EQUAL), LeaveCC, eq);
7482 __ mov(r0, Operand(LESS), LeaveCC, lt);
7483 __ mov(r0, Operand(GREATER), LeaveCC, gt);
7484 __ mov(pc, Operand(lr));
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007485
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007486 __ bind(&nan);
7487 // If one of the sides was a NaN then the v flag is set. Load r0 with
7488 // whatever it takes to make the comparison fail, since comparisons with NaN
7489 // always fail.
7490 if (cc_ == lt || cc_ == le) {
7491 __ mov(r0, Operand(GREATER));
7492 } else {
7493 __ mov(r0, Operand(LESS));
7494 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007495 __ mov(pc, Operand(lr));
7496 } else {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007497 // Checks for NaN in the doubles we have loaded. Can return the answer or
7498 // fall through if neither is a NaN. Also binds lhs_not_nan.
7499 EmitNanCheck(masm, &lhs_not_nan, cc_);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007500 // Compares two doubles in r0, r1, r2, r3 that are not NaNs. Returns the
7501 // answer. Never falls through.
7502 EmitTwoNonNanDoubleComparison(masm, cc_);
7503 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007504
7505 __ bind(&not_smis);
7506 // At this point we know we are dealing with two different objects,
7507 // and neither of them is a Smi. The objects are in r0 and r1.
7508 if (strict_) {
7509 // This returns non-equal for some object types, or falls through if it
7510 // was not lucky.
7511 EmitStrictTwoHeapObjectCompare(masm);
7512 }
7513
7514 Label check_for_symbols;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007515 Label flat_string_check;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007516 // Check for heap-number-heap-number comparison. Can jump to slow case,
7517 // or load both doubles into r0, r1, r2, r3 and jump to the code that handles
7518 // that case. If the inputs are not doubles then jumps to check_for_symbols.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007519 // In this case r2 will contain the type of r0. Never falls through.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007520 EmitCheckForTwoHeapNumbers(masm,
7521 &both_loaded_as_doubles,
7522 &check_for_symbols,
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007523 &flat_string_check);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007524
7525 __ bind(&check_for_symbols);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007526 // In the strict case the EmitStrictTwoHeapObjectCompare already took care of
7527 // symbols.
7528 if (cc_ == eq && !strict_) {
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00007529 // Returns an answer for two symbols or two detectable objects.
7530 // Otherwise jumps to string case or not both strings case.
7531 // Assumes that r2 is the type of r0 on entry.
7532 EmitCheckForSymbolsOrObjects(masm, &flat_string_check, &slow);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007533 }
7534
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007535 // Check for both being sequential ASCII strings, and inline if that is the
7536 // case.
7537 __ bind(&flat_string_check);
7538
7539 __ JumpIfNonSmisNotBothSequentialAsciiStrings(r0, r1, r2, r3, &slow);
7540
7541 __ IncrementCounter(&Counters::string_compare_native, 1, r2, r3);
7542 StringCompareStub::GenerateCompareFlatAsciiStrings(masm,
7543 r1,
7544 r0,
7545 r2,
7546 r3,
7547 r4,
7548 r5);
7549 // Never falls through to here.
7550
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007551 __ bind(&slow);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007552
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00007553 __ Push(r1, r0);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007554 // Figure out which native to call and setup the arguments.
7555 Builtins::JavaScript native;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007556 if (cc_ == eq) {
7557 native = strict_ ? Builtins::STRICT_EQUALS : Builtins::EQUALS;
7558 } else {
7559 native = Builtins::COMPARE;
7560 int ncr; // NaN compare result
7561 if (cc_ == lt || cc_ == le) {
7562 ncr = GREATER;
7563 } else {
7564 ASSERT(cc_ == gt || cc_ == ge); // remaining cases
7565 ncr = LESS;
7566 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007567 __ mov(r0, Operand(Smi::FromInt(ncr)));
7568 __ push(r0);
7569 }
7570
7571 // Call the native; it returns -1 (less), 0 (equal), or 1 (greater)
7572 // tagged as a small integer.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007573 __ InvokeBuiltin(native, JUMP_JS);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007574}
7575
7576
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00007577// We fall into this code if the operands were Smis, but the result was
7578// not (eg. overflow). We branch into this code (to the not_smi label) if
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007579// the operands were not both Smi. The operands are in r0 and r1. In order
7580// to call the C-implemented binary fp operation routines we need to end up
7581// with the double precision floating point operands in r0 and r1 (for the
7582// value in r1) and r2 and r3 (for the value in r0).
ager@chromium.org357bf652010-04-12 11:30:10 +00007583void GenericBinaryOpStub::HandleBinaryOpSlowCases(
7584 MacroAssembler* masm,
7585 Label* not_smi,
7586 Register lhs,
7587 Register rhs,
7588 const Builtins::JavaScript& builtin) {
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00007589 Label slow, slow_reverse, do_the_call;
ager@chromium.org357bf652010-04-12 11:30:10 +00007590 bool use_fp_registers = CpuFeatures::IsSupported(VFP3) && Token::MOD != op_;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007591
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00007592 ASSERT((lhs.is(r0) && rhs.is(r1)) || (lhs.is(r1) && rhs.is(r0)));
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00007593 Register heap_number_map = r6;
ager@chromium.org357bf652010-04-12 11:30:10 +00007594
7595 if (ShouldGenerateSmiCode()) {
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00007596 __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
7597
ager@chromium.org357bf652010-04-12 11:30:10 +00007598 // Smi-smi case (overflow).
7599 // Since both are Smis there is no heap number to overwrite, so allocate.
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00007600 // The new heap number is in r5. r3 and r7 are scratch.
7601 __ AllocateHeapNumber(
7602 r5, r3, r7, heap_number_map, lhs.is(r0) ? &slow_reverse : &slow);
ager@chromium.org357bf652010-04-12 11:30:10 +00007603
7604 // If we have floating point hardware, inline ADD, SUB, MUL, and DIV,
7605 // using registers d7 and d6 for the double values.
lrn@chromium.org32d961d2010-06-30 09:09:34 +00007606 if (CpuFeatures::IsSupported(VFP3)) {
ager@chromium.org357bf652010-04-12 11:30:10 +00007607 CpuFeatures::Scope scope(VFP3);
7608 __ mov(r7, Operand(rhs, ASR, kSmiTagSize));
7609 __ vmov(s15, r7);
7610 __ vcvt_f64_s32(d7, s15);
7611 __ mov(r7, Operand(lhs, ASR, kSmiTagSize));
7612 __ vmov(s13, r7);
7613 __ vcvt_f64_s32(d6, s13);
lrn@chromium.org32d961d2010-06-30 09:09:34 +00007614 if (!use_fp_registers) {
7615 __ vmov(r2, r3, d7);
7616 __ vmov(r0, r1, d6);
7617 }
ager@chromium.org357bf652010-04-12 11:30:10 +00007618 } else {
lrn@chromium.org32d961d2010-06-30 09:09:34 +00007619 // Write Smi from rhs to r3 and r2 in double format. r9 is scratch.
ager@chromium.org357bf652010-04-12 11:30:10 +00007620 __ mov(r7, Operand(rhs));
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00007621 ConvertToDoubleStub stub1(r3, r2, r7, r9);
ager@chromium.org357bf652010-04-12 11:30:10 +00007622 __ push(lr);
7623 __ Call(stub1.GetCode(), RelocInfo::CODE_TARGET);
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00007624 // Write Smi from lhs to r1 and r0 in double format. r9 is scratch.
ager@chromium.org357bf652010-04-12 11:30:10 +00007625 __ mov(r7, Operand(lhs));
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00007626 ConvertToDoubleStub stub2(r1, r0, r7, r9);
ager@chromium.org357bf652010-04-12 11:30:10 +00007627 __ Call(stub2.GetCode(), RelocInfo::CODE_TARGET);
7628 __ pop(lr);
7629 }
7630 __ jmp(&do_the_call); // Tail call. No return.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007631 }
7632
ager@chromium.org357bf652010-04-12 11:30:10 +00007633 // We branch here if at least one of r0 and r1 is not a Smi.
7634 __ bind(not_smi);
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00007635 __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007636
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00007637 // After this point we have the left hand side in r1 and the right hand side
7638 // in r0.
ager@chromium.org357bf652010-04-12 11:30:10 +00007639 if (lhs.is(r0)) {
7640 __ Swap(r0, r1, ip);
7641 }
7642
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00007643 // The type transition also calculates the answer.
7644 bool generate_code_to_calculate_answer = true;
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00007645
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00007646 if (ShouldGenerateFPCode()) {
ager@chromium.org357bf652010-04-12 11:30:10 +00007647 if (runtime_operands_type_ == BinaryOpIC::DEFAULT) {
7648 switch (op_) {
7649 case Token::ADD:
7650 case Token::SUB:
7651 case Token::MUL:
7652 case Token::DIV:
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00007653 GenerateTypeTransition(masm); // Tail call.
7654 generate_code_to_calculate_answer = false;
ager@chromium.org357bf652010-04-12 11:30:10 +00007655 break;
7656
7657 default:
7658 break;
7659 }
7660 }
7661
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00007662 if (generate_code_to_calculate_answer) {
7663 Label r0_is_smi, r1_is_smi, finished_loading_r0, finished_loading_r1;
7664 if (mode_ == NO_OVERWRITE) {
7665 // In the case where there is no chance of an overwritable float we may
7666 // as well do the allocation immediately while r0 and r1 are untouched.
7667 __ AllocateHeapNumber(r5, r3, r7, heap_number_map, &slow);
lrn@chromium.org32d961d2010-06-30 09:09:34 +00007668 }
ager@chromium.org357bf652010-04-12 11:30:10 +00007669
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00007670 // Move r0 to a double in r2-r3.
7671 __ tst(r0, Operand(kSmiTagMask));
7672 __ b(eq, &r0_is_smi); // It's a Smi so don't check it's a heap number.
7673 __ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset));
7674 __ AssertRegisterIsRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
7675 __ cmp(r4, heap_number_map);
7676 __ b(ne, &slow);
7677 if (mode_ == OVERWRITE_RIGHT) {
7678 __ mov(r5, Operand(r0)); // Overwrite this heap number.
lrn@chromium.org32d961d2010-06-30 09:09:34 +00007679 }
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00007680 if (use_fp_registers) {
7681 CpuFeatures::Scope scope(VFP3);
7682 // Load the double from tagged HeapNumber r0 to d7.
7683 __ sub(r7, r0, Operand(kHeapObjectTag));
7684 __ vldr(d7, r7, HeapNumber::kValueOffset);
ager@chromium.org357bf652010-04-12 11:30:10 +00007685 } else {
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00007686 // Calling convention says that second double is in r2 and r3.
7687 __ Ldrd(r2, r3, FieldMemOperand(r0, HeapNumber::kValueOffset));
ager@chromium.org357bf652010-04-12 11:30:10 +00007688 }
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00007689 __ jmp(&finished_loading_r0);
7690 __ bind(&r0_is_smi);
7691 if (mode_ == OVERWRITE_RIGHT) {
7692 // We can't overwrite a Smi so get address of new heap number into r5.
7693 __ AllocateHeapNumber(r5, r4, r7, heap_number_map, &slow);
7694 }
ager@chromium.org357bf652010-04-12 11:30:10 +00007695
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00007696 if (CpuFeatures::IsSupported(VFP3)) {
7697 CpuFeatures::Scope scope(VFP3);
7698 // Convert smi in r0 to double in d7.
7699 __ mov(r7, Operand(r0, ASR, kSmiTagSize));
7700 __ vmov(s15, r7);
7701 __ vcvt_f64_s32(d7, s15);
7702 if (!use_fp_registers) {
7703 __ vmov(r2, r3, d7);
7704 }
7705 } else {
7706 // Write Smi from r0 to r3 and r2 in double format.
7707 __ mov(r7, Operand(r0));
7708 ConvertToDoubleStub stub3(r3, r2, r7, r4);
7709 __ push(lr);
7710 __ Call(stub3.GetCode(), RelocInfo::CODE_TARGET);
7711 __ pop(lr);
7712 }
7713
7714 // HEAP_NUMBERS stub is slower than GENERIC on a pair of smis.
7715 // r0 is known to be a smi. If r1 is also a smi then switch to GENERIC.
7716 Label r1_is_not_smi;
7717 if (runtime_operands_type_ == BinaryOpIC::HEAP_NUMBERS) {
7718 __ tst(r1, Operand(kSmiTagMask));
7719 __ b(ne, &r1_is_not_smi);
7720 GenerateTypeTransition(masm); // Tail call.
7721 }
7722
7723 __ bind(&finished_loading_r0);
7724
7725 // Move r1 to a double in r0-r1.
7726 __ tst(r1, Operand(kSmiTagMask));
7727 __ b(eq, &r1_is_smi); // It's a Smi so don't check it's a heap number.
7728 __ bind(&r1_is_not_smi);
7729 __ ldr(r4, FieldMemOperand(r1, HeapNumber::kMapOffset));
7730 __ AssertRegisterIsRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
7731 __ cmp(r4, heap_number_map);
7732 __ b(ne, &slow);
7733 if (mode_ == OVERWRITE_LEFT) {
7734 __ mov(r5, Operand(r1)); // Overwrite this heap number.
7735 }
7736 if (use_fp_registers) {
7737 CpuFeatures::Scope scope(VFP3);
7738 // Load the double from tagged HeapNumber r1 to d6.
7739 __ sub(r7, r1, Operand(kHeapObjectTag));
7740 __ vldr(d6, r7, HeapNumber::kValueOffset);
7741 } else {
7742 // Calling convention says that first double is in r0 and r1.
7743 __ Ldrd(r0, r1, FieldMemOperand(r1, HeapNumber::kValueOffset));
7744 }
7745 __ jmp(&finished_loading_r1);
7746 __ bind(&r1_is_smi);
7747 if (mode_ == OVERWRITE_LEFT) {
7748 // We can't overwrite a Smi so get address of new heap number into r5.
7749 __ AllocateHeapNumber(r5, r4, r7, heap_number_map, &slow);
7750 }
7751
7752 if (CpuFeatures::IsSupported(VFP3)) {
7753 CpuFeatures::Scope scope(VFP3);
7754 // Convert smi in r1 to double in d6.
7755 __ mov(r7, Operand(r1, ASR, kSmiTagSize));
7756 __ vmov(s13, r7);
7757 __ vcvt_f64_s32(d6, s13);
7758 if (!use_fp_registers) {
7759 __ vmov(r0, r1, d6);
7760 }
7761 } else {
7762 // Write Smi from r1 to r1 and r0 in double format.
7763 __ mov(r7, Operand(r1));
7764 ConvertToDoubleStub stub4(r1, r0, r7, r9);
7765 __ push(lr);
7766 __ Call(stub4.GetCode(), RelocInfo::CODE_TARGET);
7767 __ pop(lr);
7768 }
7769
7770 __ bind(&finished_loading_r1);
ager@chromium.org357bf652010-04-12 11:30:10 +00007771 }
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00007772
7773 if (generate_code_to_calculate_answer || do_the_call.is_linked()) {
7774 __ bind(&do_the_call);
7775 // If we are inlining the operation using VFP3 instructions for
7776 // add, subtract, multiply, or divide, the arguments are in d6 and d7.
7777 if (use_fp_registers) {
7778 CpuFeatures::Scope scope(VFP3);
7779 // ARMv7 VFP3 instructions to implement
7780 // double precision, add, subtract, multiply, divide.
7781
7782 if (Token::MUL == op_) {
7783 __ vmul(d5, d6, d7);
7784 } else if (Token::DIV == op_) {
7785 __ vdiv(d5, d6, d7);
7786 } else if (Token::ADD == op_) {
7787 __ vadd(d5, d6, d7);
7788 } else if (Token::SUB == op_) {
7789 __ vsub(d5, d6, d7);
7790 } else {
7791 UNREACHABLE();
7792 }
7793 __ sub(r0, r5, Operand(kHeapObjectTag));
7794 __ vstr(d5, r0, HeapNumber::kValueOffset);
7795 __ add(r0, r0, Operand(kHeapObjectTag));
7796 __ mov(pc, lr);
7797 } else {
7798 // If we did not inline the operation, then the arguments are in:
7799 // r0: Left value (least significant part of mantissa).
7800 // r1: Left value (sign, exponent, top of mantissa).
7801 // r2: Right value (least significant part of mantissa).
7802 // r3: Right value (sign, exponent, top of mantissa).
7803 // r5: Address of heap number for result.
7804
7805 __ push(lr); // For later.
7806 __ PrepareCallCFunction(4, r4); // Two doubles count as 4 arguments.
7807 // Call C routine that may not cause GC or other trouble. r5 is callee
7808 // save.
7809 __ CallCFunction(ExternalReference::double_fp_operation(op_), 4);
7810 // Store answer in the overwritable heap number.
7811 #if !defined(USE_ARM_EABI)
7812 // Double returned in fp coprocessor register 0 and 1, encoded as
7813 // register cr8. Offsets must be divisible by 4 for coprocessor so we
7814 // need to substract the tag from r5.
7815 __ sub(r4, r5, Operand(kHeapObjectTag));
7816 __ stc(p1, cr8, MemOperand(r4, HeapNumber::kValueOffset));
7817 #else
7818 // Double returned in registers 0 and 1.
7819 __ Strd(r0, r1, FieldMemOperand(r5, HeapNumber::kValueOffset));
7820 #endif
7821 __ mov(r0, Operand(r5));
7822 // And we are done.
7823 __ pop(pc);
7824 }
7825 }
7826 }
7827
7828 if (!generate_code_to_calculate_answer &&
7829 !slow_reverse.is_linked() &&
7830 !slow.is_linked()) {
7831 return;
ager@chromium.org357bf652010-04-12 11:30:10 +00007832 }
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00007833
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00007834 if (lhs.is(r0)) {
7835 __ b(&slow);
7836 __ bind(&slow_reverse);
7837 __ Swap(r0, r1, ip);
7838 }
7839
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00007840 heap_number_map = no_reg; // Don't use this any more from here on.
7841
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007842 // We jump to here if something goes wrong (one param is not a number of any
7843 // sort or new-space allocation fails).
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007844 __ bind(&slow);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007845
7846 // Push arguments to the stack
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00007847 __ Push(r1, r0);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007848
ager@chromium.org357bf652010-04-12 11:30:10 +00007849 if (Token::ADD == op_) {
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007850 // Test for string arguments before calling runtime.
7851 // r1 : first argument
7852 // r0 : second argument
7853 // sp[0] : second argument
ager@chromium.org5c838252010-02-19 08:53:10 +00007854 // sp[4] : first argument
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007855
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007856 Label not_strings, not_string1, string1, string1_smi2;
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007857 __ tst(r1, Operand(kSmiTagMask));
7858 __ b(eq, &not_string1);
7859 __ CompareObjectType(r1, r2, r2, FIRST_NONSTRING_TYPE);
7860 __ b(ge, &not_string1);
7861
7862 // First argument is a a string, test second.
7863 __ tst(r0, Operand(kSmiTagMask));
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007864 __ b(eq, &string1_smi2);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007865 __ CompareObjectType(r0, r2, r2, FIRST_NONSTRING_TYPE);
7866 __ b(ge, &string1);
7867
7868 // First and second argument are strings.
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007869 StringAddStub string_add_stub(NO_STRING_CHECK_IN_STUB);
7870 __ TailCallStub(&string_add_stub);
7871
7872 __ bind(&string1_smi2);
7873 // First argument is a string, second is a smi. Try to lookup the number
7874 // string for the smi in the number string cache.
7875 NumberToStringStub::GenerateLookupNumberStringCache(
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00007876 masm, r0, r2, r4, r5, r6, true, &string1);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007877
7878 // Replace second argument on stack and tailcall string add stub to make
7879 // the result.
7880 __ str(r2, MemOperand(sp, 0));
7881 __ TailCallStub(&string_add_stub);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007882
7883 // Only first argument is a string.
7884 __ bind(&string1);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007885 __ InvokeBuiltin(Builtins::STRING_ADD_LEFT, JUMP_JS);
7886
7887 // First argument was not a string, test second.
7888 __ bind(&not_string1);
7889 __ tst(r0, Operand(kSmiTagMask));
7890 __ b(eq, &not_strings);
7891 __ CompareObjectType(r0, r2, r2, FIRST_NONSTRING_TYPE);
7892 __ b(ge, &not_strings);
7893
7894 // Only second argument is a string.
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007895 __ InvokeBuiltin(Builtins::STRING_ADD_RIGHT, JUMP_JS);
7896
7897 __ bind(&not_strings);
7898 }
7899
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007900 __ InvokeBuiltin(builtin, JUMP_JS); // Tail call. No return.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007901}
7902
7903
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007904// Tries to get a signed int32 out of a double precision floating point heap
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007905// number. Rounds towards 0. Fastest for doubles that are in the ranges
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007906// -0x7fffffff to -0x40000000 or 0x40000000 to 0x7fffffff. This corresponds
7907// almost to the range of signed int32 values that are not Smis. Jumps to the
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007908// label 'slow' if the double isn't in the range -0x80000000.0 to 0x80000000.0
7909// (excluding the endpoints).
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007910static void GetInt32(MacroAssembler* masm,
7911 Register source,
7912 Register dest,
7913 Register scratch,
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007914 Register scratch2,
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007915 Label* slow) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007916 Label right_exponent, done;
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007917 // Get exponent word.
7918 __ ldr(scratch, FieldMemOperand(source, HeapNumber::kExponentOffset));
7919 // Get exponent alone in scratch2.
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00007920 __ Ubfx(scratch2,
7921 scratch,
7922 HeapNumber::kExponentShift,
7923 HeapNumber::kExponentBits);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007924 // Load dest with zero. We use this either for the final shift or
7925 // for the answer.
7926 __ mov(dest, Operand(0));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007927 // Check whether the exponent matches a 32 bit signed int that is not a Smi.
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007928 // A non-Smi integer is 1.xxx * 2^30 so the exponent is 30 (biased). This is
7929 // the exponent that we are fastest at and also the highest exponent we can
7930 // handle here.
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00007931 const uint32_t non_smi_exponent = HeapNumber::kExponentBias + 30;
7932 // The non_smi_exponent, 0x41d, is too big for ARM's immediate field so we
7933 // split it up to avoid a constant pool entry. You can't do that in general
7934 // for cmp because of the overflow flag, but we know the exponent is in the
7935 // range 0-2047 so there is no overflow.
7936 int fudge_factor = 0x400;
7937 __ sub(scratch2, scratch2, Operand(fudge_factor));
7938 __ cmp(scratch2, Operand(non_smi_exponent - fudge_factor));
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007939 // If we have a match of the int32-but-not-Smi exponent then skip some logic.
7940 __ b(eq, &right_exponent);
7941 // If the exponent is higher than that then go to slow case. This catches
7942 // numbers that don't fit in a signed int32, infinities and NaNs.
7943 __ b(gt, slow);
7944
7945 // We know the exponent is smaller than 30 (biased). If it is less than
7946 // 0 (biased) then the number is smaller in magnitude than 1.0 * 2^0, ie
7947 // it rounds to zero.
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00007948 const uint32_t zero_exponent = HeapNumber::kExponentBias + 0;
7949 __ sub(scratch2, scratch2, Operand(zero_exponent - fudge_factor), SetCC);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007950 // Dest already has a Smi zero.
7951 __ b(lt, &done);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007952 if (!CpuFeatures::IsSupported(VFP3)) {
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00007953 // We have an exponent between 0 and 30 in scratch2. Subtract from 30 to
7954 // get how much to shift down.
7955 __ rsb(dest, scratch2, Operand(30));
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007956 }
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007957 __ bind(&right_exponent);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007958 if (CpuFeatures::IsSupported(VFP3)) {
7959 CpuFeatures::Scope scope(VFP3);
7960 // ARMv7 VFP3 instructions implementing double precision to integer
7961 // conversion using round to zero.
7962 __ ldr(scratch2, FieldMemOperand(source, HeapNumber::kMantissaOffset));
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00007963 __ vmov(d7, scratch2, scratch);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007964 __ vcvt_s32_f64(s15, d7);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00007965 __ vmov(dest, s15);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007966 } else {
7967 // Get the top bits of the mantissa.
7968 __ and_(scratch2, scratch, Operand(HeapNumber::kMantissaMask));
7969 // Put back the implicit 1.
7970 __ orr(scratch2, scratch2, Operand(1 << HeapNumber::kExponentShift));
7971 // Shift up the mantissa bits to take up the space the exponent used to
7972 // take. We just orred in the implicit bit so that took care of one and
7973 // we want to leave the sign bit 0 so we subtract 2 bits from the shift
7974 // distance.
7975 const int shift_distance = HeapNumber::kNonMantissaBitsInTopWord - 2;
7976 __ mov(scratch2, Operand(scratch2, LSL, shift_distance));
7977 // Put sign in zero flag.
7978 __ tst(scratch, Operand(HeapNumber::kSignMask));
7979 // Get the second half of the double. For some exponents we don't
7980 // actually need this because the bits get shifted out again, but
7981 // it's probably slower to test than just to do it.
7982 __ ldr(scratch, FieldMemOperand(source, HeapNumber::kMantissaOffset));
7983 // Shift down 22 bits to get the last 10 bits.
7984 __ orr(scratch, scratch2, Operand(scratch, LSR, 32 - shift_distance));
7985 // Move down according to the exponent.
7986 __ mov(dest, Operand(scratch, LSR, dest));
7987 // Fix sign if sign bit was set.
7988 __ rsb(dest, dest, Operand(0), LeaveCC, ne);
7989 }
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007990 __ bind(&done);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007991}
7992
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007993// For bitwise ops where the inputs are not both Smis we here try to determine
7994// whether both inputs are either Smis or at least heap numbers that can be
7995// represented by a 32 bit signed value. We truncate towards zero as required
7996// by the ES spec. If this is the case we do the bitwise op and see if the
7997// result is a Smi. If so, great, otherwise we try to find a heap number to
7998// write the answer into (either by allocating or by overwriting).
ager@chromium.org357bf652010-04-12 11:30:10 +00007999// On entry the operands are in lhs and rhs. On exit the answer is in r0.
8000void GenericBinaryOpStub::HandleNonSmiBitwiseOp(MacroAssembler* masm,
8001 Register lhs,
8002 Register rhs) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008003 Label slow, result_not_a_smi;
ager@chromium.org357bf652010-04-12 11:30:10 +00008004 Label rhs_is_smi, lhs_is_smi;
8005 Label done_checking_rhs, done_checking_lhs;
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008006
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00008007 Register heap_number_map = r6;
8008 __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
8009
ager@chromium.org357bf652010-04-12 11:30:10 +00008010 __ tst(lhs, Operand(kSmiTagMask));
8011 __ 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 +00008012 __ ldr(r4, FieldMemOperand(lhs, HeapNumber::kMapOffset));
8013 __ cmp(r4, heap_number_map);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008014 __ b(ne, &slow);
ager@chromium.org357bf652010-04-12 11:30:10 +00008015 GetInt32(masm, lhs, r3, r5, r4, &slow);
8016 __ jmp(&done_checking_lhs);
8017 __ bind(&lhs_is_smi);
8018 __ mov(r3, Operand(lhs, ASR, 1));
8019 __ bind(&done_checking_lhs);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008020
ager@chromium.org357bf652010-04-12 11:30:10 +00008021 __ tst(rhs, Operand(kSmiTagMask));
8022 __ 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 +00008023 __ ldr(r4, FieldMemOperand(rhs, HeapNumber::kMapOffset));
8024 __ cmp(r4, heap_number_map);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008025 __ b(ne, &slow);
ager@chromium.org357bf652010-04-12 11:30:10 +00008026 GetInt32(masm, rhs, r2, r5, r4, &slow);
8027 __ jmp(&done_checking_rhs);
8028 __ bind(&rhs_is_smi);
8029 __ mov(r2, Operand(rhs, ASR, 1));
8030 __ bind(&done_checking_rhs);
8031
8032 ASSERT(((lhs.is(r0) && rhs.is(r1)) || (lhs.is(r1) && rhs.is(r0))));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008033
8034 // r0 and r1: Original operands (Smi or heap numbers).
8035 // r2 and r3: Signed int32 operands.
8036 switch (op_) {
8037 case Token::BIT_OR: __ orr(r2, r2, Operand(r3)); break;
8038 case Token::BIT_XOR: __ eor(r2, r2, Operand(r3)); break;
8039 case Token::BIT_AND: __ and_(r2, r2, Operand(r3)); break;
8040 case Token::SAR:
8041 // Use only the 5 least significant bits of the shift count.
8042 __ and_(r2, r2, Operand(0x1f));
8043 __ mov(r2, Operand(r3, ASR, r2));
8044 break;
8045 case Token::SHR:
8046 // Use only the 5 least significant bits of the shift count.
8047 __ and_(r2, r2, Operand(0x1f));
8048 __ mov(r2, Operand(r3, LSR, r2), SetCC);
8049 // SHR is special because it is required to produce a positive answer.
8050 // The code below for writing into heap numbers isn't capable of writing
8051 // the register as an unsigned int so we go to slow case if we hit this
8052 // case.
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00008053 if (CpuFeatures::IsSupported(VFP3)) {
8054 __ b(mi, &result_not_a_smi);
8055 } else {
8056 __ b(mi, &slow);
8057 }
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008058 break;
8059 case Token::SHL:
8060 // Use only the 5 least significant bits of the shift count.
8061 __ and_(r2, r2, Operand(0x1f));
8062 __ mov(r2, Operand(r3, LSL, r2));
8063 break;
8064 default: UNREACHABLE();
8065 }
8066 // check that the *signed* result fits in a smi
8067 __ add(r3, r2, Operand(0x40000000), SetCC);
8068 __ b(mi, &result_not_a_smi);
8069 __ mov(r0, Operand(r2, LSL, kSmiTagSize));
8070 __ Ret();
8071
8072 Label have_to_allocate, got_a_heap_number;
8073 __ bind(&result_not_a_smi);
8074 switch (mode_) {
8075 case OVERWRITE_RIGHT: {
ager@chromium.org357bf652010-04-12 11:30:10 +00008076 __ tst(rhs, Operand(kSmiTagMask));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008077 __ b(eq, &have_to_allocate);
ager@chromium.org357bf652010-04-12 11:30:10 +00008078 __ mov(r5, Operand(rhs));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008079 break;
8080 }
8081 case OVERWRITE_LEFT: {
ager@chromium.org357bf652010-04-12 11:30:10 +00008082 __ tst(lhs, Operand(kSmiTagMask));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008083 __ b(eq, &have_to_allocate);
ager@chromium.org357bf652010-04-12 11:30:10 +00008084 __ mov(r5, Operand(lhs));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008085 break;
8086 }
8087 case NO_OVERWRITE: {
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00008088 // Get a new heap number in r5. r4 and r7 are scratch.
8089 __ AllocateHeapNumber(r5, r4, r7, heap_number_map, &slow);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008090 }
8091 default: break;
8092 }
8093 __ bind(&got_a_heap_number);
8094 // r2: Answer as signed int32.
8095 // r5: Heap number to write answer into.
8096
8097 // Nothing can go wrong now, so move the heap number to r0, which is the
8098 // result.
8099 __ mov(r0, Operand(r5));
8100
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00008101 if (CpuFeatures::IsSupported(VFP3)) {
8102 // Convert the int32 in r2 to the heap number in r0. r3 is corrupted.
8103 CpuFeatures::Scope scope(VFP3);
8104 __ vmov(s0, r2);
8105 if (op_ == Token::SHR) {
8106 __ vcvt_f64_u32(d0, s0);
8107 } else {
8108 __ vcvt_f64_s32(d0, s0);
8109 }
8110 __ sub(r3, r0, Operand(kHeapObjectTag));
8111 __ vstr(d0, r3, HeapNumber::kValueOffset);
8112 __ Ret();
8113 } else {
8114 // Tail call that writes the int32 in r2 to the heap number in r0, using
8115 // r3 as scratch. r0 is preserved and returned.
8116 WriteInt32ToHeapNumberStub stub(r2, r0, r3);
8117 __ TailCallStub(&stub);
8118 }
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008119
8120 if (mode_ != NO_OVERWRITE) {
8121 __ bind(&have_to_allocate);
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00008122 // Get a new heap number in r5. r4 and r7 are scratch.
8123 __ AllocateHeapNumber(r5, r4, r7, heap_number_map, &slow);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008124 __ jmp(&got_a_heap_number);
8125 }
8126
8127 // If all else failed then we go to the runtime system.
8128 __ bind(&slow);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00008129 __ Push(lhs, rhs); // Restore stack.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008130 switch (op_) {
8131 case Token::BIT_OR:
8132 __ InvokeBuiltin(Builtins::BIT_OR, JUMP_JS);
8133 break;
8134 case Token::BIT_AND:
8135 __ InvokeBuiltin(Builtins::BIT_AND, JUMP_JS);
8136 break;
8137 case Token::BIT_XOR:
8138 __ InvokeBuiltin(Builtins::BIT_XOR, JUMP_JS);
8139 break;
8140 case Token::SAR:
8141 __ InvokeBuiltin(Builtins::SAR, JUMP_JS);
8142 break;
8143 case Token::SHR:
8144 __ InvokeBuiltin(Builtins::SHR, JUMP_JS);
8145 break;
8146 case Token::SHL:
8147 __ InvokeBuiltin(Builtins::SHL, JUMP_JS);
8148 break;
8149 default:
8150 UNREACHABLE();
8151 }
8152}
8153
8154
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008155// Can we multiply by x with max two shifts and an add.
8156// This answers yes to all integers from 2 to 10.
8157static bool IsEasyToMultiplyBy(int x) {
8158 if (x < 2) return false; // Avoid special cases.
8159 if (x > (Smi::kMaxValue + 1) >> 2) return false; // Almost always overflows.
8160 if (IsPowerOf2(x)) return true; // Simple shift.
8161 if (PopCountLessThanEqual2(x)) return true; // Shift and add and shift.
8162 if (IsPowerOf2(x + 1)) return true; // Patterns like 11111.
8163 return false;
8164}
8165
8166
8167// Can multiply by anything that IsEasyToMultiplyBy returns true for.
8168// Source and destination may be the same register. This routine does
8169// not set carry and overflow the way a mul instruction would.
8170static void MultiplyByKnownInt(MacroAssembler* masm,
8171 Register source,
8172 Register destination,
8173 int known_int) {
8174 if (IsPowerOf2(known_int)) {
8175 __ mov(destination, Operand(source, LSL, BitPosition(known_int)));
8176 } else if (PopCountLessThanEqual2(known_int)) {
8177 int first_bit = BitPosition(known_int);
8178 int second_bit = BitPosition(known_int ^ (1 << first_bit));
8179 __ add(destination, source, Operand(source, LSL, second_bit - first_bit));
8180 if (first_bit != 0) {
8181 __ mov(destination, Operand(destination, LSL, first_bit));
8182 }
8183 } else {
8184 ASSERT(IsPowerOf2(known_int + 1)); // Patterns like 1111.
8185 int the_bit = BitPosition(known_int + 1);
8186 __ rsb(destination, source, Operand(source, LSL, the_bit));
8187 }
8188}
8189
8190
8191// This function (as opposed to MultiplyByKnownInt) takes the known int in a
8192// a register for the cases where it doesn't know a good trick, and may deliver
8193// a result that needs shifting.
8194static void MultiplyByKnownInt2(
8195 MacroAssembler* masm,
8196 Register result,
8197 Register source,
8198 Register known_int_register, // Smi tagged.
8199 int known_int,
8200 int* required_shift) { // Including Smi tag shift
8201 switch (known_int) {
8202 case 3:
8203 __ add(result, source, Operand(source, LSL, 1));
8204 *required_shift = 1;
8205 break;
8206 case 5:
8207 __ add(result, source, Operand(source, LSL, 2));
8208 *required_shift = 1;
8209 break;
8210 case 6:
8211 __ add(result, source, Operand(source, LSL, 1));
8212 *required_shift = 2;
8213 break;
8214 case 7:
8215 __ rsb(result, source, Operand(source, LSL, 3));
8216 *required_shift = 1;
8217 break;
8218 case 9:
8219 __ add(result, source, Operand(source, LSL, 3));
8220 *required_shift = 1;
8221 break;
8222 case 10:
8223 __ add(result, source, Operand(source, LSL, 2));
8224 *required_shift = 2;
8225 break;
8226 default:
8227 ASSERT(!IsPowerOf2(known_int)); // That would be very inefficient.
8228 __ mul(result, source, known_int_register);
8229 *required_shift = 0;
8230 }
8231}
8232
8233
lrn@chromium.org32d961d2010-06-30 09:09:34 +00008234// This uses versions of the sum-of-digits-to-see-if-a-number-is-divisible-by-3
8235// trick. See http://en.wikipedia.org/wiki/Divisibility_rule
8236// Takes the sum of the digits base (mask + 1) repeatedly until we have a
8237// number from 0 to mask. On exit the 'eq' condition flags are set if the
8238// answer is exactly the mask.
8239void IntegerModStub::DigitSum(MacroAssembler* masm,
8240 Register lhs,
8241 int mask,
8242 int shift,
8243 Label* entry) {
8244 ASSERT(mask > 0);
8245 ASSERT(mask <= 0xff); // This ensures we don't need ip to use it.
8246 Label loop;
8247 __ bind(&loop);
8248 __ and_(ip, lhs, Operand(mask));
8249 __ add(lhs, ip, Operand(lhs, LSR, shift));
8250 __ bind(entry);
8251 __ cmp(lhs, Operand(mask));
8252 __ b(gt, &loop);
8253}
8254
8255
8256void IntegerModStub::DigitSum(MacroAssembler* masm,
8257 Register lhs,
8258 Register scratch,
8259 int mask,
8260 int shift1,
8261 int shift2,
8262 Label* entry) {
8263 ASSERT(mask > 0);
8264 ASSERT(mask <= 0xff); // This ensures we don't need ip to use it.
8265 Label loop;
8266 __ bind(&loop);
8267 __ bic(scratch, lhs, Operand(mask));
8268 __ and_(ip, lhs, Operand(mask));
8269 __ add(lhs, ip, Operand(lhs, LSR, shift1));
8270 __ add(lhs, lhs, Operand(scratch, LSR, shift2));
8271 __ bind(entry);
8272 __ cmp(lhs, Operand(mask));
8273 __ b(gt, &loop);
8274}
8275
8276
8277// Splits the number into two halves (bottom half has shift bits). The top
8278// half is subtracted from the bottom half. If the result is negative then
8279// rhs is added.
8280void IntegerModStub::ModGetInRangeBySubtraction(MacroAssembler* masm,
8281 Register lhs,
8282 int shift,
8283 int rhs) {
8284 int mask = (1 << shift) - 1;
8285 __ and_(ip, lhs, Operand(mask));
8286 __ sub(lhs, ip, Operand(lhs, LSR, shift), SetCC);
8287 __ add(lhs, lhs, Operand(rhs), LeaveCC, mi);
8288}
8289
8290
8291void IntegerModStub::ModReduce(MacroAssembler* masm,
8292 Register lhs,
8293 int max,
8294 int denominator) {
8295 int limit = denominator;
8296 while (limit * 2 <= max) limit *= 2;
8297 while (limit >= denominator) {
8298 __ cmp(lhs, Operand(limit));
8299 __ sub(lhs, lhs, Operand(limit), LeaveCC, ge);
8300 limit >>= 1;
8301 }
8302}
8303
8304
8305void IntegerModStub::ModAnswer(MacroAssembler* masm,
8306 Register result,
8307 Register shift_distance,
8308 Register mask_bits,
8309 Register sum_of_digits) {
8310 __ add(result, mask_bits, Operand(sum_of_digits, LSL, shift_distance));
8311 __ Ret();
8312}
8313
8314
8315// See comment for class.
8316void IntegerModStub::Generate(MacroAssembler* masm) {
8317 __ mov(lhs_, Operand(lhs_, LSR, shift_distance_));
8318 __ bic(odd_number_, odd_number_, Operand(1));
8319 __ mov(odd_number_, Operand(odd_number_, LSL, 1));
8320 // We now have (odd_number_ - 1) * 2 in the register.
8321 // Build a switch out of branches instead of data because it avoids
8322 // having to teach the assembler about intra-code-object pointers
8323 // that are not in relative branch instructions.
8324 Label mod3, mod5, mod7, mod9, mod11, mod13, mod15, mod17, mod19;
8325 Label mod21, mod23, mod25;
8326 { Assembler::BlockConstPoolScope block_const_pool(masm);
8327 __ add(pc, pc, Operand(odd_number_));
8328 // When you read pc it is always 8 ahead, but when you write it you always
8329 // write the actual value. So we put in two nops to take up the slack.
8330 __ nop();
8331 __ nop();
8332 __ b(&mod3);
8333 __ b(&mod5);
8334 __ b(&mod7);
8335 __ b(&mod9);
8336 __ b(&mod11);
8337 __ b(&mod13);
8338 __ b(&mod15);
8339 __ b(&mod17);
8340 __ b(&mod19);
8341 __ b(&mod21);
8342 __ b(&mod23);
8343 __ b(&mod25);
8344 }
8345
8346 // For each denominator we find a multiple that is almost only ones
8347 // when expressed in binary. Then we do the sum-of-digits trick for
8348 // that number. If the multiple is not 1 then we have to do a little
8349 // more work afterwards to get the answer into the 0-denominator-1
8350 // range.
8351 DigitSum(masm, lhs_, 3, 2, &mod3); // 3 = b11.
8352 __ sub(lhs_, lhs_, Operand(3), LeaveCC, eq);
8353 ModAnswer(masm, result_, shift_distance_, mask_bits_, lhs_);
8354
8355 DigitSum(masm, lhs_, 0xf, 4, &mod5); // 5 * 3 = b1111.
8356 ModGetInRangeBySubtraction(masm, lhs_, 2, 5);
8357 ModAnswer(masm, result_, shift_distance_, mask_bits_, lhs_);
8358
8359 DigitSum(masm, lhs_, 7, 3, &mod7); // 7 = b111.
8360 __ sub(lhs_, lhs_, Operand(7), LeaveCC, eq);
8361 ModAnswer(masm, result_, shift_distance_, mask_bits_, lhs_);
8362
8363 DigitSum(masm, lhs_, 0x3f, 6, &mod9); // 7 * 9 = b111111.
8364 ModGetInRangeBySubtraction(masm, lhs_, 3, 9);
8365 ModAnswer(masm, result_, shift_distance_, mask_bits_, lhs_);
8366
8367 DigitSum(masm, lhs_, r5, 0x3f, 6, 3, &mod11); // 5 * 11 = b110111.
8368 ModReduce(masm, lhs_, 0x3f, 11);
8369 ModAnswer(masm, result_, shift_distance_, mask_bits_, lhs_);
8370
8371 DigitSum(masm, lhs_, r5, 0xff, 8, 5, &mod13); // 19 * 13 = b11110111.
8372 ModReduce(masm, lhs_, 0xff, 13);
8373 ModAnswer(masm, result_, shift_distance_, mask_bits_, lhs_);
8374
8375 DigitSum(masm, lhs_, 0xf, 4, &mod15); // 15 = b1111.
8376 __ sub(lhs_, lhs_, Operand(15), LeaveCC, eq);
8377 ModAnswer(masm, result_, shift_distance_, mask_bits_, lhs_);
8378
8379 DigitSum(masm, lhs_, 0xff, 8, &mod17); // 15 * 17 = b11111111.
8380 ModGetInRangeBySubtraction(masm, lhs_, 4, 17);
8381 ModAnswer(masm, result_, shift_distance_, mask_bits_, lhs_);
8382
8383 DigitSum(masm, lhs_, r5, 0xff, 8, 5, &mod19); // 13 * 19 = b11110111.
8384 ModReduce(masm, lhs_, 0xff, 19);
8385 ModAnswer(masm, result_, shift_distance_, mask_bits_, lhs_);
8386
8387 DigitSum(masm, lhs_, 0x3f, 6, &mod21); // 3 * 21 = b111111.
8388 ModReduce(masm, lhs_, 0x3f, 21);
8389 ModAnswer(masm, result_, shift_distance_, mask_bits_, lhs_);
8390
8391 DigitSum(masm, lhs_, r5, 0xff, 8, 7, &mod23); // 11 * 23 = b11111101.
8392 ModReduce(masm, lhs_, 0xff, 23);
8393 ModAnswer(masm, result_, shift_distance_, mask_bits_, lhs_);
8394
8395 DigitSum(masm, lhs_, r5, 0x7f, 7, 6, &mod25); // 5 * 25 = b1111101.
8396 ModReduce(masm, lhs_, 0x7f, 25);
8397 ModAnswer(masm, result_, shift_distance_, mask_bits_, lhs_);
8398}
8399
8400
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00008401const char* GenericBinaryOpStub::GetName() {
8402 if (name_ != NULL) return name_;
8403 const int len = 100;
8404 name_ = Bootstrapper::AllocateAutoDeletedArray(len);
8405 if (name_ == NULL) return "OOM";
8406 const char* op_name = Token::Name(op_);
8407 const char* overwrite_name;
8408 switch (mode_) {
8409 case NO_OVERWRITE: overwrite_name = "Alloc"; break;
8410 case OVERWRITE_RIGHT: overwrite_name = "OverwriteRight"; break;
8411 case OVERWRITE_LEFT: overwrite_name = "OverwriteLeft"; break;
8412 default: overwrite_name = "UnknownOverwrite"; break;
8413 }
8414
8415 OS::SNPrintF(Vector<char>(name_, len),
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00008416 "GenericBinaryOpStub_%s_%s%s_%s",
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00008417 op_name,
8418 overwrite_name,
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00008419 specialized_on_rhs_ ? "_ConstantRhs" : "",
8420 BinaryOpIC::GetName(runtime_operands_type_));
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00008421 return name_;
8422}
8423
8424
ager@chromium.org5c838252010-02-19 08:53:10 +00008425
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008426void GenericBinaryOpStub::Generate(MacroAssembler* masm) {
ager@chromium.org357bf652010-04-12 11:30:10 +00008427 // lhs_ : x
8428 // rhs_ : y
8429 // r0 : result
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008430
ager@chromium.org357bf652010-04-12 11:30:10 +00008431 Register result = r0;
8432 Register lhs = lhs_;
8433 Register rhs = rhs_;
8434
8435 // This code can't cope with other register allocations yet.
8436 ASSERT(result.is(r0) &&
8437 ((lhs.is(r0) && rhs.is(r1)) ||
8438 (lhs.is(r1) && rhs.is(r0))));
8439
8440 Register smi_test_reg = VirtualFrame::scratch0();
8441 Register scratch = VirtualFrame::scratch1();
8442
8443 // All ops need to know whether we are dealing with two Smis. Set up
8444 // smi_test_reg to tell us that.
8445 if (ShouldGenerateSmiCode()) {
8446 __ orr(smi_test_reg, lhs, Operand(rhs));
8447 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008448
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008449 switch (op_) {
8450 case Token::ADD: {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008451 Label not_smi;
8452 // Fast path.
ager@chromium.org357bf652010-04-12 11:30:10 +00008453 if (ShouldGenerateSmiCode()) {
8454 ASSERT(kSmiTag == 0); // Adjust code below.
8455 __ tst(smi_test_reg, Operand(kSmiTagMask));
8456 __ b(ne, &not_smi);
8457 __ add(r0, r1, Operand(r0), SetCC); // Add y optimistically.
8458 // Return if no overflow.
8459 __ Ret(vc);
8460 __ sub(r0, r0, Operand(r1)); // Revert optimistic add.
8461 }
8462 HandleBinaryOpSlowCases(masm, &not_smi, lhs, rhs, Builtins::ADD);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008463 break;
8464 }
8465
8466 case Token::SUB: {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008467 Label not_smi;
8468 // Fast path.
ager@chromium.org357bf652010-04-12 11:30:10 +00008469 if (ShouldGenerateSmiCode()) {
8470 ASSERT(kSmiTag == 0); // Adjust code below.
8471 __ tst(smi_test_reg, Operand(kSmiTagMask));
8472 __ b(ne, &not_smi);
8473 if (lhs.is(r1)) {
8474 __ sub(r0, r1, Operand(r0), SetCC); // Subtract y optimistically.
8475 // Return if no overflow.
8476 __ Ret(vc);
8477 __ sub(r0, r1, Operand(r0)); // Revert optimistic subtract.
8478 } else {
8479 __ sub(r0, r0, Operand(r1), SetCC); // Subtract y optimistically.
8480 // Return if no overflow.
8481 __ Ret(vc);
8482 __ add(r0, r0, Operand(r1)); // Revert optimistic subtract.
8483 }
8484 }
8485 HandleBinaryOpSlowCases(masm, &not_smi, lhs, rhs, Builtins::SUB);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008486 break;
8487 }
8488
8489 case Token::MUL: {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008490 Label not_smi, slow;
ager@chromium.org357bf652010-04-12 11:30:10 +00008491 if (ShouldGenerateSmiCode()) {
8492 ASSERT(kSmiTag == 0); // adjust code below
8493 __ tst(smi_test_reg, Operand(kSmiTagMask));
8494 Register scratch2 = smi_test_reg;
8495 smi_test_reg = no_reg;
8496 __ b(ne, &not_smi);
8497 // Remove tag from one operand (but keep sign), so that result is Smi.
8498 __ mov(ip, Operand(rhs, ASR, kSmiTagSize));
8499 // Do multiplication
8500 // scratch = lower 32 bits of ip * lhs.
8501 __ smull(scratch, scratch2, lhs, ip);
8502 // Go slow on overflows (overflow bit is not set).
8503 __ mov(ip, Operand(scratch, ASR, 31));
8504 // No overflow if higher 33 bits are identical.
8505 __ cmp(ip, Operand(scratch2));
8506 __ b(ne, &slow);
8507 // Go slow on zero result to handle -0.
8508 __ tst(scratch, Operand(scratch));
8509 __ mov(result, Operand(scratch), LeaveCC, ne);
8510 __ Ret(ne);
8511 // We need -0 if we were multiplying a negative number with 0 to get 0.
8512 // We know one of them was zero.
8513 __ add(scratch2, rhs, Operand(lhs), SetCC);
8514 __ mov(result, Operand(Smi::FromInt(0)), LeaveCC, pl);
8515 __ Ret(pl); // Return Smi 0 if the non-zero one was positive.
8516 // Slow case. We fall through here if we multiplied a negative number
8517 // with 0, because that would mean we should produce -0.
8518 __ bind(&slow);
8519 }
8520 HandleBinaryOpSlowCases(masm, &not_smi, lhs, rhs, Builtins::MUL);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008521 break;
8522 }
8523
8524 case Token::DIV:
8525 case Token::MOD: {
8526 Label not_smi;
ager@chromium.org357bf652010-04-12 11:30:10 +00008527 if (ShouldGenerateSmiCode() && specialized_on_rhs_) {
lrn@chromium.org32d961d2010-06-30 09:09:34 +00008528 Label lhs_is_unsuitable;
ager@chromium.org357bf652010-04-12 11:30:10 +00008529 __ BranchOnNotSmi(lhs, &not_smi);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008530 if (IsPowerOf2(constant_rhs_)) {
8531 if (op_ == Token::MOD) {
ager@chromium.org357bf652010-04-12 11:30:10 +00008532 __ and_(rhs,
8533 lhs,
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008534 Operand(0x80000000u | ((constant_rhs_ << kSmiTagSize) - 1)),
8535 SetCC);
8536 // We now have the answer, but if the input was negative we also
8537 // have the sign bit. Our work is done if the result is
8538 // positive or zero:
ager@chromium.org357bf652010-04-12 11:30:10 +00008539 if (!rhs.is(r0)) {
8540 __ mov(r0, rhs, LeaveCC, pl);
8541 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008542 __ Ret(pl);
8543 // A mod of a negative left hand side must return a negative number.
8544 // Unfortunately if the answer is 0 then we must return -0. And we
ager@chromium.org357bf652010-04-12 11:30:10 +00008545 // already optimistically trashed rhs so we may need to restore it.
8546 __ eor(rhs, rhs, Operand(0x80000000u), SetCC);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008547 // Next two instructions are conditional on the answer being -0.
ager@chromium.org357bf652010-04-12 11:30:10 +00008548 __ mov(rhs, Operand(Smi::FromInt(constant_rhs_)), LeaveCC, eq);
lrn@chromium.org32d961d2010-06-30 09:09:34 +00008549 __ b(eq, &lhs_is_unsuitable);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008550 // We need to subtract the dividend. Eg. -3 % 4 == -3.
ager@chromium.org357bf652010-04-12 11:30:10 +00008551 __ sub(result, rhs, Operand(Smi::FromInt(constant_rhs_)));
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008552 } else {
8553 ASSERT(op_ == Token::DIV);
ager@chromium.org357bf652010-04-12 11:30:10 +00008554 __ tst(lhs,
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008555 Operand(0x80000000u | ((constant_rhs_ << kSmiTagSize) - 1)));
lrn@chromium.org32d961d2010-06-30 09:09:34 +00008556 __ b(ne, &lhs_is_unsuitable); // Go slow on negative or remainder.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008557 int shift = 0;
8558 int d = constant_rhs_;
8559 while ((d & 1) == 0) {
8560 d >>= 1;
8561 shift++;
8562 }
ager@chromium.org357bf652010-04-12 11:30:10 +00008563 __ mov(r0, Operand(lhs, LSR, shift));
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008564 __ bic(r0, r0, Operand(kSmiTagMask));
8565 }
8566 } else {
8567 // Not a power of 2.
ager@chromium.org357bf652010-04-12 11:30:10 +00008568 __ tst(lhs, Operand(0x80000000u));
lrn@chromium.org32d961d2010-06-30 09:09:34 +00008569 __ b(ne, &lhs_is_unsuitable);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008570 // Find a fixed point reciprocal of the divisor so we can divide by
8571 // multiplying.
8572 double divisor = 1.0 / constant_rhs_;
8573 int shift = 32;
8574 double scale = 4294967296.0; // 1 << 32.
8575 uint32_t mul;
8576 // Maximise the precision of the fixed point reciprocal.
8577 while (true) {
8578 mul = static_cast<uint32_t>(scale * divisor);
8579 if (mul >= 0x7fffffff) break;
8580 scale *= 2.0;
8581 shift++;
8582 }
8583 mul++;
ager@chromium.org357bf652010-04-12 11:30:10 +00008584 Register scratch2 = smi_test_reg;
8585 smi_test_reg = no_reg;
8586 __ mov(scratch2, Operand(mul));
8587 __ umull(scratch, scratch2, scratch2, lhs);
8588 __ mov(scratch2, Operand(scratch2, LSR, shift - 31));
8589 // scratch2 is lhs / rhs. scratch2 is not Smi tagged.
8590 // rhs is still the known rhs. rhs is Smi tagged.
8591 // lhs is still the unkown lhs. lhs is Smi tagged.
8592 int required_scratch_shift = 0; // Including the Smi tag shift of 1.
8593 // scratch = scratch2 * rhs.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008594 MultiplyByKnownInt2(masm,
ager@chromium.org357bf652010-04-12 11:30:10 +00008595 scratch,
8596 scratch2,
8597 rhs,
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008598 constant_rhs_,
ager@chromium.org357bf652010-04-12 11:30:10 +00008599 &required_scratch_shift);
8600 // scratch << required_scratch_shift is now the Smi tagged rhs *
8601 // (lhs / rhs) where / indicates integer division.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008602 if (op_ == Token::DIV) {
ager@chromium.org357bf652010-04-12 11:30:10 +00008603 __ cmp(lhs, Operand(scratch, LSL, required_scratch_shift));
lrn@chromium.org32d961d2010-06-30 09:09:34 +00008604 __ b(ne, &lhs_is_unsuitable); // There was a remainder.
ager@chromium.org357bf652010-04-12 11:30:10 +00008605 __ mov(result, Operand(scratch2, LSL, kSmiTagSize));
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008606 } else {
8607 ASSERT(op_ == Token::MOD);
ager@chromium.org357bf652010-04-12 11:30:10 +00008608 __ sub(result, lhs, Operand(scratch, LSL, required_scratch_shift));
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008609 }
8610 }
8611 __ Ret();
lrn@chromium.org32d961d2010-06-30 09:09:34 +00008612 __ bind(&lhs_is_unsuitable);
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00008613 } else if (op_ == Token::MOD &&
8614 runtime_operands_type_ != BinaryOpIC::HEAP_NUMBERS &&
8615 runtime_operands_type_ != BinaryOpIC::STRINGS) {
8616 // Do generate a bit of smi code for modulus even though the default for
8617 // modulus is not to do it, but as the ARM processor has no coprocessor
lrn@chromium.org32d961d2010-06-30 09:09:34 +00008618 // support for modulus checking for smis makes sense. We can handle
8619 // 1 to 25 times any power of 2. This covers over half the numbers from
8620 // 1 to 100 including all of the first 25. (Actually the constants < 10
8621 // are handled above by reciprocal multiplication. We only get here for
8622 // those cases if the right hand side is not a constant or for cases
8623 // like 192 which is 3*2^6 and ends up in the 3 case in the integer mod
8624 // stub.)
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00008625 Label slow;
lrn@chromium.org32d961d2010-06-30 09:09:34 +00008626 Label not_power_of_2;
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00008627 ASSERT(!ShouldGenerateSmiCode());
8628 ASSERT(kSmiTag == 0); // Adjust code below.
8629 // Check for two positive smis.
8630 __ orr(smi_test_reg, lhs, Operand(rhs));
8631 __ tst(smi_test_reg, Operand(0x80000000u | kSmiTagMask));
8632 __ b(ne, &slow);
8633 // Check that rhs is a power of two and not zero.
lrn@chromium.org32d961d2010-06-30 09:09:34 +00008634 Register mask_bits = r3;
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00008635 __ sub(scratch, rhs, Operand(1), SetCC);
8636 __ b(mi, &slow);
lrn@chromium.org32d961d2010-06-30 09:09:34 +00008637 __ and_(mask_bits, rhs, Operand(scratch), SetCC);
8638 __ b(ne, &not_power_of_2);
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00008639 // Calculate power of two modulus.
8640 __ and_(result, lhs, Operand(scratch));
8641 __ Ret();
lrn@chromium.org32d961d2010-06-30 09:09:34 +00008642
8643 __ bind(&not_power_of_2);
8644 __ eor(scratch, scratch, Operand(mask_bits));
8645 // At least two bits are set in the modulus. The high one(s) are in
8646 // mask_bits and the low one is scratch + 1.
8647 __ and_(mask_bits, scratch, Operand(lhs));
8648 Register shift_distance = scratch;
8649 scratch = no_reg;
8650
8651 // The rhs consists of a power of 2 multiplied by some odd number.
8652 // The power-of-2 part we handle by putting the corresponding bits
8653 // from the lhs in the mask_bits register, and the power in the
8654 // shift_distance register. Shift distance is never 0 due to Smi
8655 // tagging.
8656 __ CountLeadingZeros(r4, shift_distance, shift_distance);
8657 __ rsb(shift_distance, r4, Operand(32));
8658
8659 // Now we need to find out what the odd number is. The last bit is
8660 // always 1.
8661 Register odd_number = r4;
8662 __ mov(odd_number, Operand(rhs, LSR, shift_distance));
8663 __ cmp(odd_number, Operand(25));
8664 __ b(gt, &slow);
8665
8666 IntegerModStub stub(
8667 result, shift_distance, odd_number, mask_bits, lhs, r5);
8668 __ Jump(stub.GetCode(), RelocInfo::CODE_TARGET); // Tail call.
8669
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00008670 __ bind(&slow);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008671 }
ager@chromium.org357bf652010-04-12 11:30:10 +00008672 HandleBinaryOpSlowCases(
8673 masm,
8674 &not_smi,
8675 lhs,
8676 rhs,
8677 op_ == Token::MOD ? Builtins::MOD : Builtins::DIV);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008678 break;
8679 }
8680
8681 case Token::BIT_OR:
8682 case Token::BIT_AND:
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008683 case Token::BIT_XOR:
8684 case Token::SAR:
8685 case Token::SHR:
8686 case Token::SHL: {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008687 Label slow;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008688 ASSERT(kSmiTag == 0); // adjust code below
ager@chromium.org357bf652010-04-12 11:30:10 +00008689 __ tst(smi_test_reg, Operand(kSmiTagMask));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008690 __ b(ne, &slow);
ager@chromium.org357bf652010-04-12 11:30:10 +00008691 Register scratch2 = smi_test_reg;
8692 smi_test_reg = no_reg;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008693 switch (op_) {
ager@chromium.org357bf652010-04-12 11:30:10 +00008694 case Token::BIT_OR: __ orr(result, rhs, Operand(lhs)); break;
8695 case Token::BIT_AND: __ and_(result, rhs, Operand(lhs)); break;
8696 case Token::BIT_XOR: __ eor(result, rhs, Operand(lhs)); break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008697 case Token::SAR:
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008698 // Remove tags from right operand.
ager@chromium.org357bf652010-04-12 11:30:10 +00008699 __ GetLeastBitsFromSmi(scratch2, rhs, 5);
8700 __ mov(result, Operand(lhs, ASR, scratch2));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008701 // Smi tag result.
ager@chromium.org357bf652010-04-12 11:30:10 +00008702 __ bic(result, result, Operand(kSmiTagMask));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008703 break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008704 case Token::SHR:
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008705 // Remove tags from operands. We can't do this on a 31 bit number
8706 // because then the 0s get shifted into bit 30 instead of bit 31.
ager@chromium.org357bf652010-04-12 11:30:10 +00008707 __ mov(scratch, Operand(lhs, ASR, kSmiTagSize)); // x
8708 __ GetLeastBitsFromSmi(scratch2, rhs, 5);
8709 __ mov(scratch, Operand(scratch, LSR, scratch2));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008710 // Unsigned shift is not allowed to produce a negative number, so
8711 // check the sign bit and the sign bit after Smi tagging.
ager@chromium.org357bf652010-04-12 11:30:10 +00008712 __ tst(scratch, Operand(0xc0000000));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008713 __ b(ne, &slow);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008714 // Smi tag result.
ager@chromium.org357bf652010-04-12 11:30:10 +00008715 __ mov(result, Operand(scratch, LSL, kSmiTagSize));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008716 break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008717 case Token::SHL:
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008718 // Remove tags from operands.
ager@chromium.org357bf652010-04-12 11:30:10 +00008719 __ mov(scratch, Operand(lhs, ASR, kSmiTagSize)); // x
8720 __ GetLeastBitsFromSmi(scratch2, rhs, 5);
8721 __ mov(scratch, Operand(scratch, LSL, scratch2));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008722 // Check that the signed result fits in a Smi.
ager@chromium.org357bf652010-04-12 11:30:10 +00008723 __ add(scratch2, scratch, Operand(0x40000000), SetCC);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008724 __ b(mi, &slow);
ager@chromium.org357bf652010-04-12 11:30:10 +00008725 __ mov(result, Operand(scratch, LSL, kSmiTagSize));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008726 break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008727 default: UNREACHABLE();
8728 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008729 __ Ret();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008730 __ bind(&slow);
ager@chromium.org357bf652010-04-12 11:30:10 +00008731 HandleNonSmiBitwiseOp(masm, lhs, rhs);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008732 break;
8733 }
8734
8735 default: UNREACHABLE();
8736 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008737 // This code should be unreachable.
8738 __ stop("Unreachable");
ager@chromium.org357bf652010-04-12 11:30:10 +00008739
8740 // Generate an unreachable reference to the DEFAULT stub so that it can be
8741 // found at the end of this stub when clearing ICs at GC.
8742 // TODO(kaznacheev): Check performance impact and get rid of this.
8743 if (runtime_operands_type_ != BinaryOpIC::DEFAULT) {
8744 GenericBinaryOpStub uninit(MinorKey(), BinaryOpIC::DEFAULT);
8745 __ CallStub(&uninit);
8746 }
8747}
8748
8749
8750void GenericBinaryOpStub::GenerateTypeTransition(MacroAssembler* masm) {
8751 Label get_result;
8752
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00008753 __ Push(r1, r0);
ager@chromium.org357bf652010-04-12 11:30:10 +00008754
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00008755 __ mov(r2, Operand(Smi::FromInt(MinorKey())));
8756 __ mov(r1, Operand(Smi::FromInt(op_)));
ager@chromium.org357bf652010-04-12 11:30:10 +00008757 __ mov(r0, Operand(Smi::FromInt(runtime_operands_type_)));
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00008758 __ Push(r2, r1, r0);
ager@chromium.org357bf652010-04-12 11:30:10 +00008759
8760 __ TailCallExternalReference(
8761 ExternalReference(IC_Utility(IC::kBinaryOp_Patch)),
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00008762 5,
ager@chromium.org357bf652010-04-12 11:30:10 +00008763 1);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008764}
8765
8766
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00008767Handle<Code> GetBinaryOpStub(int key, BinaryOpIC::TypeInfo type_info) {
ager@chromium.org357bf652010-04-12 11:30:10 +00008768 GenericBinaryOpStub stub(key, type_info);
8769 return stub.GetCode();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00008770}
8771
8772
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008773void TranscendentalCacheStub::Generate(MacroAssembler* masm) {
8774 // Argument is a number and is on stack and in r0.
8775 Label runtime_call;
8776 Label input_not_smi;
8777 Label loaded;
8778
8779 if (CpuFeatures::IsSupported(VFP3)) {
8780 // Load argument and check if it is a smi.
8781 __ BranchOnNotSmi(r0, &input_not_smi);
8782
8783 CpuFeatures::Scope scope(VFP3);
8784 // Input is a smi. Convert to double and load the low and high words
8785 // of the double into r2, r3.
8786 __ IntegerToDoubleConversionWithVFP3(r0, r3, r2);
8787 __ b(&loaded);
8788
8789 __ bind(&input_not_smi);
8790 // Check if input is a HeapNumber.
8791 __ CheckMap(r0,
8792 r1,
8793 Heap::kHeapNumberMapRootIndex,
8794 &runtime_call,
8795 true);
8796 // Input is a HeapNumber. Load it to a double register and store the
8797 // low and high words into r2, r3.
8798 __ Ldrd(r2, r3, FieldMemOperand(r0, HeapNumber::kValueOffset));
8799
8800 __ bind(&loaded);
8801 // r2 = low 32 bits of double value
8802 // r3 = high 32 bits of double value
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00008803 // Compute hash (the shifts are arithmetic):
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008804 // h = (low ^ high); h ^= h >> 16; h ^= h >> 8; h = h & (cacheSize - 1);
8805 __ eor(r1, r2, Operand(r3));
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00008806 __ eor(r1, r1, Operand(r1, ASR, 16));
8807 __ eor(r1, r1, Operand(r1, ASR, 8));
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008808 ASSERT(IsPowerOf2(TranscendentalCache::kCacheSize));
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00008809 __ And(r1, r1, Operand(TranscendentalCache::kCacheSize - 1));
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008810
8811 // r2 = low 32 bits of double value.
8812 // r3 = high 32 bits of double value.
8813 // r1 = TranscendentalCache::hash(double value).
8814 __ mov(r0,
8815 Operand(ExternalReference::transcendental_cache_array_address()));
8816 // r0 points to cache array.
8817 __ ldr(r0, MemOperand(r0, type_ * sizeof(TranscendentalCache::caches_[0])));
8818 // r0 points to the cache for the type type_.
8819 // If NULL, the cache hasn't been initialized yet, so go through runtime.
8820 __ cmp(r0, Operand(0));
8821 __ b(eq, &runtime_call);
8822
8823#ifdef DEBUG
8824 // Check that the layout of cache elements match expectations.
8825 { TranscendentalCache::Element test_elem[2];
8826 char* elem_start = reinterpret_cast<char*>(&test_elem[0]);
8827 char* elem2_start = reinterpret_cast<char*>(&test_elem[1]);
8828 char* elem_in0 = reinterpret_cast<char*>(&(test_elem[0].in[0]));
8829 char* elem_in1 = reinterpret_cast<char*>(&(test_elem[0].in[1]));
8830 char* elem_out = reinterpret_cast<char*>(&(test_elem[0].output));
8831 CHECK_EQ(12, elem2_start - elem_start); // Two uint_32's and a pointer.
8832 CHECK_EQ(0, elem_in0 - elem_start);
8833 CHECK_EQ(kIntSize, elem_in1 - elem_start);
8834 CHECK_EQ(2 * kIntSize, elem_out - elem_start);
8835 }
8836#endif
8837
8838 // Find the address of the r1'st entry in the cache, i.e., &r0[r1*12].
8839 __ add(r1, r1, Operand(r1, LSL, 1));
8840 __ add(r0, r0, Operand(r1, LSL, 2));
8841 // Check if cache matches: Double value is stored in uint32_t[2] array.
8842 __ ldm(ia, r0, r4.bit()| r5.bit() | r6.bit());
8843 __ cmp(r2, r4);
8844 __ b(ne, &runtime_call);
8845 __ cmp(r3, r5);
8846 __ b(ne, &runtime_call);
8847 // Cache hit. Load result, pop argument and return.
8848 __ mov(r0, Operand(r6));
8849 __ pop();
8850 __ Ret();
8851 }
8852
8853 __ bind(&runtime_call);
8854 __ TailCallExternalReference(ExternalReference(RuntimeFunction()), 1, 1);
8855}
8856
8857
8858Runtime::FunctionId TranscendentalCacheStub::RuntimeFunction() {
8859 switch (type_) {
8860 // Add more cases when necessary.
8861 case TranscendentalCache::SIN: return Runtime::kMath_sin;
8862 case TranscendentalCache::COS: return Runtime::kMath_cos;
8863 default:
8864 UNIMPLEMENTED();
8865 return Runtime::kAbort;
8866 }
8867}
8868
8869
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008870void StackCheckStub::Generate(MacroAssembler* masm) {
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00008871 // Do tail-call to runtime routine. Runtime routines expect at least one
8872 // argument, so give it a Smi.
8873 __ mov(r0, Operand(Smi::FromInt(0)));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008874 __ push(r0);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00008875 __ TailCallRuntime(Runtime::kStackGuard, 1, 1);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008876
8877 __ StubReturn(1);
8878}
8879
8880
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00008881void GenericUnaryOpStub::Generate(MacroAssembler* masm) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008882 Label slow, done;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00008883
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00008884 Register heap_number_map = r6;
8885 __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
8886
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008887 if (op_ == Token::SUB) {
8888 // Check whether the value is a smi.
8889 Label try_float;
8890 __ tst(r0, Operand(kSmiTagMask));
8891 __ b(ne, &try_float);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008892
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008893 // Go slow case if the value of the expression is zero
8894 // to make sure that we switch between 0 and -0.
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00008895 if (negative_zero_ == kStrictNegativeZero) {
8896 // If we have to check for zero, then we can check for the max negative
8897 // smi while we are at it.
8898 __ bic(ip, r0, Operand(0x80000000), SetCC);
8899 __ b(eq, &slow);
8900 __ rsb(r0, r0, Operand(0));
8901 __ StubReturn(1);
8902 } else {
8903 // The value of the expression is a smi and 0 is OK for -0. Try
8904 // optimistic subtraction '0 - value'.
8905 __ rsb(r0, r0, Operand(0), SetCC);
8906 __ StubReturn(1, vc);
8907 // We don't have to reverse the optimistic neg since the only case
8908 // where we fall through is the minimum negative Smi, which is the case
8909 // where the neg leaves the register unchanged.
8910 __ jmp(&slow); // Go slow on max negative Smi.
8911 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008912
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008913 __ bind(&try_float);
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00008914 __ ldr(r1, FieldMemOperand(r0, HeapObject::kMapOffset));
8915 __ AssertRegisterIsRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
8916 __ cmp(r1, heap_number_map);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008917 __ b(ne, &slow);
8918 // r0 is a heap number. Get a new heap number in r1.
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00008919 if (overwrite_ == UNARY_OVERWRITE) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008920 __ ldr(r2, FieldMemOperand(r0, HeapNumber::kExponentOffset));
8921 __ eor(r2, r2, Operand(HeapNumber::kSignMask)); // Flip sign.
8922 __ str(r2, FieldMemOperand(r0, HeapNumber::kExponentOffset));
8923 } else {
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00008924 __ AllocateHeapNumber(r1, r2, r3, r6, &slow);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008925 __ ldr(r3, FieldMemOperand(r0, HeapNumber::kMantissaOffset));
8926 __ ldr(r2, FieldMemOperand(r0, HeapNumber::kExponentOffset));
8927 __ str(r3, FieldMemOperand(r1, HeapNumber::kMantissaOffset));
8928 __ eor(r2, r2, Operand(HeapNumber::kSignMask)); // Flip sign.
8929 __ str(r2, FieldMemOperand(r1, HeapNumber::kExponentOffset));
8930 __ mov(r0, Operand(r1));
8931 }
8932 } else if (op_ == Token::BIT_NOT) {
8933 // Check if the operand is a heap number.
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00008934 __ ldr(r1, FieldMemOperand(r0, HeapObject::kMapOffset));
8935 __ AssertRegisterIsRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
8936 __ cmp(r1, heap_number_map);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008937 __ b(ne, &slow);
8938
8939 // Convert the heap number is r0 to an untagged integer in r1.
8940 GetInt32(masm, r0, r1, r2, r3, &slow);
8941
8942 // Do the bitwise operation (move negated) and check if the result
8943 // fits in a smi.
8944 Label try_float;
8945 __ mvn(r1, Operand(r1));
8946 __ add(r2, r1, Operand(0x40000000), SetCC);
8947 __ b(mi, &try_float);
8948 __ mov(r0, Operand(r1, LSL, kSmiTagSize));
8949 __ b(&done);
8950
8951 __ bind(&try_float);
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00008952 if (!overwrite_ == UNARY_OVERWRITE) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008953 // Allocate a fresh heap number, but don't overwrite r0 until
8954 // we're sure we can do it without going through the slow case
8955 // that needs the value in r0.
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00008956 __ AllocateHeapNumber(r2, r3, r4, r6, &slow);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008957 __ mov(r0, Operand(r2));
8958 }
8959
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00008960 if (CpuFeatures::IsSupported(VFP3)) {
8961 // Convert the int32 in r1 to the heap number in r0. r2 is corrupted.
8962 CpuFeatures::Scope scope(VFP3);
8963 __ vmov(s0, r1);
8964 __ vcvt_f64_s32(d0, s0);
8965 __ sub(r2, r0, Operand(kHeapObjectTag));
8966 __ vstr(d0, r2, HeapNumber::kValueOffset);
8967 } else {
8968 // WriteInt32ToHeapNumberStub does not trigger GC, so we do not
8969 // have to set up a frame.
8970 WriteInt32ToHeapNumberStub stub(r1, r0, r2);
8971 __ push(lr);
8972 __ Call(stub.GetCode(), RelocInfo::CODE_TARGET);
8973 __ pop(lr);
8974 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008975 } else {
8976 UNIMPLEMENTED();
8977 }
8978
8979 __ bind(&done);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008980 __ StubReturn(1);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008981
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008982 // Handle the slow case by jumping to the JavaScript builtin.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008983 __ bind(&slow);
8984 __ push(r0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008985 switch (op_) {
8986 case Token::SUB:
8987 __ InvokeBuiltin(Builtins::UNARY_MINUS, JUMP_JS);
8988 break;
8989 case Token::BIT_NOT:
8990 __ InvokeBuiltin(Builtins::BIT_NOT, JUMP_JS);
8991 break;
8992 default:
8993 UNREACHABLE();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008994 }
ager@chromium.orga1645e22009-09-09 19:27:10 +00008995}
8996
8997
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008998void CEntryStub::GenerateThrowTOS(MacroAssembler* masm) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008999 // r0 holds the exception.
9000
9001 // Adjust this code if not the case.
9002 ASSERT(StackHandlerConstants::kSize == 4 * kPointerSize);
9003
9004 // Drop the sp to the top of the handler.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009005 __ mov(r3, Operand(ExternalReference(Top::k_handler_address)));
9006 __ ldr(sp, MemOperand(r3));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009007
9008 // Restore the next handler and frame pointer, discard handler state.
9009 ASSERT(StackHandlerConstants::kNextOffset == 0);
9010 __ pop(r2);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009011 __ str(r2, MemOperand(r3));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009012 ASSERT(StackHandlerConstants::kFPOffset == 2 * kPointerSize);
9013 __ ldm(ia_w, sp, r3.bit() | fp.bit()); // r3: discarded state.
9014
9015 // Before returning we restore the context from the frame pointer if
9016 // not NULL. The frame pointer is NULL in the exception handler of a
9017 // JS entry frame.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009018 __ cmp(fp, Operand(0));
9019 // Set cp to NULL if fp is NULL.
9020 __ mov(cp, Operand(0), LeaveCC, eq);
9021 // Restore cp otherwise.
9022 __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset), ne);
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009023#ifdef DEBUG
9024 if (FLAG_debug_code) {
9025 __ mov(lr, Operand(pc));
9026 }
9027#endif
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009028 ASSERT(StackHandlerConstants::kPCOffset == 3 * kPointerSize);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009029 __ pop(pc);
9030}
9031
9032
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00009033void CEntryStub::GenerateThrowUncatchable(MacroAssembler* masm,
9034 UncatchableExceptionType type) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009035 // Adjust this code if not the case.
9036 ASSERT(StackHandlerConstants::kSize == 4 * kPointerSize);
9037
9038 // Drop sp to the top stack handler.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009039 __ mov(r3, Operand(ExternalReference(Top::k_handler_address)));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009040 __ ldr(sp, MemOperand(r3));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009041
9042 // Unwind the handlers until the ENTRY handler is found.
9043 Label loop, done;
9044 __ bind(&loop);
9045 // Load the type of the current stack handler.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009046 const int kStateOffset = StackHandlerConstants::kStateOffset;
9047 __ ldr(r2, MemOperand(sp, kStateOffset));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009048 __ cmp(r2, Operand(StackHandler::ENTRY));
9049 __ b(eq, &done);
9050 // Fetch the next handler in the list.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009051 const int kNextOffset = StackHandlerConstants::kNextOffset;
9052 __ ldr(sp, MemOperand(sp, kNextOffset));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009053 __ jmp(&loop);
9054 __ bind(&done);
9055
9056 // Set the top handler address to next handler past the current ENTRY handler.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009057 ASSERT(StackHandlerConstants::kNextOffset == 0);
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00009058 __ pop(r2);
9059 __ str(r2, MemOperand(r3));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009060
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00009061 if (type == OUT_OF_MEMORY) {
9062 // Set external caught exception to false.
9063 ExternalReference external_caught(Top::k_external_caught_exception_address);
9064 __ mov(r0, Operand(false));
9065 __ mov(r2, Operand(external_caught));
9066 __ str(r0, MemOperand(r2));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009067
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00009068 // Set pending exception and r0 to out of memory exception.
9069 Failure* out_of_memory = Failure::OutOfMemoryException();
9070 __ mov(r0, Operand(reinterpret_cast<int32_t>(out_of_memory)));
9071 __ mov(r2, Operand(ExternalReference(Top::k_pending_exception_address)));
9072 __ str(r0, MemOperand(r2));
9073 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009074
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009075 // Stack layout at this point. See also StackHandlerConstants.
9076 // sp -> state (ENTRY)
9077 // fp
9078 // lr
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009079
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009080 // Discard handler state (r2 is not used) and restore frame pointer.
9081 ASSERT(StackHandlerConstants::kFPOffset == 2 * kPointerSize);
9082 __ ldm(ia_w, sp, r2.bit() | fp.bit()); // r2: discarded state.
9083 // Before returning we restore the context from the frame pointer if
9084 // not NULL. The frame pointer is NULL in the exception handler of a
9085 // JS entry frame.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009086 __ cmp(fp, Operand(0));
9087 // Set cp to NULL if fp is NULL.
9088 __ mov(cp, Operand(0), LeaveCC, eq);
9089 // Restore cp otherwise.
9090 __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset), ne);
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009091#ifdef DEBUG
9092 if (FLAG_debug_code) {
9093 __ mov(lr, Operand(pc));
9094 }
9095#endif
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009096 ASSERT(StackHandlerConstants::kPCOffset == 3 * kPointerSize);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009097 __ pop(pc);
9098}
9099
9100
9101void CEntryStub::GenerateCore(MacroAssembler* masm,
9102 Label* throw_normal_exception,
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00009103 Label* throw_termination_exception,
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009104 Label* throw_out_of_memory_exception,
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00009105 bool do_gc,
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009106 bool always_allocate,
9107 int frame_alignment_skew) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009108 // r0: result parameter for PerformGC, if any
9109 // r4: number of arguments including receiver (C callee-saved)
9110 // r5: pointer to builtin function (C callee-saved)
9111 // r6: pointer to the first argument (C callee-saved)
9112
9113 if (do_gc) {
9114 // Passing r0.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009115 __ PrepareCallCFunction(1, r1);
9116 __ CallCFunction(ExternalReference::perform_gc_function(), 1);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009117 }
9118
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00009119 ExternalReference scope_depth =
9120 ExternalReference::heap_always_allocate_scope_depth();
9121 if (always_allocate) {
9122 __ mov(r0, Operand(scope_depth));
9123 __ ldr(r1, MemOperand(r0));
9124 __ add(r1, r1, Operand(1));
9125 __ str(r1, MemOperand(r0));
9126 }
9127
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009128 // Call C built-in.
9129 // r0 = argc, r1 = argv
9130 __ mov(r0, Operand(r4));
9131 __ mov(r1, Operand(r6));
9132
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009133 int frame_alignment = MacroAssembler::ActivationFrameAlignment();
9134 int frame_alignment_mask = frame_alignment - 1;
9135#if defined(V8_HOST_ARCH_ARM)
9136 if (FLAG_debug_code) {
9137 if (frame_alignment > kPointerSize) {
9138 Label alignment_as_expected;
9139 ASSERT(IsPowerOf2(frame_alignment));
9140 __ sub(r2, sp, Operand(frame_alignment_skew));
9141 __ tst(r2, Operand(frame_alignment_mask));
9142 __ b(eq, &alignment_as_expected);
9143 // Don't use Check here, as it will call Runtime_Abort re-entering here.
9144 __ stop("Unexpected alignment");
9145 __ bind(&alignment_as_expected);
9146 }
9147 }
9148#endif
9149
9150 // Just before the call (jump) below lr is pushed, so the actual alignment is
9151 // adding one to the current skew.
9152 int alignment_before_call =
9153 (frame_alignment_skew + kPointerSize) & frame_alignment_mask;
9154 if (alignment_before_call > 0) {
9155 // Push until the alignment before the call is met.
9156 __ mov(r2, Operand(0));
9157 for (int i = alignment_before_call;
9158 (i & frame_alignment_mask) != 0;
9159 i += kPointerSize) {
9160 __ push(r2);
9161 }
9162 }
9163
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009164 // TODO(1242173): To let the GC traverse the return address of the exit
9165 // frames, we need to know where the return address is. Right now,
9166 // we push it on the stack to be able to find it again, but we never
9167 // restore from it in case of changes, which makes it impossible to
9168 // support moving the C entry code stub. This should be fixed, but currently
9169 // this is OK because the CEntryStub gets generated so early in the V8 boot
9170 // sequence that it is not moving ever.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009171 masm->add(lr, pc, Operand(4)); // Compute return address: (pc + 8) + 4
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009172 masm->push(lr);
9173 masm->Jump(r5);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00009174
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009175 // Restore sp back to before aligning the stack.
9176 if (alignment_before_call > 0) {
9177 __ add(sp, sp, Operand(alignment_before_call));
9178 }
9179
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00009180 if (always_allocate) {
9181 // It's okay to clobber r2 and r3 here. Don't mess with r0 and r1
9182 // though (contain the result).
9183 __ mov(r2, Operand(scope_depth));
9184 __ ldr(r3, MemOperand(r2));
9185 __ sub(r3, r3, Operand(1));
9186 __ str(r3, MemOperand(r2));
9187 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009188
9189 // check for failure result
9190 Label failure_returned;
9191 ASSERT(((kFailureTag + 1) & kFailureTagMask) == 0);
9192 // Lower 2 bits of r2 are 0 iff r0 has failure tag.
9193 __ add(r2, r0, Operand(1));
9194 __ tst(r2, Operand(kFailureTagMask));
9195 __ b(eq, &failure_returned);
9196
9197 // Exit C frame and return.
9198 // r0:r1: result
9199 // sp: stack pointer
9200 // fp: frame pointer
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009201 __ LeaveExitFrame(mode_);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009202
9203 // check if we should retry or throw exception
9204 Label retry;
9205 __ bind(&failure_returned);
9206 ASSERT(Failure::RETRY_AFTER_GC == 0);
9207 __ tst(r0, Operand(((1 << kFailureTypeTagSize) - 1) << kFailureTagSize));
9208 __ b(eq, &retry);
9209
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00009210 // Special handling of out of memory exceptions.
9211 Failure* out_of_memory = Failure::OutOfMemoryException();
9212 __ cmp(r0, Operand(reinterpret_cast<int32_t>(out_of_memory)));
9213 __ b(eq, throw_out_of_memory_exception);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009214
9215 // Retrieve the pending exception and clear the variable.
ager@chromium.org32912102009-01-16 10:38:43 +00009216 __ mov(ip, Operand(ExternalReference::the_hole_value_location()));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009217 __ ldr(r3, MemOperand(ip));
ager@chromium.org32912102009-01-16 10:38:43 +00009218 __ mov(ip, Operand(ExternalReference(Top::k_pending_exception_address)));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009219 __ ldr(r0, MemOperand(ip));
9220 __ str(r3, MemOperand(ip));
9221
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00009222 // Special handling of termination exceptions which are uncatchable
9223 // by javascript code.
9224 __ cmp(r0, Operand(Factory::termination_exception()));
9225 __ b(eq, throw_termination_exception);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009226
9227 // Handle normal exception.
9228 __ jmp(throw_normal_exception);
9229
9230 __ bind(&retry); // pass last failure (r0) as parameter (r0) when retrying
9231}
9232
9233
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009234void CEntryStub::Generate(MacroAssembler* masm) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009235 // Called from JavaScript; parameters are on stack as if calling JS function
9236 // r0: number of arguments including receiver
9237 // r1: pointer to builtin function
9238 // fp: frame pointer (restored after C call)
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009239 // sp: stack pointer (restored as callee's sp after C call)
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009240 // cp: current context (C callee-saved)
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009241
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009242 // Result returned in r0 or r0+r1 by default.
9243
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009244 // NOTE: Invocations of builtins may return failure objects
9245 // instead of a proper result. The builtin entry handles
9246 // this by performing a garbage collection and retrying the
9247 // builtin once.
9248
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009249 // Enter the exit frame that transitions from JavaScript to C++.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009250 __ EnterExitFrame(mode_);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009251
9252 // r4: number of arguments (C callee-saved)
9253 // r5: pointer to builtin function (C callee-saved)
9254 // r6: pointer to first argument (C callee-saved)
9255
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009256 Label throw_normal_exception;
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00009257 Label throw_termination_exception;
9258 Label throw_out_of_memory_exception;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009259
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00009260 // Call into the runtime system.
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00009261 GenerateCore(masm,
9262 &throw_normal_exception,
9263 &throw_termination_exception,
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009264 &throw_out_of_memory_exception,
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00009265 false,
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009266 false,
9267 -kPointerSize);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00009268
9269 // Do space-specific GC and retry runtime call.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009270 GenerateCore(masm,
9271 &throw_normal_exception,
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00009272 &throw_termination_exception,
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009273 &throw_out_of_memory_exception,
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00009274 true,
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009275 false,
9276 0);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00009277
9278 // Do full GC and retry runtime call one final time.
9279 Failure* failure = Failure::InternalError();
9280 __ mov(r0, Operand(reinterpret_cast<int32_t>(failure)));
9281 GenerateCore(masm,
9282 &throw_normal_exception,
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00009283 &throw_termination_exception,
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00009284 &throw_out_of_memory_exception,
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00009285 true,
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009286 true,
9287 kPointerSize);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009288
9289 __ bind(&throw_out_of_memory_exception);
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00009290 GenerateThrowUncatchable(masm, OUT_OF_MEMORY);
9291
9292 __ bind(&throw_termination_exception);
9293 GenerateThrowUncatchable(masm, TERMINATION);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009294
9295 __ bind(&throw_normal_exception);
9296 GenerateThrowTOS(masm);
9297}
9298
9299
9300void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) {
9301 // r0: code entry
9302 // r1: function
9303 // r2: receiver
9304 // r3: argc
9305 // [sp+0]: argv
9306
9307 Label invoke, exit;
9308
9309 // Called from C, so do not pop argc and args on exit (preserve sp)
9310 // No need to save register-passed args
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009311 // Save callee-saved registers (incl. cp and fp), sp, and lr
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009312 __ stm(db_w, sp, kCalleeSaved | lr.bit());
9313
9314 // Get address of argv, see stm above.
9315 // r0: code entry
9316 // r1: function
9317 // r2: receiver
9318 // r3: argc
ager@chromium.org5c838252010-02-19 08:53:10 +00009319 __ ldr(r4, MemOperand(sp, (kNumCalleeSaved + 1) * kPointerSize)); // argv
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009320
9321 // Push a frame with special values setup to mark it as an entry frame.
9322 // r0: code entry
9323 // r1: function
9324 // r2: receiver
9325 // r3: argc
9326 // r4: argv
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009327 __ mov(r8, Operand(-1)); // Push a bad frame pointer to fail if it is used.
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009328 int marker = is_construct ? StackFrame::ENTRY_CONSTRUCT : StackFrame::ENTRY;
9329 __ mov(r7, Operand(Smi::FromInt(marker)));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009330 __ mov(r6, Operand(Smi::FromInt(marker)));
9331 __ mov(r5, Operand(ExternalReference(Top::k_c_entry_fp_address)));
9332 __ ldr(r5, MemOperand(r5));
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00009333 __ Push(r8, r7, r6, r5);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009334
9335 // Setup frame pointer for the frame to be pushed.
9336 __ add(fp, sp, Operand(-EntryFrameConstants::kCallerFPOffset));
9337
9338 // Call a faked try-block that does the invoke.
9339 __ bl(&invoke);
9340
9341 // Caught exception: Store result (exception) in the pending
9342 // exception field in the JSEnv and return a failure sentinel.
9343 // Coming in here the fp will be invalid because the PushTryHandler below
9344 // sets it to 0 to signal the existence of the JSEntry frame.
ager@chromium.org32912102009-01-16 10:38:43 +00009345 __ mov(ip, Operand(ExternalReference(Top::k_pending_exception_address)));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009346 __ str(r0, MemOperand(ip));
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009347 __ mov(r0, Operand(reinterpret_cast<int32_t>(Failure::Exception())));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009348 __ b(&exit);
9349
9350 // Invoke: Link this frame into the handler chain.
9351 __ bind(&invoke);
9352 // Must preserve r0-r4, r5-r7 are available.
9353 __ PushTryHandler(IN_JS_ENTRY, JS_ENTRY_HANDLER);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009354 // If an exception not caught by another handler occurs, this handler
9355 // returns control to the code after the bl(&invoke) above, which
9356 // restores all kCalleeSaved registers (including cp and fp) to their
9357 // saved values before returning a failure to C.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009358
9359 // Clear any pending exceptions.
9360 __ mov(ip, Operand(ExternalReference::the_hole_value_location()));
9361 __ ldr(r5, MemOperand(ip));
ager@chromium.org32912102009-01-16 10:38:43 +00009362 __ mov(ip, Operand(ExternalReference(Top::k_pending_exception_address)));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009363 __ str(r5, MemOperand(ip));
9364
9365 // Invoke the function by calling through JS entry trampoline builtin.
9366 // Notice that we cannot store a reference to the trampoline code directly in
9367 // this stub, because runtime stubs are not traversed when doing GC.
9368
9369 // Expected registers by Builtins::JSEntryTrampoline
9370 // r0: code entry
9371 // r1: function
9372 // r2: receiver
9373 // r3: argc
9374 // r4: argv
9375 if (is_construct) {
9376 ExternalReference construct_entry(Builtins::JSConstructEntryTrampoline);
9377 __ mov(ip, Operand(construct_entry));
9378 } else {
9379 ExternalReference entry(Builtins::JSEntryTrampoline);
9380 __ mov(ip, Operand(entry));
9381 }
9382 __ ldr(ip, MemOperand(ip)); // deref address
9383
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009384 // Branch and link to JSEntryTrampoline. We don't use the double underscore
9385 // macro for the add instruction because we don't want the coverage tool
9386 // inserting instructions here after we read the pc.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009387 __ mov(lr, Operand(pc));
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009388 masm->add(pc, ip, Operand(Code::kHeaderSize - kHeapObjectTag));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009389
9390 // Unlink this frame from the handler chain. When reading the
9391 // address of the next handler, there is no need to use the address
9392 // displacement since the current stack pointer (sp) points directly
9393 // to the stack handler.
9394 __ ldr(r3, MemOperand(sp, StackHandlerConstants::kNextOffset));
9395 __ mov(ip, Operand(ExternalReference(Top::k_handler_address)));
9396 __ str(r3, MemOperand(ip));
9397 // No need to restore registers
9398 __ add(sp, sp, Operand(StackHandlerConstants::kSize));
9399
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009400
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009401 __ bind(&exit); // r0 holds result
9402 // Restore the top frame descriptors from the stack.
9403 __ pop(r3);
9404 __ mov(ip, Operand(ExternalReference(Top::k_c_entry_fp_address)));
9405 __ str(r3, MemOperand(ip));
9406
9407 // Reset the stack to the callee saved registers.
9408 __ add(sp, sp, Operand(-EntryFrameConstants::kCallerFPOffset));
9409
9410 // Restore callee-saved registers and return.
9411#ifdef DEBUG
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009412 if (FLAG_debug_code) {
9413 __ mov(lr, Operand(pc));
9414 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009415#endif
9416 __ ldm(ia_w, sp, kCalleeSaved | pc.bit());
9417}
9418
9419
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009420// This stub performs an instanceof, calling the builtin function if
9421// necessary. Uses r1 for the object, r0 for the function that it may
9422// be an instance of (these are fetched from the stack).
9423void InstanceofStub::Generate(MacroAssembler* masm) {
9424 // Get the object - slow case for smis (we may need to throw an exception
9425 // depending on the rhs).
9426 Label slow, loop, is_instance, is_not_instance;
9427 __ ldr(r0, MemOperand(sp, 1 * kPointerSize));
9428 __ BranchOnSmi(r0, &slow);
9429
9430 // Check that the left hand is a JS object and put map in r3.
9431 __ CompareObjectType(r0, r3, r2, FIRST_JS_OBJECT_TYPE);
9432 __ b(lt, &slow);
9433 __ cmp(r2, Operand(LAST_JS_OBJECT_TYPE));
9434 __ b(gt, &slow);
9435
9436 // Get the prototype of the function (r4 is result, r2 is scratch).
ager@chromium.org5c838252010-02-19 08:53:10 +00009437 __ ldr(r1, MemOperand(sp, 0));
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00009438 // r1 is function, r3 is map.
9439
9440 // Look up the function and the map in the instanceof cache.
9441 Label miss;
9442 __ LoadRoot(ip, Heap::kInstanceofCacheFunctionRootIndex);
9443 __ cmp(r1, ip);
9444 __ b(ne, &miss);
9445 __ LoadRoot(ip, Heap::kInstanceofCacheMapRootIndex);
9446 __ cmp(r3, ip);
9447 __ b(ne, &miss);
9448 __ LoadRoot(r0, Heap::kInstanceofCacheAnswerRootIndex);
9449 __ pop();
9450 __ pop();
9451 __ mov(pc, Operand(lr));
9452
9453 __ bind(&miss);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009454 __ TryGetFunctionPrototype(r1, r4, r2, &slow);
9455
9456 // Check that the function prototype is a JS object.
9457 __ BranchOnSmi(r4, &slow);
9458 __ CompareObjectType(r4, r5, r5, FIRST_JS_OBJECT_TYPE);
9459 __ b(lt, &slow);
9460 __ cmp(r5, Operand(LAST_JS_OBJECT_TYPE));
9461 __ b(gt, &slow);
9462
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00009463 __ StoreRoot(r1, Heap::kInstanceofCacheFunctionRootIndex);
9464 __ StoreRoot(r3, Heap::kInstanceofCacheMapRootIndex);
9465
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009466 // Register mapping: r3 is object map and r4 is function prototype.
9467 // Get prototype of object into r2.
9468 __ ldr(r2, FieldMemOperand(r3, Map::kPrototypeOffset));
9469
9470 // Loop through the prototype chain looking for the function prototype.
9471 __ bind(&loop);
9472 __ cmp(r2, Operand(r4));
9473 __ b(eq, &is_instance);
ager@chromium.orgab99eea2009-08-25 07:05:41 +00009474 __ LoadRoot(ip, Heap::kNullValueRootIndex);
9475 __ cmp(r2, ip);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009476 __ b(eq, &is_not_instance);
9477 __ ldr(r2, FieldMemOperand(r2, HeapObject::kMapOffset));
9478 __ ldr(r2, FieldMemOperand(r2, Map::kPrototypeOffset));
9479 __ jmp(&loop);
9480
9481 __ bind(&is_instance);
9482 __ mov(r0, Operand(Smi::FromInt(0)));
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00009483 __ StoreRoot(r0, Heap::kInstanceofCacheAnswerRootIndex);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009484 __ pop();
9485 __ pop();
9486 __ mov(pc, Operand(lr)); // Return.
9487
9488 __ bind(&is_not_instance);
9489 __ mov(r0, Operand(Smi::FromInt(1)));
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00009490 __ StoreRoot(r0, Heap::kInstanceofCacheAnswerRootIndex);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009491 __ pop();
9492 __ pop();
9493 __ mov(pc, Operand(lr)); // Return.
9494
9495 // Slow-case. Tail call builtin.
9496 __ bind(&slow);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009497 __ InvokeBuiltin(Builtins::INSTANCE_OF, JUMP_JS);
9498}
9499
9500
ager@chromium.org7c537e22008-10-16 08:43:32 +00009501void ArgumentsAccessStub::GenerateReadElement(MacroAssembler* masm) {
9502 // The displacement is the offset of the last parameter (if any)
9503 // relative to the frame pointer.
9504 static const int kDisplacement =
9505 StandardFrameConstants::kCallerSPOffset - kPointerSize;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009506
ager@chromium.org7c537e22008-10-16 08:43:32 +00009507 // Check that the key is a smi.
9508 Label slow;
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009509 __ BranchOnNotSmi(r1, &slow);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009510
ager@chromium.org7c537e22008-10-16 08:43:32 +00009511 // Check if the calling frame is an arguments adaptor frame.
9512 Label adaptor;
9513 __ ldr(r2, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
9514 __ ldr(r3, MemOperand(r2, StandardFrameConstants::kContextOffset));
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009515 __ cmp(r3, Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
ager@chromium.org7c537e22008-10-16 08:43:32 +00009516 __ b(eq, &adaptor);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009517
ager@chromium.org7c537e22008-10-16 08:43:32 +00009518 // Check index against formal parameters count limit passed in
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009519 // through register r0. Use unsigned comparison to get negative
ager@chromium.org7c537e22008-10-16 08:43:32 +00009520 // check for free.
9521 __ cmp(r1, r0);
9522 __ b(cs, &slow);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009523
ager@chromium.org7c537e22008-10-16 08:43:32 +00009524 // Read the argument from the stack and return it.
9525 __ sub(r3, r0, r1);
9526 __ add(r3, fp, Operand(r3, LSL, kPointerSizeLog2 - kSmiTagSize));
9527 __ ldr(r0, MemOperand(r3, kDisplacement));
ager@chromium.org9085a012009-05-11 19:22:57 +00009528 __ Jump(lr);
ager@chromium.org7c537e22008-10-16 08:43:32 +00009529
9530 // Arguments adaptor case: Check index against actual arguments
9531 // limit found in the arguments adaptor frame. Use unsigned
9532 // comparison to get negative check for free.
9533 __ bind(&adaptor);
9534 __ ldr(r0, MemOperand(r2, ArgumentsAdaptorFrameConstants::kLengthOffset));
9535 __ cmp(r1, r0);
9536 __ b(cs, &slow);
9537
9538 // Read the argument from the adaptor frame and return it.
9539 __ sub(r3, r0, r1);
9540 __ add(r3, r2, Operand(r3, LSL, kPointerSizeLog2 - kSmiTagSize));
9541 __ ldr(r0, MemOperand(r3, kDisplacement));
ager@chromium.org9085a012009-05-11 19:22:57 +00009542 __ Jump(lr);
ager@chromium.org7c537e22008-10-16 08:43:32 +00009543
9544 // Slow-case: Handle non-smi or out-of-bounds access to arguments
9545 // by calling the runtime system.
9546 __ bind(&slow);
9547 __ push(r1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009548 __ TailCallRuntime(Runtime::kGetArgumentsProperty, 1, 1);
ager@chromium.org7c537e22008-10-16 08:43:32 +00009549}
9550
9551
9552void ArgumentsAccessStub::GenerateNewObject(MacroAssembler* masm) {
ager@chromium.org5c838252010-02-19 08:53:10 +00009553 // sp[0] : number of parameters
9554 // sp[4] : receiver displacement
9555 // sp[8] : function
9556
ager@chromium.org7c537e22008-10-16 08:43:32 +00009557 // Check if the calling frame is an arguments adaptor frame.
ager@chromium.org5c838252010-02-19 08:53:10 +00009558 Label adaptor_frame, try_allocate, runtime;
ager@chromium.org7c537e22008-10-16 08:43:32 +00009559 __ ldr(r2, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
9560 __ ldr(r3, MemOperand(r2, StandardFrameConstants::kContextOffset));
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009561 __ cmp(r3, Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
ager@chromium.org5c838252010-02-19 08:53:10 +00009562 __ b(eq, &adaptor_frame);
9563
9564 // Get the length from the frame.
9565 __ ldr(r1, MemOperand(sp, 0));
9566 __ b(&try_allocate);
ager@chromium.org7c537e22008-10-16 08:43:32 +00009567
9568 // Patch the arguments.length and the parameters pointer.
ager@chromium.org5c838252010-02-19 08:53:10 +00009569 __ bind(&adaptor_frame);
9570 __ ldr(r1, MemOperand(r2, ArgumentsAdaptorFrameConstants::kLengthOffset));
9571 __ str(r1, MemOperand(sp, 0));
9572 __ add(r3, r2, Operand(r1, LSL, kPointerSizeLog2 - kSmiTagSize));
ager@chromium.org7c537e22008-10-16 08:43:32 +00009573 __ add(r3, r3, Operand(StandardFrameConstants::kCallerSPOffset));
9574 __ str(r3, MemOperand(sp, 1 * kPointerSize));
9575
ager@chromium.org5c838252010-02-19 08:53:10 +00009576 // Try the new space allocation. Start out with computing the size
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00009577 // of the arguments object and the elements array in words.
ager@chromium.org5c838252010-02-19 08:53:10 +00009578 Label add_arguments_object;
9579 __ bind(&try_allocate);
9580 __ cmp(r1, Operand(0));
9581 __ b(eq, &add_arguments_object);
9582 __ mov(r1, Operand(r1, LSR, kSmiTagSize));
9583 __ add(r1, r1, Operand(FixedArray::kHeaderSize / kPointerSize));
9584 __ bind(&add_arguments_object);
9585 __ add(r1, r1, Operand(Heap::kArgumentsObjectSize / kPointerSize));
9586
9587 // Do the allocation of both objects in one go.
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00009588 __ AllocateInNewSpace(
9589 r1,
9590 r0,
9591 r2,
9592 r3,
9593 &runtime,
9594 static_cast<AllocationFlags>(TAG_OBJECT | SIZE_IN_WORDS));
ager@chromium.org5c838252010-02-19 08:53:10 +00009595
9596 // Get the arguments boilerplate from the current (global) context.
9597 int offset = Context::SlotOffset(Context::ARGUMENTS_BOILERPLATE_INDEX);
9598 __ ldr(r4, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_INDEX)));
9599 __ ldr(r4, FieldMemOperand(r4, GlobalObject::kGlobalContextOffset));
9600 __ ldr(r4, MemOperand(r4, offset));
9601
9602 // Copy the JS object part.
9603 for (int i = 0; i < JSObject::kHeaderSize; i += kPointerSize) {
9604 __ ldr(r3, FieldMemOperand(r4, i));
9605 __ str(r3, FieldMemOperand(r0, i));
9606 }
9607
9608 // Setup the callee in-object property.
9609 ASSERT(Heap::arguments_callee_index == 0);
9610 __ ldr(r3, MemOperand(sp, 2 * kPointerSize));
9611 __ str(r3, FieldMemOperand(r0, JSObject::kHeaderSize));
9612
9613 // Get the length (smi tagged) and set that as an in-object property too.
9614 ASSERT(Heap::arguments_length_index == 1);
9615 __ ldr(r1, MemOperand(sp, 0 * kPointerSize));
9616 __ str(r1, FieldMemOperand(r0, JSObject::kHeaderSize + kPointerSize));
9617
9618 // If there are no actual arguments, we're done.
9619 Label done;
9620 __ cmp(r1, Operand(0));
9621 __ b(eq, &done);
9622
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009623 // Get the parameters pointer from the stack.
ager@chromium.org5c838252010-02-19 08:53:10 +00009624 __ ldr(r2, MemOperand(sp, 1 * kPointerSize));
ager@chromium.org5c838252010-02-19 08:53:10 +00009625
9626 // Setup the elements pointer in the allocated arguments object and
9627 // initialize the header in the elements fixed array.
9628 __ add(r4, r0, Operand(Heap::kArgumentsObjectSize));
9629 __ str(r4, FieldMemOperand(r0, JSObject::kElementsOffset));
9630 __ LoadRoot(r3, Heap::kFixedArrayMapRootIndex);
9631 __ str(r3, FieldMemOperand(r4, FixedArray::kMapOffset));
9632 __ str(r1, FieldMemOperand(r4, FixedArray::kLengthOffset));
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009633 __ mov(r1, Operand(r1, LSR, kSmiTagSize)); // Untag the length for the loop.
ager@chromium.org5c838252010-02-19 08:53:10 +00009634
9635 // Copy the fixed array slots.
9636 Label loop;
9637 // Setup r4 to point to the first array slot.
9638 __ add(r4, r4, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
9639 __ bind(&loop);
9640 // Pre-decrement r2 with kPointerSize on each iteration.
9641 // Pre-decrement in order to skip receiver.
9642 __ ldr(r3, MemOperand(r2, kPointerSize, NegPreIndex));
9643 // Post-increment r4 with kPointerSize on each iteration.
9644 __ str(r3, MemOperand(r4, kPointerSize, PostIndex));
9645 __ sub(r1, r1, Operand(1));
9646 __ cmp(r1, Operand(0));
9647 __ b(ne, &loop);
9648
9649 // Return and remove the on-stack parameters.
9650 __ bind(&done);
9651 __ add(sp, sp, Operand(3 * kPointerSize));
9652 __ Ret();
9653
ager@chromium.org7c537e22008-10-16 08:43:32 +00009654 // Do the runtime call to allocate the arguments object.
9655 __ bind(&runtime);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009656 __ TailCallRuntime(Runtime::kNewArgumentsFast, 3, 1);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009657}
9658
9659
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009660void RegExpExecStub::Generate(MacroAssembler* masm) {
9661 // Just jump directly to runtime if native RegExp is not selected at compile
9662 // time or if regexp entry in generated code is turned off runtime switch or
9663 // at compilation.
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00009664#ifdef V8_INTERPRETED_REGEXP
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009665 __ TailCallRuntime(Runtime::kRegExpExec, 4, 1);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00009666#else // V8_INTERPRETED_REGEXP
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009667 if (!FLAG_regexp_entry_native) {
9668 __ TailCallRuntime(Runtime::kRegExpExec, 4, 1);
9669 return;
9670 }
9671
9672 // Stack frame on entry.
9673 // sp[0]: last_match_info (expected JSArray)
9674 // sp[4]: previous index
9675 // sp[8]: subject string
9676 // sp[12]: JSRegExp object
9677
9678 static const int kLastMatchInfoOffset = 0 * kPointerSize;
9679 static const int kPreviousIndexOffset = 1 * kPointerSize;
9680 static const int kSubjectOffset = 2 * kPointerSize;
9681 static const int kJSRegExpOffset = 3 * kPointerSize;
9682
9683 Label runtime, invoke_regexp;
9684
9685 // Allocation of registers for this function. These are in callee save
9686 // registers and will be preserved by the call to the native RegExp code, as
9687 // this code is called using the normal C calling convention. When calling
9688 // directly from generated code the native RegExp code will not do a GC and
9689 // therefore the content of these registers are safe to use after the call.
9690 Register subject = r4;
9691 Register regexp_data = r5;
9692 Register last_match_info_elements = r6;
9693
9694 // Ensure that a RegExp stack is allocated.
9695 ExternalReference address_of_regexp_stack_memory_address =
9696 ExternalReference::address_of_regexp_stack_memory_address();
9697 ExternalReference address_of_regexp_stack_memory_size =
9698 ExternalReference::address_of_regexp_stack_memory_size();
9699 __ mov(r0, Operand(address_of_regexp_stack_memory_size));
9700 __ ldr(r0, MemOperand(r0, 0));
9701 __ tst(r0, Operand(r0));
9702 __ b(eq, &runtime);
9703
9704 // Check that the first argument is a JSRegExp object.
9705 __ ldr(r0, MemOperand(sp, kJSRegExpOffset));
9706 ASSERT_EQ(0, kSmiTag);
9707 __ tst(r0, Operand(kSmiTagMask));
9708 __ b(eq, &runtime);
9709 __ CompareObjectType(r0, r1, r1, JS_REGEXP_TYPE);
9710 __ b(ne, &runtime);
9711
9712 // Check that the RegExp has been compiled (data contains a fixed array).
9713 __ ldr(regexp_data, FieldMemOperand(r0, JSRegExp::kDataOffset));
9714 if (FLAG_debug_code) {
9715 __ tst(regexp_data, Operand(kSmiTagMask));
9716 __ Check(nz, "Unexpected type for RegExp data, FixedArray expected");
9717 __ CompareObjectType(regexp_data, r0, r0, FIXED_ARRAY_TYPE);
9718 __ Check(eq, "Unexpected type for RegExp data, FixedArray expected");
9719 }
9720
9721 // regexp_data: RegExp data (FixedArray)
9722 // Check the type of the RegExp. Only continue if type is JSRegExp::IRREGEXP.
9723 __ ldr(r0, FieldMemOperand(regexp_data, JSRegExp::kDataTagOffset));
9724 __ cmp(r0, Operand(Smi::FromInt(JSRegExp::IRREGEXP)));
9725 __ b(ne, &runtime);
9726
9727 // regexp_data: RegExp data (FixedArray)
9728 // Check that the number of captures fit in the static offsets vector buffer.
9729 __ ldr(r2,
9730 FieldMemOperand(regexp_data, JSRegExp::kIrregexpCaptureCountOffset));
9731 // Calculate number of capture registers (number_of_captures + 1) * 2. This
9732 // uses the asumption that smis are 2 * their untagged value.
9733 ASSERT_EQ(0, kSmiTag);
9734 ASSERT_EQ(1, kSmiTagSize + kSmiShiftSize);
9735 __ add(r2, r2, Operand(2)); // r2 was a smi.
9736 // Check that the static offsets vector buffer is large enough.
9737 __ cmp(r2, Operand(OffsetsVector::kStaticOffsetsVectorSize));
9738 __ b(hi, &runtime);
9739
9740 // r2: Number of capture registers
9741 // regexp_data: RegExp data (FixedArray)
9742 // Check that the second argument is a string.
9743 __ ldr(subject, MemOperand(sp, kSubjectOffset));
9744 __ tst(subject, Operand(kSmiTagMask));
9745 __ b(eq, &runtime);
9746 Condition is_string = masm->IsObjectStringType(subject, r0);
9747 __ b(NegateCondition(is_string), &runtime);
9748 // Get the length of the string to r3.
9749 __ ldr(r3, FieldMemOperand(subject, String::kLengthOffset));
9750
9751 // r2: Number of capture registers
ager@chromium.orgac091b72010-05-05 07:34:42 +00009752 // r3: Length of subject string as a smi
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009753 // subject: Subject string
9754 // regexp_data: RegExp data (FixedArray)
9755 // Check that the third argument is a positive smi less than the subject
9756 // string length. A negative value will be greater (unsigned comparison).
9757 __ ldr(r0, MemOperand(sp, kPreviousIndexOffset));
ager@chromium.orgac091b72010-05-05 07:34:42 +00009758 __ tst(r0, Operand(kSmiTagMask));
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00009759 __ b(ne, &runtime);
ager@chromium.orgac091b72010-05-05 07:34:42 +00009760 __ cmp(r3, Operand(r0));
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00009761 __ b(ls, &runtime);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009762
9763 // r2: Number of capture registers
9764 // subject: Subject string
9765 // regexp_data: RegExp data (FixedArray)
9766 // Check that the fourth object is a JSArray object.
9767 __ ldr(r0, MemOperand(sp, kLastMatchInfoOffset));
9768 __ tst(r0, Operand(kSmiTagMask));
9769 __ b(eq, &runtime);
9770 __ CompareObjectType(r0, r1, r1, JS_ARRAY_TYPE);
9771 __ b(ne, &runtime);
9772 // Check that the JSArray is in fast case.
9773 __ ldr(last_match_info_elements,
9774 FieldMemOperand(r0, JSArray::kElementsOffset));
9775 __ ldr(r0, FieldMemOperand(last_match_info_elements, HeapObject::kMapOffset));
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00009776 __ LoadRoot(ip, Heap::kFixedArrayMapRootIndex);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00009777 __ cmp(r0, ip);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009778 __ b(ne, &runtime);
9779 // Check that the last match info has space for the capture registers and the
9780 // additional information.
9781 __ ldr(r0,
9782 FieldMemOperand(last_match_info_elements, FixedArray::kLengthOffset));
9783 __ add(r2, r2, Operand(RegExpImpl::kLastMatchOverhead));
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009784 __ cmp(r2, Operand(r0, ASR, kSmiTagSize));
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009785 __ b(gt, &runtime);
9786
9787 // subject: Subject string
9788 // regexp_data: RegExp data (FixedArray)
9789 // Check the representation and encoding of the subject string.
9790 Label seq_string;
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009791 __ ldr(r0, FieldMemOperand(subject, HeapObject::kMapOffset));
9792 __ ldrb(r0, FieldMemOperand(r0, Map::kInstanceTypeOffset));
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00009793 // First check for flat string.
9794 __ tst(r0, Operand(kIsNotStringMask | kStringRepresentationMask));
9795 ASSERT_EQ(0, kStringTag | kSeqStringTag);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009796 __ b(eq, &seq_string);
9797
9798 // subject: Subject string
9799 // regexp_data: RegExp data (FixedArray)
9800 // Check for flat cons string.
9801 // A flat cons string is a cons string where the second part is the empty
9802 // string. In that case the subject string is just the first part of the cons
9803 // string. Also in this case the first part of the cons string is known to be
9804 // a sequential string or an external string.
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00009805 ASSERT(kExternalStringTag !=0);
9806 ASSERT_EQ(0, kConsStringTag & kExternalStringTag);
9807 __ tst(r0, Operand(kIsNotStringMask | kExternalStringTag));
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009808 __ b(ne, &runtime);
9809 __ ldr(r0, FieldMemOperand(subject, ConsString::kSecondOffset));
9810 __ LoadRoot(r1, Heap::kEmptyStringRootIndex);
9811 __ cmp(r0, r1);
9812 __ b(ne, &runtime);
9813 __ ldr(subject, FieldMemOperand(subject, ConsString::kFirstOffset));
9814 __ ldr(r0, FieldMemOperand(subject, HeapObject::kMapOffset));
9815 __ ldrb(r0, FieldMemOperand(r0, Map::kInstanceTypeOffset));
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00009816 // Is first part a flat string?
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009817 ASSERT_EQ(0, kSeqStringTag);
9818 __ tst(r0, Operand(kStringRepresentationMask));
9819 __ b(nz, &runtime);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009820
9821 __ bind(&seq_string);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009822 // subject: Subject string
9823 // regexp_data: RegExp data (FixedArray)
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00009824 // r0: Instance type of subject string
9825 ASSERT_EQ(4, kAsciiStringTag);
9826 ASSERT_EQ(0, kTwoByteStringTag);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009827 // Find the code object based on the assumptions above.
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00009828 __ and_(r0, r0, Operand(kStringEncodingMask));
9829 __ mov(r3, Operand(r0, ASR, 2), SetCC);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009830 __ ldr(r7, FieldMemOperand(regexp_data, JSRegExp::kDataAsciiCodeOffset), ne);
9831 __ ldr(r7, FieldMemOperand(regexp_data, JSRegExp::kDataUC16CodeOffset), eq);
9832
9833 // Check that the irregexp code has been generated for the actual string
9834 // encoding. If it has, the field contains a code object otherwise it contains
9835 // the hole.
9836 __ CompareObjectType(r7, r0, r0, CODE_TYPE);
9837 __ b(ne, &runtime);
9838
9839 // r3: encoding of subject string (1 if ascii, 0 if two_byte);
9840 // r7: code
9841 // subject: Subject string
9842 // regexp_data: RegExp data (FixedArray)
9843 // Load used arguments before starting to push arguments for call to native
9844 // RegExp code to avoid handling changing stack height.
9845 __ ldr(r1, MemOperand(sp, kPreviousIndexOffset));
9846 __ mov(r1, Operand(r1, ASR, kSmiTagSize));
9847
9848 // r1: previous index
9849 // r3: encoding of subject string (1 if ascii, 0 if two_byte);
9850 // r7: code
9851 // subject: Subject string
9852 // regexp_data: RegExp data (FixedArray)
9853 // All checks done. Now push arguments for native regexp code.
9854 __ IncrementCounter(&Counters::regexp_entry_native, 1, r0, r2);
9855
9856 static const int kRegExpExecuteArguments = 7;
9857 __ push(lr);
9858 __ PrepareCallCFunction(kRegExpExecuteArguments, r0);
9859
9860 // Argument 7 (sp[8]): Indicate that this is a direct call from JavaScript.
9861 __ mov(r0, Operand(1));
9862 __ str(r0, MemOperand(sp, 2 * kPointerSize));
9863
9864 // Argument 6 (sp[4]): Start (high end) of backtracking stack memory area.
9865 __ mov(r0, Operand(address_of_regexp_stack_memory_address));
9866 __ ldr(r0, MemOperand(r0, 0));
9867 __ mov(r2, Operand(address_of_regexp_stack_memory_size));
9868 __ ldr(r2, MemOperand(r2, 0));
9869 __ add(r0, r0, Operand(r2));
9870 __ str(r0, MemOperand(sp, 1 * kPointerSize));
9871
9872 // Argument 5 (sp[0]): static offsets vector buffer.
9873 __ mov(r0, Operand(ExternalReference::address_of_static_offsets_vector()));
9874 __ str(r0, MemOperand(sp, 0 * kPointerSize));
9875
9876 // For arguments 4 and 3 get string length, calculate start of string data and
9877 // calculate the shift of the index (0 for ASCII and 1 for two byte).
9878 __ ldr(r0, FieldMemOperand(subject, String::kLengthOffset));
ager@chromium.orgac091b72010-05-05 07:34:42 +00009879 __ mov(r0, Operand(r0, ASR, kSmiTagSize));
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009880 ASSERT_EQ(SeqAsciiString::kHeaderSize, SeqTwoByteString::kHeaderSize);
9881 __ add(r9, subject, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
9882 __ eor(r3, r3, Operand(1));
9883 // Argument 4 (r3): End of string data
9884 // Argument 3 (r2): Start of string data
9885 __ add(r2, r9, Operand(r1, LSL, r3));
9886 __ add(r3, r9, Operand(r0, LSL, r3));
9887
9888 // Argument 2 (r1): Previous index.
9889 // Already there
9890
9891 // Argument 1 (r0): Subject string.
9892 __ mov(r0, subject);
9893
9894 // Locate the code entry and call it.
9895 __ add(r7, r7, Operand(Code::kHeaderSize - kHeapObjectTag));
9896 __ CallCFunction(r7, kRegExpExecuteArguments);
9897 __ pop(lr);
9898
9899 // r0: result
9900 // subject: subject string (callee saved)
9901 // regexp_data: RegExp data (callee saved)
9902 // last_match_info_elements: Last match info elements (callee saved)
9903
9904 // Check the result.
9905 Label success;
9906 __ cmp(r0, Operand(NativeRegExpMacroAssembler::SUCCESS));
9907 __ b(eq, &success);
9908 Label failure;
9909 __ cmp(r0, Operand(NativeRegExpMacroAssembler::FAILURE));
9910 __ b(eq, &failure);
9911 __ cmp(r0, Operand(NativeRegExpMacroAssembler::EXCEPTION));
9912 // If not exception it can only be retry. Handle that in the runtime system.
9913 __ b(ne, &runtime);
9914 // Result must now be exception. If there is no pending exception already a
9915 // stack overflow (on the backtrack stack) was detected in RegExp code but
9916 // haven't created the exception yet. Handle that in the runtime system.
9917 // TODO(592): Rerunning the RegExp to get the stack overflow exception.
9918 __ mov(r0, Operand(ExternalReference::the_hole_value_location()));
9919 __ ldr(r0, MemOperand(r0, 0));
9920 __ mov(r1, Operand(ExternalReference(Top::k_pending_exception_address)));
9921 __ ldr(r1, MemOperand(r1, 0));
9922 __ cmp(r0, r1);
9923 __ b(eq, &runtime);
9924 __ bind(&failure);
9925 // For failure and exception return null.
9926 __ mov(r0, Operand(Factory::null_value()));
9927 __ add(sp, sp, Operand(4 * kPointerSize));
9928 __ Ret();
9929
9930 // Process the result from the native regexp code.
9931 __ bind(&success);
9932 __ ldr(r1,
9933 FieldMemOperand(regexp_data, JSRegExp::kIrregexpCaptureCountOffset));
9934 // Calculate number of capture registers (number_of_captures + 1) * 2.
9935 ASSERT_EQ(0, kSmiTag);
9936 ASSERT_EQ(1, kSmiTagSize + kSmiShiftSize);
9937 __ add(r1, r1, Operand(2)); // r1 was a smi.
9938
9939 // r1: number of capture registers
9940 // r4: subject string
9941 // Store the capture count.
9942 __ mov(r2, Operand(r1, LSL, kSmiTagSize + kSmiShiftSize)); // To smi.
9943 __ str(r2, FieldMemOperand(last_match_info_elements,
9944 RegExpImpl::kLastCaptureCountOffset));
9945 // Store last subject and last input.
9946 __ mov(r3, last_match_info_elements); // Moved up to reduce latency.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009947 __ str(subject,
9948 FieldMemOperand(last_match_info_elements,
9949 RegExpImpl::kLastSubjectOffset));
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00009950 __ RecordWrite(r3, Operand(RegExpImpl::kLastSubjectOffset), r2, r7);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009951 __ str(subject,
9952 FieldMemOperand(last_match_info_elements,
9953 RegExpImpl::kLastInputOffset));
9954 __ mov(r3, last_match_info_elements);
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00009955 __ RecordWrite(r3, Operand(RegExpImpl::kLastInputOffset), r2, r7);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009956
9957 // Get the static offsets vector filled by the native regexp code.
9958 ExternalReference address_of_static_offsets_vector =
9959 ExternalReference::address_of_static_offsets_vector();
9960 __ mov(r2, Operand(address_of_static_offsets_vector));
9961
9962 // r1: number of capture registers
9963 // r2: offsets vector
9964 Label next_capture, done;
9965 // Capture register counter starts from number of capture registers and
9966 // counts down until wraping after zero.
9967 __ add(r0,
9968 last_match_info_elements,
9969 Operand(RegExpImpl::kFirstCaptureOffset - kHeapObjectTag));
9970 __ bind(&next_capture);
9971 __ sub(r1, r1, Operand(1), SetCC);
9972 __ b(mi, &done);
9973 // Read the value from the static offsets vector buffer.
9974 __ ldr(r3, MemOperand(r2, kPointerSize, PostIndex));
9975 // Store the smi value in the last match info.
9976 __ mov(r3, Operand(r3, LSL, kSmiTagSize));
9977 __ str(r3, MemOperand(r0, kPointerSize, PostIndex));
9978 __ jmp(&next_capture);
9979 __ bind(&done);
9980
9981 // Return last match info.
9982 __ ldr(r0, MemOperand(sp, kLastMatchInfoOffset));
9983 __ add(sp, sp, Operand(4 * kPointerSize));
9984 __ Ret();
9985
9986 // Do the runtime call to execute the regexp.
9987 __ bind(&runtime);
9988 __ TailCallRuntime(Runtime::kRegExpExec, 4, 1);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00009989#endif // V8_INTERPRETED_REGEXP
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009990}
9991
9992
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009993void CallFunctionStub::Generate(MacroAssembler* masm) {
9994 Label slow;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009995
9996 // If the receiver might be a value (string, number or boolean) check for this
9997 // and box it if it is.
9998 if (ReceiverMightBeValue()) {
9999 // Get the receiver from the stack.
10000 // function, receiver [, arguments]
10001 Label receiver_is_value, receiver_is_js_object;
10002 __ ldr(r1, MemOperand(sp, argc_ * kPointerSize));
10003
10004 // Check if receiver is a smi (which is a number value).
10005 __ BranchOnSmi(r1, &receiver_is_value);
10006
10007 // Check if the receiver is a valid JS object.
10008 __ CompareObjectType(r1, r2, r2, FIRST_JS_OBJECT_TYPE);
10009 __ b(ge, &receiver_is_js_object);
10010
10011 // Call the runtime to box the value.
10012 __ bind(&receiver_is_value);
10013 __ EnterInternalFrame();
10014 __ push(r1);
10015 __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_JS);
10016 __ LeaveInternalFrame();
10017 __ str(r0, MemOperand(sp, argc_ * kPointerSize));
10018
10019 __ bind(&receiver_is_js_object);
10020 }
10021
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000010022 // Get the function to call from the stack.
10023 // function, receiver [, arguments]
10024 __ ldr(r1, MemOperand(sp, (argc_ + 1) * kPointerSize));
10025
10026 // Check that the function is really a JavaScript function.
10027 // r1: pushed function (to be verified)
ager@chromium.orgeadaf222009-06-16 09:43:10 +000010028 __ BranchOnSmi(r1, &slow);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000010029 // Get the map of the function object.
ager@chromium.orgeadaf222009-06-16 09:43:10 +000010030 __ CompareObjectType(r1, r2, r2, JS_FUNCTION_TYPE);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000010031 __ b(ne, &slow);
10032
10033 // Fast-case: Invoke the function now.
10034 // r1: pushed function
10035 ParameterCount actual(argc_);
10036 __ InvokeFunction(r1, actual, JUMP_FUNCTION);
10037
10038 // Slow-case: Non-function called.
10039 __ bind(&slow);
ager@chromium.org5c838252010-02-19 08:53:10 +000010040 // CALL_NON_FUNCTION expects the non-function callee as receiver (instead
10041 // of the original receiver from the call site).
10042 __ str(r1, MemOperand(sp, argc_ * kPointerSize));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000010043 __ mov(r0, Operand(argc_)); // Setup the number of arguments.
ager@chromium.org3bf7b912008-11-17 09:09:45 +000010044 __ mov(r2, Operand(0));
10045 __ GetBuiltinEntry(r3, Builtins::CALL_NON_FUNCTION);
10046 __ Jump(Handle<Code>(Builtins::builtin(Builtins::ArgumentsAdaptorTrampoline)),
10047 RelocInfo::CODE_TARGET);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000010048}
10049
10050
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +000010051// Unfortunately you have to run without snapshots to see most of these
10052// names in the profile since most compare stubs end up in the snapshot.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000010053const char* CompareStub::GetName() {
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +000010054 if (name_ != NULL) return name_;
10055 const int kMaxNameLength = 100;
10056 name_ = Bootstrapper::AllocateAutoDeletedArray(kMaxNameLength);
10057 if (name_ == NULL) return "OOM";
10058
10059 const char* cc_name;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000010060 switch (cc_) {
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +000010061 case lt: cc_name = "LT"; break;
10062 case gt: cc_name = "GT"; break;
10063 case le: cc_name = "LE"; break;
10064 case ge: cc_name = "GE"; break;
10065 case eq: cc_name = "EQ"; break;
10066 case ne: cc_name = "NE"; break;
10067 default: cc_name = "UnknownCondition"; break;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000010068 }
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +000010069
10070 const char* strict_name = "";
10071 if (strict_ && (cc_ == eq || cc_ == ne)) {
10072 strict_name = "_STRICT";
10073 }
10074
10075 const char* never_nan_nan_name = "";
10076 if (never_nan_nan_ && (cc_ == eq || cc_ == ne)) {
10077 never_nan_nan_name = "_NO_NAN";
10078 }
10079
10080 const char* include_number_compare_name = "";
10081 if (!include_number_compare_) {
10082 include_number_compare_name = "_NO_NUMBER";
10083 }
10084
10085 OS::SNPrintF(Vector<char>(name_, kMaxNameLength),
10086 "CompareStub_%s%s%s%s",
10087 cc_name,
10088 strict_name,
10089 never_nan_nan_name,
10090 include_number_compare_name);
10091 return name_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000010092}
10093
10094
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010095int CompareStub::MinorKey() {
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +000010096 // Encode the three parameters in a unique 16 bit value. To avoid duplicate
10097 // stubs the never NaN NaN condition is only taken into account if the
10098 // condition is equals.
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +000010099 ASSERT((static_cast<unsigned>(cc_) >> 28) < (1 << 13));
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +000010100 return ConditionField::encode(static_cast<unsigned>(cc_) >> 28)
10101 | StrictField::encode(strict_)
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +000010102 | NeverNanNanField::encode(cc_ == eq ? never_nan_nan_ : false)
10103 | IncludeNumberCompareField::encode(include_number_compare_);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010104}
10105
10106
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010107// StringCharCodeAtGenerator
10108
10109void StringCharCodeAtGenerator::GenerateFast(MacroAssembler* masm) {
10110 Label flat_string;
ager@chromium.orgac091b72010-05-05 07:34:42 +000010111 Label ascii_string;
10112 Label got_char_code;
10113
10114 // If the receiver is a smi trigger the non-string case.
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010115 __ BranchOnSmi(object_, receiver_not_string_);
ager@chromium.orgac091b72010-05-05 07:34:42 +000010116
10117 // Fetch the instance type of the receiver into result register.
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010118 __ ldr(result_, FieldMemOperand(object_, HeapObject::kMapOffset));
10119 __ ldrb(result_, FieldMemOperand(result_, Map::kInstanceTypeOffset));
ager@chromium.orgac091b72010-05-05 07:34:42 +000010120 // If the receiver is not a string trigger the non-string case.
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010121 __ tst(result_, Operand(kIsNotStringMask));
10122 __ b(ne, receiver_not_string_);
ager@chromium.orgac091b72010-05-05 07:34:42 +000010123
10124 // If the index is non-smi trigger the non-smi case.
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010125 __ BranchOnNotSmi(index_, &index_not_smi_);
10126
10127 // Put smi-tagged index into scratch register.
10128 __ mov(scratch_, index_);
10129 __ bind(&got_smi_index_);
ager@chromium.orgac091b72010-05-05 07:34:42 +000010130
10131 // Check for index out of range.
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010132 __ ldr(ip, FieldMemOperand(object_, String::kLengthOffset));
10133 __ cmp(ip, Operand(scratch_));
10134 __ b(ls, index_out_of_range_);
ager@chromium.orgac091b72010-05-05 07:34:42 +000010135
10136 // We need special handling for non-flat strings.
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010137 ASSERT(kSeqStringTag == 0);
10138 __ tst(result_, Operand(kStringRepresentationMask));
10139 __ b(eq, &flat_string);
ager@chromium.orgac091b72010-05-05 07:34:42 +000010140
10141 // Handle non-flat strings.
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010142 __ tst(result_, Operand(kIsConsStringMask));
10143 __ b(eq, &call_runtime_);
ager@chromium.orgac091b72010-05-05 07:34:42 +000010144
10145 // ConsString.
10146 // Check whether the right hand side is the empty string (i.e. if
10147 // this is really a flat string in a cons string). If that is not
10148 // the case we would rather go to the runtime system now to flatten
10149 // the string.
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010150 __ ldr(result_, FieldMemOperand(object_, ConsString::kSecondOffset));
10151 __ LoadRoot(ip, Heap::kEmptyStringRootIndex);
10152 __ cmp(result_, Operand(ip));
10153 __ b(ne, &call_runtime_);
ager@chromium.orgac091b72010-05-05 07:34:42 +000010154 // Get the first of the two strings and load its instance type.
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010155 __ ldr(object_, FieldMemOperand(object_, ConsString::kFirstOffset));
10156 __ ldr(result_, FieldMemOperand(object_, HeapObject::kMapOffset));
10157 __ ldrb(result_, FieldMemOperand(result_, Map::kInstanceTypeOffset));
10158 // If the first cons component is also non-flat, then go to runtime.
10159 ASSERT(kSeqStringTag == 0);
10160 __ tst(result_, Operand(kStringRepresentationMask));
10161 __ b(nz, &call_runtime_);
10162
10163 // Check for 1-byte or 2-byte string.
10164 __ bind(&flat_string);
10165 ASSERT(kAsciiStringTag != 0);
10166 __ tst(result_, Operand(kStringEncodingMask));
10167 __ b(nz, &ascii_string);
10168
10169 // 2-byte string.
10170 // Load the 2-byte character code into the result register. We can
10171 // add without shifting since the smi tag size is the log2 of the
10172 // number of bytes in a two-byte character.
10173 ASSERT(kSmiTag == 0 && kSmiTagSize == 1 && kSmiShiftSize == 0);
10174 __ add(scratch_, object_, Operand(scratch_));
10175 __ ldrh(result_, FieldMemOperand(scratch_, SeqTwoByteString::kHeaderSize));
10176 __ jmp(&got_char_code);
ager@chromium.orgac091b72010-05-05 07:34:42 +000010177
10178 // ASCII string.
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010179 // Load the byte into the result register.
ager@chromium.orgac091b72010-05-05 07:34:42 +000010180 __ bind(&ascii_string);
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010181 __ add(scratch_, object_, Operand(scratch_, LSR, kSmiTagSize));
10182 __ ldrb(result_, FieldMemOperand(scratch_, SeqAsciiString::kHeaderSize));
ager@chromium.orgac091b72010-05-05 07:34:42 +000010183
10184 __ bind(&got_char_code);
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010185 __ mov(result_, Operand(result_, LSL, kSmiTagSize));
10186 __ bind(&exit_);
ager@chromium.orgac091b72010-05-05 07:34:42 +000010187}
10188
10189
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010190void StringCharCodeAtGenerator::GenerateSlow(
10191 MacroAssembler* masm, const RuntimeCallHelper& call_helper) {
10192 __ Abort("Unexpected fallthrough to CharCodeAt slow case");
ager@chromium.orgac091b72010-05-05 07:34:42 +000010193
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010194 // Index is not a smi.
10195 __ bind(&index_not_smi_);
10196 // If index is a heap number, try converting it to an integer.
ager@chromium.org2cc82ae2010-06-14 07:35:38 +000010197 __ CheckMap(index_,
10198 scratch_,
10199 Heap::kHeapNumberMapRootIndex,
10200 index_not_number_,
10201 true);
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010202 call_helper.BeforeCall(masm);
sgjesse@chromium.org82dbbab2010-06-02 08:57:44 +000010203 __ Push(object_, index_);
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010204 __ push(index_); // Consumed by runtime conversion function.
10205 if (index_flags_ == STRING_INDEX_IS_NUMBER) {
10206 __ CallRuntime(Runtime::kNumberToIntegerMapMinusZero, 1);
10207 } else {
10208 ASSERT(index_flags_ == STRING_INDEX_IS_ARRAY_INDEX);
10209 // NumberToSmi discards numbers that are not exact integers.
10210 __ CallRuntime(Runtime::kNumberToSmi, 1);
10211 }
10212 if (!scratch_.is(r0)) {
10213 // Save the conversion result before the pop instructions below
10214 // have a chance to overwrite it.
10215 __ mov(scratch_, r0);
10216 }
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010217 __ pop(index_);
10218 __ pop(object_);
sgjesse@chromium.org82dbbab2010-06-02 08:57:44 +000010219 // Reload the instance type.
10220 __ ldr(result_, FieldMemOperand(object_, HeapObject::kMapOffset));
10221 __ ldrb(result_, FieldMemOperand(result_, Map::kInstanceTypeOffset));
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010222 call_helper.AfterCall(masm);
10223 // If index is still not a smi, it must be out of range.
10224 __ BranchOnNotSmi(scratch_, index_out_of_range_);
10225 // Otherwise, return to the fast path.
10226 __ jmp(&got_smi_index_);
ager@chromium.orgac091b72010-05-05 07:34:42 +000010227
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010228 // Call runtime. We get here when the receiver is a string and the
10229 // index is a number, but the code of getting the actual character
10230 // is too complex (e.g., when the string needs to be flattened).
10231 __ bind(&call_runtime_);
10232 call_helper.BeforeCall(masm);
10233 __ Push(object_, index_);
10234 __ CallRuntime(Runtime::kStringCharCodeAt, 2);
10235 if (!result_.is(r0)) {
10236 __ mov(result_, r0);
10237 }
10238 call_helper.AfterCall(masm);
10239 __ jmp(&exit_);
10240
10241 __ Abort("Unexpected fallthrough from CharCodeAt slow case");
10242}
10243
10244
10245// -------------------------------------------------------------------------
10246// StringCharFromCodeGenerator
10247
10248void StringCharFromCodeGenerator::GenerateFast(MacroAssembler* masm) {
ager@chromium.orgac091b72010-05-05 07:34:42 +000010249 // Fast case of Heap::LookupSingleCharacterStringFromCode.
10250 ASSERT(kSmiTag == 0);
10251 ASSERT(kSmiShiftSize == 0);
10252 ASSERT(IsPowerOf2(String::kMaxAsciiCharCode + 1));
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010253 __ tst(code_,
10254 Operand(kSmiTagMask |
10255 ((~String::kMaxAsciiCharCode) << kSmiTagSize)));
10256 __ b(nz, &slow_case_);
ager@chromium.orgac091b72010-05-05 07:34:42 +000010257
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010258 __ LoadRoot(result_, Heap::kSingleCharacterStringCacheRootIndex);
10259 // At this point code register contains smi tagged ascii char code.
ager@chromium.orgac091b72010-05-05 07:34:42 +000010260 ASSERT(kSmiTag == 0);
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010261 __ add(result_, result_, Operand(code_, LSL, kPointerSizeLog2 - kSmiTagSize));
10262 __ ldr(result_, FieldMemOperand(result_, FixedArray::kHeaderSize));
10263 __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
10264 __ cmp(result_, Operand(ip));
10265 __ b(eq, &slow_case_);
10266 __ bind(&exit_);
10267}
ager@chromium.orgac091b72010-05-05 07:34:42 +000010268
ager@chromium.orgac091b72010-05-05 07:34:42 +000010269
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010270void StringCharFromCodeGenerator::GenerateSlow(
10271 MacroAssembler* masm, const RuntimeCallHelper& call_helper) {
10272 __ Abort("Unexpected fallthrough to CharFromCode slow case");
10273
10274 __ bind(&slow_case_);
10275 call_helper.BeforeCall(masm);
10276 __ push(code_);
10277 __ CallRuntime(Runtime::kCharFromCode, 1);
10278 if (!result_.is(r0)) {
10279 __ mov(result_, r0);
ager@chromium.orgac091b72010-05-05 07:34:42 +000010280 }
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010281 call_helper.AfterCall(masm);
10282 __ jmp(&exit_);
10283
10284 __ Abort("Unexpected fallthrough from CharFromCode slow case");
10285}
10286
10287
10288// -------------------------------------------------------------------------
10289// StringCharAtGenerator
10290
10291void StringCharAtGenerator::GenerateFast(MacroAssembler* masm) {
10292 char_code_at_generator_.GenerateFast(masm);
10293 char_from_code_generator_.GenerateFast(masm);
10294}
10295
10296
10297void StringCharAtGenerator::GenerateSlow(
10298 MacroAssembler* masm, const RuntimeCallHelper& call_helper) {
10299 char_code_at_generator_.GenerateSlow(masm, call_helper);
10300 char_from_code_generator_.GenerateSlow(masm, call_helper);
ager@chromium.orgac091b72010-05-05 07:34:42 +000010301}
10302
10303
10304void StringHelper::GenerateCopyCharacters(MacroAssembler* masm,
10305 Register dest,
10306 Register src,
10307 Register count,
10308 Register scratch,
10309 bool ascii) {
ager@chromium.org5c838252010-02-19 08:53:10 +000010310 Label loop;
10311 Label done;
10312 // This loop just copies one character at a time, as it is only used for very
10313 // short strings.
10314 if (!ascii) {
10315 __ add(count, count, Operand(count), SetCC);
10316 } else {
10317 __ cmp(count, Operand(0));
10318 }
10319 __ b(eq, &done);
10320
10321 __ bind(&loop);
10322 __ ldrb(scratch, MemOperand(src, 1, PostIndex));
10323 // Perform sub between load and dependent store to get the load time to
10324 // complete.
10325 __ sub(count, count, Operand(1), SetCC);
10326 __ strb(scratch, MemOperand(dest, 1, PostIndex));
10327 // last iteration.
10328 __ b(gt, &loop);
10329
10330 __ bind(&done);
10331}
10332
10333
10334enum CopyCharactersFlags {
10335 COPY_ASCII = 1,
10336 DEST_ALWAYS_ALIGNED = 2
10337};
10338
10339
ager@chromium.orgac091b72010-05-05 07:34:42 +000010340void StringHelper::GenerateCopyCharactersLong(MacroAssembler* masm,
10341 Register dest,
10342 Register src,
10343 Register count,
10344 Register scratch1,
10345 Register scratch2,
10346 Register scratch3,
10347 Register scratch4,
10348 Register scratch5,
10349 int flags) {
ager@chromium.org5c838252010-02-19 08:53:10 +000010350 bool ascii = (flags & COPY_ASCII) != 0;
10351 bool dest_always_aligned = (flags & DEST_ALWAYS_ALIGNED) != 0;
10352
10353 if (dest_always_aligned && FLAG_debug_code) {
10354 // Check that destination is actually word aligned if the flag says
10355 // that it is.
10356 __ tst(dest, Operand(kPointerAlignmentMask));
10357 __ Check(eq, "Destination of copy not aligned.");
10358 }
10359
10360 const int kReadAlignment = 4;
10361 const int kReadAlignmentMask = kReadAlignment - 1;
10362 // Ensure that reading an entire aligned word containing the last character
10363 // of a string will not read outside the allocated area (because we pad up
10364 // to kObjectAlignment).
10365 ASSERT(kObjectAlignment >= kReadAlignment);
10366 // Assumes word reads and writes are little endian.
10367 // Nothing to do for zero characters.
10368 Label done;
10369 if (!ascii) {
10370 __ add(count, count, Operand(count), SetCC);
10371 } else {
10372 __ cmp(count, Operand(0));
10373 }
10374 __ b(eq, &done);
10375
10376 // Assume that you cannot read (or write) unaligned.
10377 Label byte_loop;
10378 // Must copy at least eight bytes, otherwise just do it one byte at a time.
10379 __ cmp(count, Operand(8));
10380 __ add(count, dest, Operand(count));
10381 Register limit = count; // Read until src equals this.
10382 __ b(lt, &byte_loop);
10383
10384 if (!dest_always_aligned) {
10385 // Align dest by byte copying. Copies between zero and three bytes.
10386 __ and_(scratch4, dest, Operand(kReadAlignmentMask), SetCC);
10387 Label dest_aligned;
10388 __ b(eq, &dest_aligned);
10389 __ cmp(scratch4, Operand(2));
10390 __ ldrb(scratch1, MemOperand(src, 1, PostIndex));
10391 __ ldrb(scratch2, MemOperand(src, 1, PostIndex), le);
10392 __ ldrb(scratch3, MemOperand(src, 1, PostIndex), lt);
10393 __ strb(scratch1, MemOperand(dest, 1, PostIndex));
10394 __ strb(scratch2, MemOperand(dest, 1, PostIndex), le);
10395 __ strb(scratch3, MemOperand(dest, 1, PostIndex), lt);
10396 __ bind(&dest_aligned);
10397 }
10398
10399 Label simple_loop;
10400
10401 __ sub(scratch4, dest, Operand(src));
10402 __ and_(scratch4, scratch4, Operand(0x03), SetCC);
10403 __ b(eq, &simple_loop);
10404 // Shift register is number of bits in a source word that
10405 // must be combined with bits in the next source word in order
10406 // to create a destination word.
10407
10408 // Complex loop for src/dst that are not aligned the same way.
10409 {
10410 Label loop;
10411 __ mov(scratch4, Operand(scratch4, LSL, 3));
10412 Register left_shift = scratch4;
10413 __ and_(src, src, Operand(~3)); // Round down to load previous word.
10414 __ ldr(scratch1, MemOperand(src, 4, PostIndex));
10415 // Store the "shift" most significant bits of scratch in the least
10416 // signficant bits (i.e., shift down by (32-shift)).
10417 __ rsb(scratch2, left_shift, Operand(32));
10418 Register right_shift = scratch2;
10419 __ mov(scratch1, Operand(scratch1, LSR, right_shift));
10420
10421 __ bind(&loop);
10422 __ ldr(scratch3, MemOperand(src, 4, PostIndex));
10423 __ sub(scratch5, limit, Operand(dest));
10424 __ orr(scratch1, scratch1, Operand(scratch3, LSL, left_shift));
10425 __ str(scratch1, MemOperand(dest, 4, PostIndex));
10426 __ mov(scratch1, Operand(scratch3, LSR, right_shift));
10427 // Loop if four or more bytes left to copy.
10428 // Compare to eight, because we did the subtract before increasing dst.
10429 __ sub(scratch5, scratch5, Operand(8), SetCC);
10430 __ b(ge, &loop);
10431 }
10432 // There is now between zero and three bytes left to copy (negative that
10433 // number is in scratch5), and between one and three bytes already read into
10434 // scratch1 (eight times that number in scratch4). We may have read past
10435 // the end of the string, but because objects are aligned, we have not read
10436 // past the end of the object.
10437 // Find the minimum of remaining characters to move and preloaded characters
10438 // and write those as bytes.
10439 __ add(scratch5, scratch5, Operand(4), SetCC);
10440 __ b(eq, &done);
10441 __ cmp(scratch4, Operand(scratch5, LSL, 3), ne);
10442 // Move minimum of bytes read and bytes left to copy to scratch4.
10443 __ mov(scratch5, Operand(scratch4, LSR, 3), LeaveCC, lt);
10444 // Between one and three (value in scratch5) characters already read into
10445 // scratch ready to write.
10446 __ cmp(scratch5, Operand(2));
10447 __ strb(scratch1, MemOperand(dest, 1, PostIndex));
10448 __ mov(scratch1, Operand(scratch1, LSR, 8), LeaveCC, ge);
10449 __ strb(scratch1, MemOperand(dest, 1, PostIndex), ge);
10450 __ mov(scratch1, Operand(scratch1, LSR, 8), LeaveCC, gt);
10451 __ strb(scratch1, MemOperand(dest, 1, PostIndex), gt);
10452 // Copy any remaining bytes.
10453 __ b(&byte_loop);
10454
10455 // Simple loop.
10456 // Copy words from src to dst, until less than four bytes left.
10457 // Both src and dest are word aligned.
10458 __ bind(&simple_loop);
10459 {
10460 Label loop;
10461 __ bind(&loop);
10462 __ ldr(scratch1, MemOperand(src, 4, PostIndex));
10463 __ sub(scratch3, limit, Operand(dest));
10464 __ str(scratch1, MemOperand(dest, 4, PostIndex));
10465 // Compare to 8, not 4, because we do the substraction before increasing
10466 // dest.
10467 __ cmp(scratch3, Operand(8));
10468 __ b(ge, &loop);
10469 }
10470
10471 // Copy bytes from src to dst until dst hits limit.
10472 __ bind(&byte_loop);
10473 __ cmp(dest, Operand(limit));
10474 __ ldrb(scratch1, MemOperand(src, 1, PostIndex), lt);
10475 __ b(ge, &done);
10476 __ strb(scratch1, MemOperand(dest, 1, PostIndex));
10477 __ b(&byte_loop);
10478
10479 __ bind(&done);
10480}
10481
10482
ager@chromium.orgac091b72010-05-05 07:34:42 +000010483void StringHelper::GenerateTwoCharacterSymbolTableProbe(MacroAssembler* masm,
10484 Register c1,
10485 Register c2,
10486 Register scratch1,
10487 Register scratch2,
10488 Register scratch3,
10489 Register scratch4,
10490 Register scratch5,
10491 Label* not_found) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010492 // Register scratch3 is the general scratch register in this function.
10493 Register scratch = scratch3;
10494
10495 // Make sure that both characters are not digits as such strings has a
10496 // different hash algorithm. Don't try to look for these in the symbol table.
10497 Label not_array_index;
10498 __ sub(scratch, c1, Operand(static_cast<int>('0')));
10499 __ cmp(scratch, Operand(static_cast<int>('9' - '0')));
10500 __ b(hi, &not_array_index);
10501 __ sub(scratch, c2, Operand(static_cast<int>('0')));
10502 __ cmp(scratch, Operand(static_cast<int>('9' - '0')));
10503
10504 // If check failed combine both characters into single halfword.
10505 // This is required by the contract of the method: code at the
10506 // not_found branch expects this combination in c1 register
10507 __ orr(c1, c1, Operand(c2, LSL, kBitsPerByte), LeaveCC, ls);
10508 __ b(ls, not_found);
10509
10510 __ bind(&not_array_index);
10511 // Calculate the two character string hash.
10512 Register hash = scratch1;
ager@chromium.orgac091b72010-05-05 07:34:42 +000010513 StringHelper::GenerateHashInit(masm, hash, c1);
10514 StringHelper::GenerateHashAddCharacter(masm, hash, c2);
10515 StringHelper::GenerateHashGetHash(masm, hash);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010516
10517 // Collect the two characters in a register.
10518 Register chars = c1;
10519 __ orr(chars, chars, Operand(c2, LSL, kBitsPerByte));
10520
10521 // chars: two character string, char 1 in byte 0 and char 2 in byte 1.
10522 // hash: hash of two character string.
10523
10524 // Load symbol table
10525 // Load address of first element of the symbol table.
10526 Register symbol_table = c2;
10527 __ LoadRoot(symbol_table, Heap::kSymbolTableRootIndex);
10528
10529 // Load undefined value
10530 Register undefined = scratch4;
10531 __ LoadRoot(undefined, Heap::kUndefinedValueRootIndex);
10532
10533 // Calculate capacity mask from the symbol table capacity.
10534 Register mask = scratch2;
10535 __ ldr(mask, FieldMemOperand(symbol_table, SymbolTable::kCapacityOffset));
10536 __ mov(mask, Operand(mask, ASR, 1));
10537 __ sub(mask, mask, Operand(1));
10538
10539 // Calculate untagged address of the first element of the symbol table.
10540 Register first_symbol_table_element = symbol_table;
10541 __ add(first_symbol_table_element, symbol_table,
10542 Operand(SymbolTable::kElementsStartOffset - kHeapObjectTag));
10543
10544 // Registers
10545 // chars: two character string, char 1 in byte 0 and char 2 in byte 1.
10546 // hash: hash of two character string
10547 // mask: capacity mask
10548 // first_symbol_table_element: address of the first element of
10549 // the symbol table
10550 // scratch: -
10551
10552 // Perform a number of probes in the symbol table.
10553 static const int kProbes = 4;
10554 Label found_in_symbol_table;
10555 Label next_probe[kProbes];
10556 for (int i = 0; i < kProbes; i++) {
10557 Register candidate = scratch5; // Scratch register contains candidate.
10558
10559 // Calculate entry in symbol table.
10560 if (i > 0) {
10561 __ add(candidate, hash, Operand(SymbolTable::GetProbeOffset(i)));
10562 } else {
10563 __ mov(candidate, hash);
10564 }
10565
10566 __ and_(candidate, candidate, Operand(mask));
10567
10568 // Load the entry from the symble table.
10569 ASSERT_EQ(1, SymbolTable::kEntrySize);
10570 __ ldr(candidate,
10571 MemOperand(first_symbol_table_element,
10572 candidate,
10573 LSL,
10574 kPointerSizeLog2));
10575
10576 // If entry is undefined no string with this hash can be found.
10577 __ cmp(candidate, undefined);
10578 __ b(eq, not_found);
10579
10580 // If length is not 2 the string is not a candidate.
10581 __ ldr(scratch, FieldMemOperand(candidate, String::kLengthOffset));
ager@chromium.orgac091b72010-05-05 07:34:42 +000010582 __ cmp(scratch, Operand(Smi::FromInt(2)));
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010583 __ b(ne, &next_probe[i]);
10584
10585 // Check that the candidate is a non-external ascii string.
10586 __ ldr(scratch, FieldMemOperand(candidate, HeapObject::kMapOffset));
10587 __ ldrb(scratch, FieldMemOperand(scratch, Map::kInstanceTypeOffset));
10588 __ JumpIfInstanceTypeIsNotSequentialAscii(scratch, scratch,
10589 &next_probe[i]);
10590
10591 // Check if the two characters match.
10592 // Assumes that word load is little endian.
10593 __ ldrh(scratch, FieldMemOperand(candidate, SeqAsciiString::kHeaderSize));
10594 __ cmp(chars, scratch);
10595 __ b(eq, &found_in_symbol_table);
10596 __ bind(&next_probe[i]);
10597 }
10598
10599 // No matching 2 character string found by probing.
10600 __ jmp(not_found);
10601
10602 // Scratch register contains result when we fall through to here.
10603 Register result = scratch;
10604 __ bind(&found_in_symbol_table);
ager@chromium.org357bf652010-04-12 11:30:10 +000010605 __ Move(r0, result);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010606}
10607
10608
ager@chromium.orgac091b72010-05-05 07:34:42 +000010609void StringHelper::GenerateHashInit(MacroAssembler* masm,
10610 Register hash,
10611 Register character) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010612 // hash = character + (character << 10);
10613 __ add(hash, character, Operand(character, LSL, 10));
10614 // hash ^= hash >> 6;
10615 __ eor(hash, hash, Operand(hash, ASR, 6));
10616}
10617
10618
ager@chromium.orgac091b72010-05-05 07:34:42 +000010619void StringHelper::GenerateHashAddCharacter(MacroAssembler* masm,
10620 Register hash,
10621 Register character) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010622 // hash += character;
10623 __ add(hash, hash, Operand(character));
10624 // hash += hash << 10;
10625 __ add(hash, hash, Operand(hash, LSL, 10));
10626 // hash ^= hash >> 6;
10627 __ eor(hash, hash, Operand(hash, ASR, 6));
10628}
10629
10630
ager@chromium.orgac091b72010-05-05 07:34:42 +000010631void StringHelper::GenerateHashGetHash(MacroAssembler* masm,
10632 Register hash) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010633 // hash += hash << 3;
10634 __ add(hash, hash, Operand(hash, LSL, 3));
10635 // hash ^= hash >> 11;
10636 __ eor(hash, hash, Operand(hash, ASR, 11));
10637 // hash += hash << 15;
10638 __ add(hash, hash, Operand(hash, LSL, 15), SetCC);
10639
10640 // if (hash == 0) hash = 27;
10641 __ mov(hash, Operand(27), LeaveCC, nz);
10642}
10643
10644
ager@chromium.org5c838252010-02-19 08:53:10 +000010645void SubStringStub::Generate(MacroAssembler* masm) {
10646 Label runtime;
10647
10648 // Stack frame on entry.
10649 // lr: return address
10650 // sp[0]: to
10651 // sp[4]: from
10652 // sp[8]: string
10653
10654 // This stub is called from the native-call %_SubString(...), so
10655 // nothing can be assumed about the arguments. It is tested that:
10656 // "string" is a sequential string,
10657 // both "from" and "to" are smis, and
10658 // 0 <= from <= to <= string.length.
10659 // If any of these assumptions fail, we call the runtime system.
10660
10661 static const int kToOffset = 0 * kPointerSize;
10662 static const int kFromOffset = 1 * kPointerSize;
10663 static const int kStringOffset = 2 * kPointerSize;
10664
10665
10666 // Check bounds and smi-ness.
10667 __ ldr(r7, MemOperand(sp, kToOffset));
10668 __ ldr(r6, MemOperand(sp, kFromOffset));
10669 ASSERT_EQ(0, kSmiTag);
10670 ASSERT_EQ(1, kSmiTagSize + kSmiShiftSize);
10671 // I.e., arithmetic shift right by one un-smi-tags.
10672 __ mov(r2, Operand(r7, ASR, 1), SetCC);
10673 __ mov(r3, Operand(r6, ASR, 1), SetCC, cc);
10674 // If either r2 or r6 had the smi tag bit set, then carry is set now.
10675 __ b(cs, &runtime); // Either "from" or "to" is not a smi.
10676 __ b(mi, &runtime); // From is negative.
10677
10678 __ sub(r2, r2, Operand(r3), SetCC);
10679 __ b(mi, &runtime); // Fail if from > to.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010680 // Special handling of sub-strings of length 1 and 2. One character strings
10681 // are handled in the runtime system (looked up in the single character
10682 // cache). Two character strings are looked for in the symbol cache.
ager@chromium.org5c838252010-02-19 08:53:10 +000010683 __ cmp(r2, Operand(2));
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010684 __ b(lt, &runtime);
ager@chromium.org5c838252010-02-19 08:53:10 +000010685
10686 // r2: length
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010687 // r3: from index (untaged smi)
ager@chromium.org5c838252010-02-19 08:53:10 +000010688 // r6: from (smi)
10689 // r7: to (smi)
10690
10691 // Make sure first argument is a sequential (or flat) string.
10692 __ ldr(r5, MemOperand(sp, kStringOffset));
10693 ASSERT_EQ(0, kSmiTag);
10694 __ tst(r5, Operand(kSmiTagMask));
10695 __ b(eq, &runtime);
10696 Condition is_string = masm->IsObjectStringType(r5, r1);
10697 __ b(NegateCondition(is_string), &runtime);
10698
10699 // r1: instance type
10700 // r2: length
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010701 // r3: from index (untaged smi)
ager@chromium.org5c838252010-02-19 08:53:10 +000010702 // r5: string
10703 // r6: from (smi)
10704 // r7: to (smi)
10705 Label seq_string;
10706 __ and_(r4, r1, Operand(kStringRepresentationMask));
10707 ASSERT(kSeqStringTag < kConsStringTag);
10708 ASSERT(kExternalStringTag > kConsStringTag);
10709 __ cmp(r4, Operand(kConsStringTag));
10710 __ b(gt, &runtime); // External strings go to runtime.
10711 __ b(lt, &seq_string); // Sequential strings are handled directly.
10712
10713 // Cons string. Try to recurse (once) on the first substring.
10714 // (This adds a little more generality than necessary to handle flattened
10715 // cons strings, but not much).
10716 __ ldr(r5, FieldMemOperand(r5, ConsString::kFirstOffset));
10717 __ ldr(r4, FieldMemOperand(r5, HeapObject::kMapOffset));
10718 __ ldrb(r1, FieldMemOperand(r4, Map::kInstanceTypeOffset));
10719 __ tst(r1, Operand(kStringRepresentationMask));
10720 ASSERT_EQ(0, kSeqStringTag);
10721 __ b(ne, &runtime); // Cons and External strings go to runtime.
10722
10723 // Definitly a sequential string.
10724 __ bind(&seq_string);
10725
10726 // r1: instance type.
10727 // r2: length
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010728 // r3: from index (untaged smi)
ager@chromium.org5c838252010-02-19 08:53:10 +000010729 // r5: string
10730 // r6: from (smi)
10731 // r7: to (smi)
10732 __ ldr(r4, FieldMemOperand(r5, String::kLengthOffset));
ager@chromium.orgac091b72010-05-05 07:34:42 +000010733 __ cmp(r4, Operand(r7));
ager@chromium.org5c838252010-02-19 08:53:10 +000010734 __ b(lt, &runtime); // Fail if to > length.
10735
10736 // r1: instance type.
10737 // r2: result string length.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010738 // r3: from index (untaged smi)
ager@chromium.org5c838252010-02-19 08:53:10 +000010739 // r5: string.
10740 // r6: from offset (smi)
10741 // Check for flat ascii string.
10742 Label non_ascii_flat;
10743 __ tst(r1, Operand(kStringEncodingMask));
10744 ASSERT_EQ(0, kTwoByteStringTag);
10745 __ b(eq, &non_ascii_flat);
10746
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010747 Label result_longer_than_two;
10748 __ cmp(r2, Operand(2));
10749 __ b(gt, &result_longer_than_two);
10750
10751 // Sub string of length 2 requested.
10752 // Get the two characters forming the sub string.
10753 __ add(r5, r5, Operand(r3));
10754 __ ldrb(r3, FieldMemOperand(r5, SeqAsciiString::kHeaderSize));
10755 __ ldrb(r4, FieldMemOperand(r5, SeqAsciiString::kHeaderSize + 1));
10756
10757 // Try to lookup two character string in symbol table.
10758 Label make_two_character_string;
ager@chromium.orgac091b72010-05-05 07:34:42 +000010759 StringHelper::GenerateTwoCharacterSymbolTableProbe(
10760 masm, r3, r4, r1, r5, r6, r7, r9, &make_two_character_string);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010761 __ IncrementCounter(&Counters::sub_string_native, 1, r3, r4);
10762 __ add(sp, sp, Operand(3 * kPointerSize));
10763 __ Ret();
10764
10765 // r2: result string length.
10766 // r3: two characters combined into halfword in little endian byte order.
10767 __ bind(&make_two_character_string);
10768 __ AllocateAsciiString(r0, r2, r4, r5, r9, &runtime);
10769 __ strh(r3, FieldMemOperand(r0, SeqAsciiString::kHeaderSize));
10770 __ IncrementCounter(&Counters::sub_string_native, 1, r3, r4);
10771 __ add(sp, sp, Operand(3 * kPointerSize));
10772 __ Ret();
10773
10774 __ bind(&result_longer_than_two);
10775
ager@chromium.org5c838252010-02-19 08:53:10 +000010776 // Allocate the result.
10777 __ AllocateAsciiString(r0, r2, r3, r4, r1, &runtime);
10778
10779 // r0: result string.
10780 // r2: result string length.
10781 // r5: string.
10782 // r6: from offset (smi)
10783 // Locate first character of result.
10784 __ add(r1, r0, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
10785 // Locate 'from' character of string.
10786 __ add(r5, r5, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
10787 __ add(r5, r5, Operand(r6, ASR, 1));
10788
10789 // r0: result string.
10790 // r1: first character of result string.
10791 // r2: result string length.
10792 // r5: first character of sub string to copy.
10793 ASSERT_EQ(0, SeqAsciiString::kHeaderSize & kObjectAlignmentMask);
ager@chromium.orgac091b72010-05-05 07:34:42 +000010794 StringHelper::GenerateCopyCharactersLong(masm, r1, r5, r2, r3, r4, r6, r7, r9,
10795 COPY_ASCII | DEST_ALWAYS_ALIGNED);
ager@chromium.org5c838252010-02-19 08:53:10 +000010796 __ IncrementCounter(&Counters::sub_string_native, 1, r3, r4);
10797 __ add(sp, sp, Operand(3 * kPointerSize));
10798 __ Ret();
10799
10800 __ bind(&non_ascii_flat);
10801 // r2: result string length.
10802 // r5: string.
10803 // r6: from offset (smi)
10804 // Check for flat two byte string.
10805
10806 // Allocate the result.
10807 __ AllocateTwoByteString(r0, r2, r1, r3, r4, &runtime);
10808
10809 // r0: result string.
10810 // r2: result string length.
10811 // r5: string.
10812 // Locate first character of result.
10813 __ add(r1, r0, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
10814 // Locate 'from' character of string.
10815 __ add(r5, r5, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
10816 // As "from" is a smi it is 2 times the value which matches the size of a two
10817 // byte character.
10818 __ add(r5, r5, Operand(r6));
10819
10820 // r0: result string.
10821 // r1: first character of result.
10822 // r2: result length.
10823 // r5: first character of string to copy.
10824 ASSERT_EQ(0, SeqTwoByteString::kHeaderSize & kObjectAlignmentMask);
ager@chromium.orgac091b72010-05-05 07:34:42 +000010825 StringHelper::GenerateCopyCharactersLong(masm, r1, r5, r2, r3, r4, r6, r7, r9,
10826 DEST_ALWAYS_ALIGNED);
ager@chromium.org5c838252010-02-19 08:53:10 +000010827 __ IncrementCounter(&Counters::sub_string_native, 1, r3, r4);
10828 __ add(sp, sp, Operand(3 * kPointerSize));
10829 __ Ret();
10830
10831 // Just jump to runtime to create the sub string.
10832 __ bind(&runtime);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010833 __ TailCallRuntime(Runtime::kSubString, 3, 1);
ager@chromium.org5c838252010-02-19 08:53:10 +000010834}
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010835
10836
10837void StringCompareStub::GenerateCompareFlatAsciiStrings(MacroAssembler* masm,
10838 Register left,
10839 Register right,
10840 Register scratch1,
10841 Register scratch2,
10842 Register scratch3,
10843 Register scratch4) {
10844 Label compare_lengths;
10845 // Find minimum length and length difference.
10846 __ ldr(scratch1, FieldMemOperand(left, String::kLengthOffset));
10847 __ ldr(scratch2, FieldMemOperand(right, String::kLengthOffset));
10848 __ sub(scratch3, scratch1, Operand(scratch2), SetCC);
10849 Register length_delta = scratch3;
10850 __ mov(scratch1, scratch2, LeaveCC, gt);
10851 Register min_length = scratch1;
ager@chromium.orgac091b72010-05-05 07:34:42 +000010852 ASSERT(kSmiTag == 0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010853 __ tst(min_length, Operand(min_length));
10854 __ b(eq, &compare_lengths);
10855
ager@chromium.orgac091b72010-05-05 07:34:42 +000010856 // Untag smi.
10857 __ mov(min_length, Operand(min_length, ASR, kSmiTagSize));
10858
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010859 // Setup registers so that we only need to increment one register
10860 // in the loop.
10861 __ add(scratch2, min_length,
10862 Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
10863 __ add(left, left, Operand(scratch2));
10864 __ add(right, right, Operand(scratch2));
10865 // Registers left and right points to the min_length character of strings.
10866 __ rsb(min_length, min_length, Operand(-1));
10867 Register index = min_length;
10868 // Index starts at -min_length.
10869
10870 {
10871 // Compare loop.
10872 Label loop;
10873 __ bind(&loop);
10874 // Compare characters.
10875 __ add(index, index, Operand(1), SetCC);
10876 __ ldrb(scratch2, MemOperand(left, index), ne);
10877 __ ldrb(scratch4, MemOperand(right, index), ne);
10878 // Skip to compare lengths with eq condition true.
10879 __ b(eq, &compare_lengths);
10880 __ cmp(scratch2, scratch4);
10881 __ b(eq, &loop);
10882 // Fallthrough with eq condition false.
10883 }
10884 // Compare lengths - strings up to min-length are equal.
10885 __ bind(&compare_lengths);
10886 ASSERT(Smi::FromInt(EQUAL) == static_cast<Smi*>(0));
10887 // Use zero length_delta as result.
10888 __ mov(r0, Operand(length_delta), SetCC, eq);
10889 // Fall through to here if characters compare not-equal.
10890 __ mov(r0, Operand(Smi::FromInt(GREATER)), LeaveCC, gt);
10891 __ mov(r0, Operand(Smi::FromInt(LESS)), LeaveCC, lt);
10892 __ Ret();
10893}
10894
10895
10896void StringCompareStub::Generate(MacroAssembler* masm) {
10897 Label runtime;
10898
10899 // Stack frame on entry.
ager@chromium.org5c838252010-02-19 08:53:10 +000010900 // sp[0]: right string
10901 // sp[4]: left string
10902 __ ldr(r0, MemOperand(sp, 1 * kPointerSize)); // left
10903 __ ldr(r1, MemOperand(sp, 0 * kPointerSize)); // right
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010904
10905 Label not_same;
10906 __ cmp(r0, r1);
10907 __ b(ne, &not_same);
10908 ASSERT_EQ(0, EQUAL);
10909 ASSERT_EQ(0, kSmiTag);
10910 __ mov(r0, Operand(Smi::FromInt(EQUAL)));
10911 __ IncrementCounter(&Counters::string_compare_native, 1, r1, r2);
10912 __ add(sp, sp, Operand(2 * kPointerSize));
10913 __ Ret();
10914
10915 __ bind(&not_same);
10916
10917 // Check that both objects are sequential ascii strings.
10918 __ JumpIfNotBothSequentialAsciiStrings(r0, r1, r2, r3, &runtime);
10919
10920 // Compare flat ascii strings natively. Remove arguments from stack first.
10921 __ IncrementCounter(&Counters::string_compare_native, 1, r2, r3);
10922 __ add(sp, sp, Operand(2 * kPointerSize));
10923 GenerateCompareFlatAsciiStrings(masm, r0, r1, r2, r3, r4, r5);
10924
10925 // Call the runtime; it returns -1 (less), 0 (equal), or 1 (greater)
10926 // tagged as a small integer.
10927 __ bind(&runtime);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010928 __ TailCallRuntime(Runtime::kStringCompare, 2, 1);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010929}
10930
10931
ager@chromium.org5c838252010-02-19 08:53:10 +000010932void StringAddStub::Generate(MacroAssembler* masm) {
10933 Label string_add_runtime;
10934 // Stack on entry:
10935 // sp[0]: second argument.
10936 // sp[4]: first argument.
10937
10938 // Load the two arguments.
10939 __ ldr(r0, MemOperand(sp, 1 * kPointerSize)); // First argument.
10940 __ ldr(r1, MemOperand(sp, 0 * kPointerSize)); // Second argument.
10941
10942 // Make sure that both arguments are strings if not known in advance.
10943 if (string_check_) {
10944 ASSERT_EQ(0, kSmiTag);
10945 __ JumpIfEitherSmi(r0, r1, &string_add_runtime);
10946 // Load instance types.
10947 __ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset));
10948 __ ldr(r5, FieldMemOperand(r1, HeapObject::kMapOffset));
10949 __ ldrb(r4, FieldMemOperand(r4, Map::kInstanceTypeOffset));
10950 __ ldrb(r5, FieldMemOperand(r5, Map::kInstanceTypeOffset));
10951 ASSERT_EQ(0, kStringTag);
10952 // If either is not a string, go to runtime.
10953 __ tst(r4, Operand(kIsNotStringMask));
10954 __ tst(r5, Operand(kIsNotStringMask), eq);
10955 __ b(ne, &string_add_runtime);
10956 }
10957
10958 // Both arguments are strings.
10959 // r0: first string
10960 // r1: second string
10961 // r4: first string instance type (if string_check_)
10962 // r5: second string instance type (if string_check_)
10963 {
10964 Label strings_not_empty;
10965 // Check if either of the strings are empty. In that case return the other.
10966 __ ldr(r2, FieldMemOperand(r0, String::kLengthOffset));
10967 __ ldr(r3, FieldMemOperand(r1, String::kLengthOffset));
ager@chromium.orgac091b72010-05-05 07:34:42 +000010968 ASSERT(kSmiTag == 0);
10969 __ cmp(r2, Operand(Smi::FromInt(0))); // Test if first string is empty.
ager@chromium.org5c838252010-02-19 08:53:10 +000010970 __ mov(r0, Operand(r1), LeaveCC, eq); // If first is empty, return second.
ager@chromium.orgac091b72010-05-05 07:34:42 +000010971 ASSERT(kSmiTag == 0);
10972 // Else test if second string is empty.
10973 __ cmp(r3, Operand(Smi::FromInt(0)), ne);
ager@chromium.org5c838252010-02-19 08:53:10 +000010974 __ b(ne, &strings_not_empty); // If either string was empty, return r0.
10975
10976 __ IncrementCounter(&Counters::string_add_native, 1, r2, r3);
10977 __ add(sp, sp, Operand(2 * kPointerSize));
10978 __ Ret();
10979
10980 __ bind(&strings_not_empty);
10981 }
10982
ager@chromium.orgac091b72010-05-05 07:34:42 +000010983 __ mov(r2, Operand(r2, ASR, kSmiTagSize));
10984 __ mov(r3, Operand(r3, ASR, kSmiTagSize));
ager@chromium.org5c838252010-02-19 08:53:10 +000010985 // Both strings are non-empty.
10986 // r0: first string
10987 // r1: second string
10988 // r2: length of first string
10989 // r3: length of second string
10990 // r4: first string instance type (if string_check_)
10991 // r5: second string instance type (if string_check_)
10992 // Look at the length of the result of adding the two strings.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010993 Label string_add_flat_result, longer_than_two;
ager@chromium.org5c838252010-02-19 08:53:10 +000010994 // Adding two lengths can't overflow.
10995 ASSERT(String::kMaxLength * 2 > String::kMaxLength);
10996 __ add(r6, r2, Operand(r3));
10997 // Use the runtime system when adding two one character strings, as it
10998 // contains optimizations for this specific case using the symbol table.
10999 __ cmp(r6, Operand(2));
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000011000 __ b(ne, &longer_than_two);
11001
11002 // Check that both strings are non-external ascii strings.
11003 if (!string_check_) {
11004 __ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset));
11005 __ ldr(r5, FieldMemOperand(r1, HeapObject::kMapOffset));
11006 __ ldrb(r4, FieldMemOperand(r4, Map::kInstanceTypeOffset));
11007 __ ldrb(r5, FieldMemOperand(r5, Map::kInstanceTypeOffset));
11008 }
11009 __ JumpIfBothInstanceTypesAreNotSequentialAscii(r4, r5, r6, r7,
11010 &string_add_runtime);
11011
11012 // Get the two characters forming the sub string.
11013 __ ldrb(r2, FieldMemOperand(r0, SeqAsciiString::kHeaderSize));
11014 __ ldrb(r3, FieldMemOperand(r1, SeqAsciiString::kHeaderSize));
11015
11016 // Try to lookup two character string in symbol table. If it is not found
11017 // just allocate a new one.
11018 Label make_two_character_string;
ager@chromium.orgac091b72010-05-05 07:34:42 +000011019 StringHelper::GenerateTwoCharacterSymbolTableProbe(
11020 masm, r2, r3, r6, r7, r4, r5, r9, &make_two_character_string);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000011021 __ IncrementCounter(&Counters::string_add_native, 1, r2, r3);
11022 __ add(sp, sp, Operand(2 * kPointerSize));
11023 __ Ret();
11024
11025 __ bind(&make_two_character_string);
11026 // Resulting string has length 2 and first chars of two strings
11027 // are combined into single halfword in r2 register.
11028 // So we can fill resulting string without two loops by a single
11029 // halfword store instruction (which assumes that processor is
11030 // in a little endian mode)
11031 __ mov(r6, Operand(2));
11032 __ AllocateAsciiString(r0, r6, r4, r5, r9, &string_add_runtime);
11033 __ strh(r2, FieldMemOperand(r0, SeqAsciiString::kHeaderSize));
11034 __ IncrementCounter(&Counters::string_add_native, 1, r2, r3);
11035 __ add(sp, sp, Operand(2 * kPointerSize));
11036 __ Ret();
11037
11038 __ bind(&longer_than_two);
ager@chromium.org5c838252010-02-19 08:53:10 +000011039 // Check if resulting string will be flat.
11040 __ cmp(r6, Operand(String::kMinNonFlatLength));
11041 __ b(lt, &string_add_flat_result);
11042 // Handle exceptionally long strings in the runtime system.
11043 ASSERT((String::kMaxLength & 0x80000000) == 0);
11044 ASSERT(IsPowerOf2(String::kMaxLength + 1));
11045 // kMaxLength + 1 is representable as shifted literal, kMaxLength is not.
11046 __ cmp(r6, Operand(String::kMaxLength + 1));
11047 __ b(hs, &string_add_runtime);
11048
11049 // If result is not supposed to be flat, allocate a cons string object.
11050 // If both strings are ascii the result is an ascii cons string.
11051 if (!string_check_) {
11052 __ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset));
11053 __ ldr(r5, FieldMemOperand(r1, HeapObject::kMapOffset));
11054 __ ldrb(r4, FieldMemOperand(r4, Map::kInstanceTypeOffset));
11055 __ ldrb(r5, FieldMemOperand(r5, Map::kInstanceTypeOffset));
11056 }
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +000011057 Label non_ascii, allocated, ascii_data;
ager@chromium.org5c838252010-02-19 08:53:10 +000011058 ASSERT_EQ(0, kTwoByteStringTag);
11059 __ tst(r4, Operand(kStringEncodingMask));
11060 __ tst(r5, Operand(kStringEncodingMask), ne);
11061 __ b(eq, &non_ascii);
11062
11063 // Allocate an ASCII cons string.
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +000011064 __ bind(&ascii_data);
ager@chromium.org5c838252010-02-19 08:53:10 +000011065 __ AllocateAsciiConsString(r7, r6, r4, r5, &string_add_runtime);
11066 __ bind(&allocated);
11067 // Fill the fields of the cons string.
11068 __ str(r0, FieldMemOperand(r7, ConsString::kFirstOffset));
11069 __ str(r1, FieldMemOperand(r7, ConsString::kSecondOffset));
11070 __ mov(r0, Operand(r7));
11071 __ IncrementCounter(&Counters::string_add_native, 1, r2, r3);
11072 __ add(sp, sp, Operand(2 * kPointerSize));
11073 __ Ret();
11074
11075 __ bind(&non_ascii);
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +000011076 // At least one of the strings is two-byte. Check whether it happens
11077 // to contain only ascii characters.
11078 // r4: first instance type.
11079 // r5: second instance type.
11080 __ tst(r4, Operand(kAsciiDataHintMask));
11081 __ tst(r5, Operand(kAsciiDataHintMask), ne);
11082 __ b(ne, &ascii_data);
11083 __ eor(r4, r4, Operand(r5));
11084 ASSERT(kAsciiStringTag != 0 && kAsciiDataHintTag != 0);
11085 __ and_(r4, r4, Operand(kAsciiStringTag | kAsciiDataHintTag));
11086 __ cmp(r4, Operand(kAsciiStringTag | kAsciiDataHintTag));
11087 __ b(eq, &ascii_data);
11088
ager@chromium.org5c838252010-02-19 08:53:10 +000011089 // Allocate a two byte cons string.
11090 __ AllocateTwoByteConsString(r7, r6, r4, r5, &string_add_runtime);
11091 __ jmp(&allocated);
11092
11093 // Handle creating a flat result. First check that both strings are
11094 // sequential and that they have the same encoding.
11095 // r0: first string
11096 // r1: second string
11097 // r2: length of first string
11098 // r3: length of second string
11099 // r4: first string instance type (if string_check_)
11100 // r5: second string instance type (if string_check_)
11101 // r6: sum of lengths.
11102 __ bind(&string_add_flat_result);
11103 if (!string_check_) {
11104 __ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset));
11105 __ ldr(r5, FieldMemOperand(r1, HeapObject::kMapOffset));
11106 __ ldrb(r4, FieldMemOperand(r4, Map::kInstanceTypeOffset));
11107 __ ldrb(r5, FieldMemOperand(r5, Map::kInstanceTypeOffset));
11108 }
11109 // Check that both strings are sequential.
11110 ASSERT_EQ(0, kSeqStringTag);
11111 __ tst(r4, Operand(kStringRepresentationMask));
11112 __ tst(r5, Operand(kStringRepresentationMask), eq);
11113 __ b(ne, &string_add_runtime);
11114 // Now check if both strings have the same encoding (ASCII/Two-byte).
11115 // r0: first string.
11116 // r1: second string.
11117 // r2: length of first string.
11118 // r3: length of second string.
11119 // r6: sum of lengths..
11120 Label non_ascii_string_add_flat_result;
11121 ASSERT(IsPowerOf2(kStringEncodingMask)); // Just one bit to test.
11122 __ eor(r7, r4, Operand(r5));
11123 __ tst(r7, Operand(kStringEncodingMask));
11124 __ b(ne, &string_add_runtime);
11125 // And see if it's ASCII or two-byte.
11126 __ tst(r4, Operand(kStringEncodingMask));
11127 __ b(eq, &non_ascii_string_add_flat_result);
11128
11129 // Both strings are sequential ASCII strings. We also know that they are
11130 // short (since the sum of the lengths is less than kMinNonFlatLength).
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000011131 // r6: length of resulting flat string
ager@chromium.org5c838252010-02-19 08:53:10 +000011132 __ AllocateAsciiString(r7, r6, r4, r5, r9, &string_add_runtime);
11133 // Locate first character of result.
11134 __ add(r6, r7, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
11135 // Locate first character of first argument.
11136 __ add(r0, r0, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
11137 // r0: first character of first string.
11138 // r1: second string.
11139 // r2: length of first string.
11140 // r3: length of second string.
11141 // r6: first character of result.
11142 // r7: result string.
ager@chromium.orgac091b72010-05-05 07:34:42 +000011143 StringHelper::GenerateCopyCharacters(masm, r6, r0, r2, r4, true);
ager@chromium.org5c838252010-02-19 08:53:10 +000011144
11145 // Load second argument and locate first character.
11146 __ add(r1, r1, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
11147 // r1: first character of second string.
11148 // r3: length of second string.
11149 // r6: next character of result.
11150 // r7: result string.
ager@chromium.orgac091b72010-05-05 07:34:42 +000011151 StringHelper::GenerateCopyCharacters(masm, r6, r1, r3, r4, true);
ager@chromium.org5c838252010-02-19 08:53:10 +000011152 __ mov(r0, Operand(r7));
11153 __ IncrementCounter(&Counters::string_add_native, 1, r2, r3);
11154 __ add(sp, sp, Operand(2 * kPointerSize));
11155 __ Ret();
11156
11157 __ bind(&non_ascii_string_add_flat_result);
11158 // Both strings are sequential two byte strings.
11159 // r0: first string.
11160 // r1: second string.
11161 // r2: length of first string.
11162 // r3: length of second string.
11163 // r6: sum of length of strings.
11164 __ AllocateTwoByteString(r7, r6, r4, r5, r9, &string_add_runtime);
11165 // r0: first string.
11166 // r1: second string.
11167 // r2: length of first string.
11168 // r3: length of second string.
11169 // r7: result string.
11170
11171 // Locate first character of result.
11172 __ add(r6, r7, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
11173 // Locate first character of first argument.
11174 __ add(r0, r0, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
11175
11176 // r0: first character of first string.
11177 // r1: second string.
11178 // r2: length of first string.
11179 // r3: length of second string.
11180 // r6: first character of result.
11181 // r7: result string.
ager@chromium.orgac091b72010-05-05 07:34:42 +000011182 StringHelper::GenerateCopyCharacters(masm, r6, r0, r2, r4, false);
ager@chromium.org5c838252010-02-19 08:53:10 +000011183
11184 // Locate first character of second argument.
11185 __ add(r1, r1, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
11186
11187 // r1: first character of second string.
11188 // r3: length of second string.
11189 // r6: next character of result (after copy of first string).
11190 // r7: result string.
ager@chromium.orgac091b72010-05-05 07:34:42 +000011191 StringHelper::GenerateCopyCharacters(masm, r6, r1, r3, r4, false);
ager@chromium.org5c838252010-02-19 08:53:10 +000011192
11193 __ mov(r0, Operand(r7));
11194 __ IncrementCounter(&Counters::string_add_native, 1, r2, r3);
11195 __ add(sp, sp, Operand(2 * kPointerSize));
11196 __ Ret();
11197
11198 // Just jump to runtime to add the two strings.
11199 __ bind(&string_add_runtime);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000011200 __ TailCallRuntime(Runtime::kStringAdd, 2, 1);
ager@chromium.org5c838252010-02-19 08:53:10 +000011201}
11202
11203
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000011204#undef __
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011205
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011206} } // namespace v8::internal
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +000011207
11208#endif // V8_TARGET_ARCH_ARM