blob: fa6efcd3c141382014c9aa117004710ff2df7fcd [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.
4346 __ mov(scratch2, Operand(0x3ff00000));
4347 __ mov(scratch1, Operand(0));
4348 __ vmov(d0, scratch1, scratch2);
4349
4350 // Get the absolute untagged value of the exponent and use that for the
4351 // calculation.
4352 __ mov(scratch1, Operand(exponent, ASR, kSmiTagSize), SetCC);
4353 __ rsb(scratch1, scratch1, Operand(0), LeaveCC, mi); // Negate if negative.
4354 __ vmov(d2, d0, mi); // 1.0 needed in d2 later if exponent is negative.
4355
4356 // Run through all the bits in the exponent. The result is calculated in d0
4357 // and d1 holds base^(bit^2).
4358 Label more_bits;
4359 __ bind(&more_bits);
4360 __ mov(scratch1, Operand(scratch1, LSR, 1), SetCC);
4361 __ vmul(d0, d0, d1, cs); // Multiply with base^(bit^2) if bit is set.
4362 __ vmul(d1, d1, d1, ne); // Don't bother calculating next d1 if done.
4363 __ b(ne, &more_bits);
4364
4365 // If exponent is positive we are done.
4366 __ cmp(exponent, Operand(0));
4367 __ b(ge, &allocate_return);
4368
4369 // If exponent is negative result is 1/result (d2 already holds 1.0 in that
4370 // case). However if d0 has reached infinity this will not provide the
4371 // correct result, so call runtime if that is the case.
4372 __ mov(scratch2, Operand(0x7FF00000));
4373 __ mov(scratch1, Operand(0));
4374 __ vmov(d1, scratch1, scratch2); // Load infinity into d1.
4375 __ vcmp(d0, d1);
4376 __ vmrs(pc);
4377 runtime.Branch(eq); // d0 reached infinity.
4378 __ vdiv(d0, d2, d0);
4379 __ b(&allocate_return);
4380
4381 __ bind(&exponent_nonsmi);
lrn@chromium.org32d961d2010-06-30 09:09:34 +00004382 // Special handling of raising to the power of -0.5 and 0.5. First check
4383 // that the value is a heap number and that the lower bits (which for both
4384 // values are zero).
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00004385 heap_number_map = r6;
lrn@chromium.org32d961d2010-06-30 09:09:34 +00004386 __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
4387 __ ldr(scratch1, FieldMemOperand(exponent, HeapObject::kMapOffset));
4388 __ ldr(scratch2, FieldMemOperand(exponent, HeapNumber::kMantissaOffset));
4389 __ cmp(scratch1, heap_number_map);
4390 runtime.Branch(ne);
4391 __ tst(scratch2, scratch2);
4392 runtime.Branch(ne);
4393
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00004394 // Load the higher bits (which contains the floating point exponent).
lrn@chromium.org32d961d2010-06-30 09:09:34 +00004395 __ ldr(scratch1, FieldMemOperand(exponent, HeapNumber::kExponentOffset));
4396
4397 // Compare exponent with -0.5.
4398 __ cmp(scratch1, Operand(0xbfe00000));
4399 __ b(ne, &not_minus_half);
4400
4401 // Get the double value from the base into vfp register d0.
4402 __ ObjectToDoubleVFPRegister(base, d0,
4403 scratch1, scratch2, heap_number_map, s0,
4404 runtime.entry_label(),
4405 AVOID_NANS_AND_INFINITIES);
4406
4407 // Load 1.0 into d2.
4408 __ mov(scratch2, Operand(0x3ff00000));
4409 __ mov(scratch1, Operand(0));
4410 __ vmov(d2, scratch1, scratch2);
4411
4412 // Calculate the reciprocal of the square root. 1/sqrt(x) = sqrt(1/x).
4413 __ vdiv(d0, d2, d0);
4414 __ vsqrt(d0, d0);
4415
4416 __ b(&allocate_return);
4417
4418 __ bind(&not_minus_half);
4419 // Compare exponent with 0.5.
4420 __ cmp(scratch1, Operand(0x3fe00000));
4421 runtime.Branch(ne);
4422
4423 // Get the double value from the base into vfp register d0.
4424 __ ObjectToDoubleVFPRegister(base, d0,
4425 scratch1, scratch2, heap_number_map, s0,
4426 runtime.entry_label(),
4427 AVOID_NANS_AND_INFINITIES);
4428 __ vsqrt(d0, d0);
4429
4430 __ bind(&allocate_return);
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00004431 Register scratch3 = r5;
4432 __ AllocateHeapNumberWithValue(scratch3, d0, scratch1, scratch2,
4433 heap_number_map, runtime.entry_label());
4434 __ mov(base, scratch3);
lrn@chromium.org32d961d2010-06-30 09:09:34 +00004435 done.Jump();
4436
4437 runtime.Bind();
4438
4439 // Push back the arguments again for the runtime call.
4440 frame_->EmitPush(base);
4441 frame_->EmitPush(exponent);
4442 frame_->CallRuntime(Runtime::kMath_pow, 2);
4443 __ Move(base, r0);
4444
4445 done.Bind();
4446 frame_->EmitPush(base);
4447 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004448}
4449
4450
lrn@chromium.org32d961d2010-06-30 09:09:34 +00004451// Generates the Math.sqrt method.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004452void CodeGenerator::GenerateMathSqrt(ZoneList<Expression*>* args) {
4453 ASSERT(args->length() == 1);
4454 Load(args->at(0));
lrn@chromium.org32d961d2010-06-30 09:09:34 +00004455
4456 if (!CpuFeatures::IsSupported(VFP3)) {
4457 frame_->CallRuntime(Runtime::kMath_sqrt, 1);
4458 frame_->EmitPush(r0);
4459 } else {
4460 CpuFeatures::Scope scope(VFP3);
4461 JumpTarget runtime, done;
4462
4463 Register scratch1 = VirtualFrame::scratch0();
4464 Register scratch2 = VirtualFrame::scratch1();
4465
4466 // Get the value from the frame.
4467 Register tos = frame_->PopToRegister();
4468
4469 // Set the frame for the runtime jump target. The code below jumps to the
4470 // jump target label so the frame needs to be established before that.
4471 ASSERT(runtime.entry_frame() == NULL);
4472 runtime.set_entry_frame(frame_);
4473
4474 Register heap_number_map = r6;
4475 __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
4476
4477 // Get the double value from the heap number into vfp register d0.
4478 __ ObjectToDoubleVFPRegister(tos, d0,
4479 scratch1, scratch2, heap_number_map, s0,
4480 runtime.entry_label());
4481
4482 // Calculate the square root of d0 and place result in a heap number object.
4483 __ vsqrt(d0, d0);
4484 __ AllocateHeapNumberWithValue(
4485 tos, d0, scratch1, scratch2, heap_number_map, runtime.entry_label());
4486 done.Jump();
4487
4488 runtime.Bind();
4489 // Push back the argument again for the runtime call.
4490 frame_->EmitPush(tos);
4491 frame_->CallRuntime(Runtime::kMath_sqrt, 1);
4492 __ Move(tos, r0);
4493
4494 done.Bind();
4495 frame_->EmitPush(tos);
4496 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004497}
4498
4499
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004500class DeferredStringCharCodeAt : public DeferredCode {
4501 public:
4502 DeferredStringCharCodeAt(Register object,
4503 Register index,
4504 Register scratch,
4505 Register result)
4506 : result_(result),
4507 char_code_at_generator_(object,
4508 index,
4509 scratch,
4510 result,
4511 &need_conversion_,
4512 &need_conversion_,
4513 &index_out_of_range_,
4514 STRING_INDEX_IS_NUMBER) {}
4515
4516 StringCharCodeAtGenerator* fast_case_generator() {
4517 return &char_code_at_generator_;
4518 }
4519
4520 virtual void Generate() {
4521 VirtualFrameRuntimeCallHelper call_helper(frame_state());
4522 char_code_at_generator_.GenerateSlow(masm(), call_helper);
4523
4524 __ bind(&need_conversion_);
4525 // Move the undefined value into the result register, which will
4526 // trigger conversion.
4527 __ LoadRoot(result_, Heap::kUndefinedValueRootIndex);
4528 __ jmp(exit_label());
4529
4530 __ bind(&index_out_of_range_);
4531 // When the index is out of range, the spec requires us to return
4532 // NaN.
4533 __ LoadRoot(result_, Heap::kNanValueRootIndex);
4534 __ jmp(exit_label());
4535 }
4536
4537 private:
4538 Register result_;
4539
4540 Label need_conversion_;
4541 Label index_out_of_range_;
4542
4543 StringCharCodeAtGenerator char_code_at_generator_;
4544};
4545
4546
4547// This generates code that performs a String.prototype.charCodeAt() call
4548// or returns a smi in order to trigger conversion.
4549void CodeGenerator::GenerateStringCharCodeAt(ZoneList<Expression*>* args) {
4550 VirtualFrame::SpilledScope spilled_scope(frame_);
4551 Comment(masm_, "[ GenerateStringCharCodeAt");
kasper.lund7276f142008-07-30 08:49:36 +00004552 ASSERT(args->length() == 2);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004553
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004554 Load(args->at(0));
4555 Load(args->at(1));
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004556
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004557 Register index = r1;
4558 Register object = r2;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004559
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004560 frame_->EmitPop(r1);
4561 frame_->EmitPop(r2);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004562
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004563 // We need two extra registers.
4564 Register scratch = r3;
4565 Register result = r0;
4566
4567 DeferredStringCharCodeAt* deferred =
4568 new DeferredStringCharCodeAt(object,
4569 index,
4570 scratch,
4571 result);
4572 deferred->fast_case_generator()->GenerateFast(masm_);
4573 deferred->BindExit();
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004574 frame_->EmitPush(result);
kasper.lund7276f142008-07-30 08:49:36 +00004575}
4576
4577
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004578class DeferredStringCharFromCode : public DeferredCode {
4579 public:
4580 DeferredStringCharFromCode(Register code,
4581 Register result)
4582 : char_from_code_generator_(code, result) {}
4583
4584 StringCharFromCodeGenerator* fast_case_generator() {
4585 return &char_from_code_generator_;
4586 }
4587
4588 virtual void Generate() {
4589 VirtualFrameRuntimeCallHelper call_helper(frame_state());
4590 char_from_code_generator_.GenerateSlow(masm(), call_helper);
4591 }
4592
4593 private:
4594 StringCharFromCodeGenerator char_from_code_generator_;
4595};
4596
4597
4598// Generates code for creating a one-char string from a char code.
4599void CodeGenerator::GenerateStringCharFromCode(ZoneList<Expression*>* args) {
4600 VirtualFrame::SpilledScope spilled_scope(frame_);
4601 Comment(masm_, "[ GenerateStringCharFromCode");
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004602 ASSERT(args->length() == 1);
4603
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004604 Load(args->at(0));
4605
ager@chromium.orgac091b72010-05-05 07:34:42 +00004606 Register code = r1;
ager@chromium.orgac091b72010-05-05 07:34:42 +00004607 Register result = r0;
4608
ager@chromium.orgac091b72010-05-05 07:34:42 +00004609 frame_->EmitPop(code);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004610
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004611 DeferredStringCharFromCode* deferred = new DeferredStringCharFromCode(
4612 code, result);
4613 deferred->fast_case_generator()->GenerateFast(masm_);
4614 deferred->BindExit();
4615 frame_->EmitPush(result);
4616}
4617
4618
4619class DeferredStringCharAt : public DeferredCode {
4620 public:
4621 DeferredStringCharAt(Register object,
4622 Register index,
4623 Register scratch1,
4624 Register scratch2,
4625 Register result)
4626 : result_(result),
4627 char_at_generator_(object,
4628 index,
4629 scratch1,
4630 scratch2,
4631 result,
4632 &need_conversion_,
4633 &need_conversion_,
4634 &index_out_of_range_,
4635 STRING_INDEX_IS_NUMBER) {}
4636
4637 StringCharAtGenerator* fast_case_generator() {
4638 return &char_at_generator_;
4639 }
4640
4641 virtual void Generate() {
4642 VirtualFrameRuntimeCallHelper call_helper(frame_state());
4643 char_at_generator_.GenerateSlow(masm(), call_helper);
4644
4645 __ bind(&need_conversion_);
4646 // Move smi zero into the result register, which will trigger
4647 // conversion.
4648 __ mov(result_, Operand(Smi::FromInt(0)));
4649 __ jmp(exit_label());
4650
4651 __ bind(&index_out_of_range_);
4652 // When the index is out of range, the spec requires us to return
4653 // the empty string.
4654 __ LoadRoot(result_, Heap::kEmptyStringRootIndex);
4655 __ jmp(exit_label());
4656 }
4657
4658 private:
4659 Register result_;
4660
4661 Label need_conversion_;
4662 Label index_out_of_range_;
4663
4664 StringCharAtGenerator char_at_generator_;
4665};
4666
4667
4668// This generates code that performs a String.prototype.charAt() call
4669// or returns a smi in order to trigger conversion.
4670void CodeGenerator::GenerateStringCharAt(ZoneList<Expression*>* args) {
4671 VirtualFrame::SpilledScope spilled_scope(frame_);
4672 Comment(masm_, "[ GenerateStringCharAt");
4673 ASSERT(args->length() == 2);
4674
4675 Load(args->at(0));
4676 Load(args->at(1));
4677
4678 Register index = r1;
4679 Register object = r2;
4680
4681 frame_->EmitPop(r1);
4682 frame_->EmitPop(r2);
4683
4684 // We need three extra registers.
4685 Register scratch1 = r3;
4686 Register scratch2 = r4;
4687 Register result = r0;
4688
4689 DeferredStringCharAt* deferred =
4690 new DeferredStringCharAt(object,
4691 index,
4692 scratch1,
4693 scratch2,
4694 result);
4695 deferred->fast_case_generator()->GenerateFast(masm_);
4696 deferred->BindExit();
ager@chromium.orgac091b72010-05-05 07:34:42 +00004697 frame_->EmitPush(result);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004698}
4699
4700
ager@chromium.org7c537e22008-10-16 08:43:32 +00004701void CodeGenerator::GenerateIsArray(ZoneList<Expression*>* args) {
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00004702 ASSERT(args->length() == 1);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004703 Load(args->at(0));
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004704 JumpTarget answer;
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00004705 // We need the CC bits to come out as not_equal in the case where the
4706 // object is a smi. This can't be done with the usual test opcode so
4707 // we use XOR to get the right CC bits.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004708 Register possible_array = frame_->PopToRegister();
4709 Register scratch = VirtualFrame::scratch0();
4710 __ and_(scratch, possible_array, Operand(kSmiTagMask));
4711 __ eor(scratch, scratch, Operand(kSmiTagMask), SetCC);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004712 answer.Branch(ne);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00004713 // It is a heap object - get the map. Check if the object is a JS array.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004714 __ CompareObjectType(possible_array, scratch, scratch, JS_ARRAY_TYPE);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004715 answer.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004716 cc_reg_ = eq;
4717}
4718
4719
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00004720void CodeGenerator::GenerateIsRegExp(ZoneList<Expression*>* args) {
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00004721 ASSERT(args->length() == 1);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004722 Load(args->at(0));
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00004723 JumpTarget answer;
4724 // We need the CC bits to come out as not_equal in the case where the
4725 // object is a smi. This can't be done with the usual test opcode so
4726 // we use XOR to get the right CC bits.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004727 Register possible_regexp = frame_->PopToRegister();
4728 Register scratch = VirtualFrame::scratch0();
4729 __ and_(scratch, possible_regexp, Operand(kSmiTagMask));
4730 __ eor(scratch, scratch, Operand(kSmiTagMask), SetCC);
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00004731 answer.Branch(ne);
4732 // It is a heap object - get the map. Check if the object is a regexp.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004733 __ CompareObjectType(possible_regexp, scratch, scratch, JS_REGEXP_TYPE);
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00004734 answer.Bind();
4735 cc_reg_ = eq;
4736}
4737
4738
ager@chromium.org6141cbe2009-11-20 12:14:52 +00004739void CodeGenerator::GenerateIsObject(ZoneList<Expression*>* args) {
4740 // This generates a fast version of:
4741 // (typeof(arg) === 'object' || %_ClassOf(arg) == 'RegExp')
ager@chromium.org6141cbe2009-11-20 12:14:52 +00004742 ASSERT(args->length() == 1);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004743 Load(args->at(0));
4744 Register possible_object = frame_->PopToRegister();
4745 __ tst(possible_object, Operand(kSmiTagMask));
ager@chromium.org6141cbe2009-11-20 12:14:52 +00004746 false_target()->Branch(eq);
4747
4748 __ LoadRoot(ip, Heap::kNullValueRootIndex);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004749 __ cmp(possible_object, ip);
ager@chromium.org6141cbe2009-11-20 12:14:52 +00004750 true_target()->Branch(eq);
4751
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004752 Register map_reg = VirtualFrame::scratch0();
4753 __ ldr(map_reg, FieldMemOperand(possible_object, HeapObject::kMapOffset));
ager@chromium.org6141cbe2009-11-20 12:14:52 +00004754 // Undetectable objects behave like undefined when tested with typeof.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004755 __ ldrb(possible_object, FieldMemOperand(map_reg, Map::kBitFieldOffset));
4756 __ tst(possible_object, Operand(1 << Map::kIsUndetectable));
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004757 false_target()->Branch(ne);
ager@chromium.org6141cbe2009-11-20 12:14:52 +00004758
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004759 __ ldrb(possible_object, FieldMemOperand(map_reg, Map::kInstanceTypeOffset));
4760 __ cmp(possible_object, Operand(FIRST_JS_OBJECT_TYPE));
ager@chromium.org6141cbe2009-11-20 12:14:52 +00004761 false_target()->Branch(lt);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004762 __ cmp(possible_object, Operand(LAST_JS_OBJECT_TYPE));
ager@chromium.org6141cbe2009-11-20 12:14:52 +00004763 cc_reg_ = le;
4764}
4765
4766
4767void CodeGenerator::GenerateIsFunction(ZoneList<Expression*>* args) {
4768 // This generates a fast version of:
4769 // (%_ClassOf(arg) === 'Function')
ager@chromium.org6141cbe2009-11-20 12:14:52 +00004770 ASSERT(args->length() == 1);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004771 Load(args->at(0));
4772 Register possible_function = frame_->PopToRegister();
4773 __ tst(possible_function, Operand(kSmiTagMask));
ager@chromium.org6141cbe2009-11-20 12:14:52 +00004774 false_target()->Branch(eq);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004775 Register map_reg = VirtualFrame::scratch0();
4776 Register scratch = VirtualFrame::scratch1();
4777 __ CompareObjectType(possible_function, map_reg, scratch, JS_FUNCTION_TYPE);
ager@chromium.org6141cbe2009-11-20 12:14:52 +00004778 cc_reg_ = eq;
4779}
4780
4781
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004782void CodeGenerator::GenerateIsUndetectableObject(ZoneList<Expression*>* args) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004783 ASSERT(args->length() == 1);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004784 Load(args->at(0));
4785 Register possible_undetectable = frame_->PopToRegister();
4786 __ tst(possible_undetectable, Operand(kSmiTagMask));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004787 false_target()->Branch(eq);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004788 Register scratch = VirtualFrame::scratch0();
4789 __ ldr(scratch,
4790 FieldMemOperand(possible_undetectable, HeapObject::kMapOffset));
4791 __ ldrb(scratch, FieldMemOperand(scratch, Map::kBitFieldOffset));
4792 __ tst(scratch, Operand(1 << Map::kIsUndetectable));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004793 cc_reg_ = ne;
4794}
4795
4796
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004797void CodeGenerator::GenerateIsConstructCall(ZoneList<Expression*>* args) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004798 ASSERT(args->length() == 0);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00004799
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004800 Register scratch0 = VirtualFrame::scratch0();
4801 Register scratch1 = VirtualFrame::scratch1();
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00004802 // Get the frame pointer for the calling frame.
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004803 __ ldr(scratch0, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00004804
4805 // Skip the arguments adaptor frame if it exists.
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004806 __ ldr(scratch1,
4807 MemOperand(scratch0, StandardFrameConstants::kContextOffset));
4808 __ cmp(scratch1, Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
4809 __ ldr(scratch0,
4810 MemOperand(scratch0, StandardFrameConstants::kCallerFPOffset), eq);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00004811
4812 // Check the marker in the calling frame.
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004813 __ ldr(scratch1,
4814 MemOperand(scratch0, StandardFrameConstants::kMarkerOffset));
4815 __ cmp(scratch1, Operand(Smi::FromInt(StackFrame::CONSTRUCT)));
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00004816 cc_reg_ = eq;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004817}
4818
4819
ager@chromium.org7c537e22008-10-16 08:43:32 +00004820void CodeGenerator::GenerateArgumentsLength(ZoneList<Expression*>* args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004821 ASSERT(args->length() == 0);
4822
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004823 Register tos = frame_->GetTOSRegister();
4824 Register scratch0 = VirtualFrame::scratch0();
4825 Register scratch1 = VirtualFrame::scratch1();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004826
lrn@chromium.org25156de2010-04-06 13:10:27 +00004827 // Check if the calling frame is an arguments adaptor frame.
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004828 __ ldr(scratch0,
4829 MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
4830 __ ldr(scratch1,
4831 MemOperand(scratch0, StandardFrameConstants::kContextOffset));
4832 __ cmp(scratch1, Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
4833
4834 // Get the number of formal parameters.
4835 __ mov(tos, Operand(Smi::FromInt(scope()->num_parameters())), LeaveCC, ne);
lrn@chromium.org25156de2010-04-06 13:10:27 +00004836
4837 // Arguments adaptor case: Read the arguments length from the
4838 // adaptor frame.
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004839 __ ldr(tos,
4840 MemOperand(scratch0, ArgumentsAdaptorFrameConstants::kLengthOffset),
4841 eq);
lrn@chromium.org25156de2010-04-06 13:10:27 +00004842
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004843 frame_->EmitPush(tos);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004844}
4845
4846
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00004847void CodeGenerator::GenerateArguments(ZoneList<Expression*>* args) {
ager@chromium.org357bf652010-04-12 11:30:10 +00004848 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004849 ASSERT(args->length() == 1);
4850
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00004851 // Satisfy contract with ArgumentsAccessStub:
4852 // Load the key into r1 and the formal parameters count into r0.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004853 Load(args->at(0));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004854 frame_->EmitPop(r1);
ager@chromium.org5c838252010-02-19 08:53:10 +00004855 __ mov(r0, Operand(Smi::FromInt(scope()->num_parameters())));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004856
4857 // Call the shared stub to get to arguments[key].
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004858 ArgumentsAccessStub stub(ArgumentsAccessStub::READ_ELEMENT);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004859 frame_->CallStub(&stub, 0);
4860 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004861}
4862
4863
ager@chromium.org357bf652010-04-12 11:30:10 +00004864void CodeGenerator::GenerateRandomHeapNumber(
4865 ZoneList<Expression*>* args) {
4866 VirtualFrame::SpilledScope spilled_scope(frame_);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00004867 ASSERT(args->length() == 0);
ager@chromium.org357bf652010-04-12 11:30:10 +00004868
4869 Label slow_allocate_heapnumber;
4870 Label heapnumber_allocated;
4871
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00004872 __ LoadRoot(r6, Heap::kHeapNumberMapRootIndex);
4873 __ AllocateHeapNumber(r4, r1, r2, r6, &slow_allocate_heapnumber);
ager@chromium.org357bf652010-04-12 11:30:10 +00004874 __ jmp(&heapnumber_allocated);
4875
4876 __ bind(&slow_allocate_heapnumber);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00004877 // To allocate a heap number, and ensure that it is not a smi, we
4878 // call the runtime function FUnaryMinus on 0, returning the double
4879 // -0.0. A new, distinct heap number is returned each time.
ager@chromium.org357bf652010-04-12 11:30:10 +00004880 __ mov(r0, Operand(Smi::FromInt(0)));
4881 __ push(r0);
4882 __ CallRuntime(Runtime::kNumberUnaryMinus, 1);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00004883 __ mov(r4, Operand(r0));
ager@chromium.org357bf652010-04-12 11:30:10 +00004884
4885 __ bind(&heapnumber_allocated);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00004886
4887 // Convert 32 random bits in r0 to 0.(32 random bits) in a double
4888 // by computing:
4889 // ( 1.(20 0s)(32 random bits) x 2^20 ) - (1.0 x 2^20)).
4890 if (CpuFeatures::IsSupported(VFP3)) {
4891 __ PrepareCallCFunction(0, r1);
4892 __ CallCFunction(ExternalReference::random_uint32_function(), 0);
4893
4894 CpuFeatures::Scope scope(VFP3);
4895 // 0x41300000 is the top half of 1.0 x 2^20 as a double.
4896 // Create this constant using mov/orr to avoid PC relative load.
4897 __ mov(r1, Operand(0x41000000));
4898 __ orr(r1, r1, Operand(0x300000));
4899 // Move 0x41300000xxxxxxxx (x = random bits) to VFP.
4900 __ vmov(d7, r0, r1);
4901 // Move 0x4130000000000000 to VFP.
4902 __ mov(r0, Operand(0));
4903 __ vmov(d8, r0, r1);
4904 // Subtract and store the result in the heap number.
4905 __ vsub(d7, d7, d8);
4906 __ sub(r0, r4, Operand(kHeapObjectTag));
4907 __ vstr(d7, r0, HeapNumber::kValueOffset);
4908 frame_->EmitPush(r4);
4909 } else {
4910 __ mov(r0, Operand(r4));
4911 __ PrepareCallCFunction(1, r1);
4912 __ CallCFunction(
4913 ExternalReference::fill_heap_number_with_random_function(), 1);
4914 frame_->EmitPush(r0);
4915 }
ager@chromium.orgeadaf222009-06-16 09:43:10 +00004916}
4917
4918
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00004919void CodeGenerator::GenerateStringAdd(ZoneList<Expression*>* args) {
4920 ASSERT_EQ(2, args->length());
4921
4922 Load(args->at(0));
4923 Load(args->at(1));
4924
ager@chromium.org5c838252010-02-19 08:53:10 +00004925 StringAddStub stub(NO_STRING_ADD_FLAGS);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004926 frame_->SpillAll();
ager@chromium.org5c838252010-02-19 08:53:10 +00004927 frame_->CallStub(&stub, 2);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00004928 frame_->EmitPush(r0);
4929}
4930
4931
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004932void CodeGenerator::GenerateSubString(ZoneList<Expression*>* args) {
4933 ASSERT_EQ(3, args->length());
4934
4935 Load(args->at(0));
4936 Load(args->at(1));
4937 Load(args->at(2));
4938
ager@chromium.org5c838252010-02-19 08:53:10 +00004939 SubStringStub stub;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004940 frame_->SpillAll();
ager@chromium.org5c838252010-02-19 08:53:10 +00004941 frame_->CallStub(&stub, 3);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004942 frame_->EmitPush(r0);
4943}
4944
4945
4946void CodeGenerator::GenerateStringCompare(ZoneList<Expression*>* args) {
4947 ASSERT_EQ(2, args->length());
4948
4949 Load(args->at(0));
4950 Load(args->at(1));
4951
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004952 StringCompareStub stub;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004953 frame_->SpillAll();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004954 frame_->CallStub(&stub, 2);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004955 frame_->EmitPush(r0);
4956}
4957
4958
4959void CodeGenerator::GenerateRegExpExec(ZoneList<Expression*>* args) {
4960 ASSERT_EQ(4, args->length());
4961
4962 Load(args->at(0));
4963 Load(args->at(1));
4964 Load(args->at(2));
4965 Load(args->at(3));
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00004966 RegExpExecStub stub;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004967 frame_->SpillAll();
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00004968 frame_->CallStub(&stub, 4);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004969 frame_->EmitPush(r0);
4970}
4971
4972
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00004973void CodeGenerator::GenerateRegExpConstructResult(ZoneList<Expression*>* args) {
4974 // No stub. This code only occurs a few times in regexp.js.
4975 const int kMaxInlineLength = 100;
4976 ASSERT_EQ(3, args->length());
4977 Load(args->at(0)); // Size of array, smi.
4978 Load(args->at(1)); // "index" property value.
4979 Load(args->at(2)); // "input" property value.
4980 {
4981 VirtualFrame::SpilledScope spilled_scope(frame_);
4982 Label slowcase;
4983 Label done;
4984 __ ldr(r1, MemOperand(sp, kPointerSize * 2));
4985 STATIC_ASSERT(kSmiTag == 0);
4986 STATIC_ASSERT(kSmiTagSize == 1);
4987 __ tst(r1, Operand(kSmiTagMask));
4988 __ b(ne, &slowcase);
4989 __ cmp(r1, Operand(Smi::FromInt(kMaxInlineLength)));
4990 __ b(hi, &slowcase);
4991 // Smi-tagging is equivalent to multiplying by 2.
4992 // Allocate RegExpResult followed by FixedArray with size in ebx.
4993 // JSArray: [Map][empty properties][Elements][Length-smi][index][input]
4994 // Elements: [Map][Length][..elements..]
4995 // Size of JSArray with two in-object properties and the header of a
4996 // FixedArray.
4997 int objects_size =
4998 (JSRegExpResult::kSize + FixedArray::kHeaderSize) / kPointerSize;
4999 __ mov(r5, Operand(r1, LSR, kSmiTagSize + kSmiShiftSize));
5000 __ add(r2, r5, Operand(objects_size));
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00005001 __ AllocateInNewSpace(
5002 r2, // In: Size, in words.
5003 r0, // Out: Start of allocation (tagged).
5004 r3, // Scratch register.
5005 r4, // Scratch register.
5006 &slowcase,
5007 static_cast<AllocationFlags>(TAG_OBJECT | SIZE_IN_WORDS));
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00005008 // r0: Start of allocated area, object-tagged.
5009 // r1: Number of elements in array, as smi.
5010 // r5: Number of elements, untagged.
5011
5012 // Set JSArray map to global.regexp_result_map().
5013 // Set empty properties FixedArray.
5014 // Set elements to point to FixedArray allocated right after the JSArray.
5015 // Interleave operations for better latency.
5016 __ ldr(r2, ContextOperand(cp, Context::GLOBAL_INDEX));
5017 __ add(r3, r0, Operand(JSRegExpResult::kSize));
5018 __ mov(r4, Operand(Factory::empty_fixed_array()));
5019 __ ldr(r2, FieldMemOperand(r2, GlobalObject::kGlobalContextOffset));
5020 __ str(r3, FieldMemOperand(r0, JSObject::kElementsOffset));
5021 __ ldr(r2, ContextOperand(r2, Context::REGEXP_RESULT_MAP_INDEX));
5022 __ str(r4, FieldMemOperand(r0, JSObject::kPropertiesOffset));
5023 __ str(r2, FieldMemOperand(r0, HeapObject::kMapOffset));
5024
5025 // Set input, index and length fields from arguments.
5026 __ ldm(ia_w, sp, static_cast<RegList>(r2.bit() | r4.bit()));
5027 __ str(r1, FieldMemOperand(r0, JSArray::kLengthOffset));
5028 __ add(sp, sp, Operand(kPointerSize));
5029 __ str(r4, FieldMemOperand(r0, JSRegExpResult::kIndexOffset));
5030 __ str(r2, FieldMemOperand(r0, JSRegExpResult::kInputOffset));
5031
5032 // Fill out the elements FixedArray.
5033 // r0: JSArray, tagged.
5034 // r3: FixedArray, tagged.
5035 // r5: Number of elements in array, untagged.
5036
5037 // Set map.
5038 __ mov(r2, Operand(Factory::fixed_array_map()));
5039 __ str(r2, FieldMemOperand(r3, HeapObject::kMapOffset));
5040 // Set FixedArray length.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005041 __ mov(r6, Operand(r5, LSL, kSmiTagSize));
5042 __ str(r6, FieldMemOperand(r3, FixedArray::kLengthOffset));
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00005043 // Fill contents of fixed-array with the-hole.
5044 __ mov(r2, Operand(Factory::the_hole_value()));
5045 __ add(r3, r3, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
5046 // Fill fixed array elements with hole.
5047 // r0: JSArray, tagged.
5048 // r2: the hole.
5049 // r3: Start of elements in FixedArray.
5050 // r5: Number of elements to fill.
5051 Label loop;
5052 __ tst(r5, Operand(r5));
5053 __ bind(&loop);
5054 __ b(le, &done); // Jump if r1 is negative or zero.
5055 __ sub(r5, r5, Operand(1), SetCC);
5056 __ str(r2, MemOperand(r3, r5, LSL, kPointerSizeLog2));
5057 __ jmp(&loop);
5058
5059 __ bind(&slowcase);
5060 __ CallRuntime(Runtime::kRegExpConstructResult, 3);
5061
5062 __ bind(&done);
5063 }
5064 frame_->Forget(3);
5065 frame_->EmitPush(r0);
5066}
5067
5068
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005069class DeferredSearchCache: public DeferredCode {
5070 public:
5071 DeferredSearchCache(Register dst, Register cache, Register key)
5072 : dst_(dst), cache_(cache), key_(key) {
5073 set_comment("[ DeferredSearchCache");
5074 }
5075
5076 virtual void Generate();
5077
5078 private:
5079 Register dst_, cache_, key_;
5080};
5081
5082
5083void DeferredSearchCache::Generate() {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005084 __ Push(cache_, key_);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005085 __ CallRuntime(Runtime::kGetFromCache, 2);
5086 if (!dst_.is(r0)) {
5087 __ mov(dst_, r0);
5088 }
5089}
5090
5091
5092void CodeGenerator::GenerateGetFromCache(ZoneList<Expression*>* args) {
5093 ASSERT_EQ(2, args->length());
5094
5095 ASSERT_NE(NULL, args->at(0)->AsLiteral());
5096 int cache_id = Smi::cast(*(args->at(0)->AsLiteral()->handle()))->value();
5097
5098 Handle<FixedArray> jsfunction_result_caches(
5099 Top::global_context()->jsfunction_result_caches());
5100 if (jsfunction_result_caches->length() <= cache_id) {
5101 __ Abort("Attempt to use undefined cache.");
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005102 frame_->EmitPushRoot(Heap::kUndefinedValueRootIndex);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005103 return;
5104 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005105
5106 Load(args->at(1));
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005107
5108 VirtualFrame::SpilledScope spilled_scope(frame_);
5109
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005110 frame_->EmitPop(r2);
5111
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005112 __ ldr(r1, ContextOperand(cp, Context::GLOBAL_INDEX));
5113 __ ldr(r1, FieldMemOperand(r1, GlobalObject::kGlobalContextOffset));
5114 __ ldr(r1, ContextOperand(r1, Context::JSFUNCTION_RESULT_CACHES_INDEX));
5115 __ ldr(r1, FieldMemOperand(r1, FixedArray::OffsetOfElementAt(cache_id)));
5116
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005117 DeferredSearchCache* deferred = new DeferredSearchCache(r0, r1, r2);
5118
5119 const int kFingerOffset =
5120 FixedArray::OffsetOfElementAt(JSFunctionResultCache::kFingerIndex);
5121 ASSERT(kSmiTag == 0 && kSmiTagSize == 1);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005122 __ ldr(r0, FieldMemOperand(r1, kFingerOffset));
5123 // r0 now holds finger offset as a smi.
5124 __ add(r3, r1, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
5125 // r3 now points to the start of fixed array elements.
5126 __ ldr(r0, MemOperand(r3, r0, LSL, kPointerSizeLog2 - kSmiTagSize, PreIndex));
5127 // Note side effect of PreIndex: r3 now points to the key of the pair.
5128 __ cmp(r2, r0);
5129 deferred->Branch(ne);
5130
5131 __ ldr(r0, MemOperand(r3, kPointerSize));
5132
5133 deferred->BindExit();
5134 frame_->EmitPush(r0);
5135}
5136
5137
ager@chromium.org5c838252010-02-19 08:53:10 +00005138void CodeGenerator::GenerateNumberToString(ZoneList<Expression*>* args) {
5139 ASSERT_EQ(args->length(), 1);
5140
5141 // Load the argument on the stack and jump to the runtime.
5142 Load(args->at(0));
5143
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005144 NumberToStringStub stub;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005145 frame_->SpillAll();
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005146 frame_->CallStub(&stub, 1);
ager@chromium.org5c838252010-02-19 08:53:10 +00005147 frame_->EmitPush(r0);
5148}
5149
5150
ager@chromium.orgac091b72010-05-05 07:34:42 +00005151class DeferredSwapElements: public DeferredCode {
5152 public:
5153 DeferredSwapElements(Register object, Register index1, Register index2)
5154 : object_(object), index1_(index1), index2_(index2) {
5155 set_comment("[ DeferredSwapElements");
5156 }
5157
5158 virtual void Generate();
5159
5160 private:
5161 Register object_, index1_, index2_;
5162};
5163
5164
5165void DeferredSwapElements::Generate() {
5166 __ push(object_);
5167 __ push(index1_);
5168 __ push(index2_);
5169 __ CallRuntime(Runtime::kSwapElements, 3);
5170}
5171
5172
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00005173void CodeGenerator::GenerateSwapElements(ZoneList<Expression*>* args) {
5174 Comment cmnt(masm_, "[ GenerateSwapElements");
5175
5176 ASSERT_EQ(3, args->length());
5177
5178 Load(args->at(0));
5179 Load(args->at(1));
5180 Load(args->at(2));
5181
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005182 VirtualFrame::SpilledScope spilled_scope(frame_);
5183
ager@chromium.orgac091b72010-05-05 07:34:42 +00005184 Register index2 = r2;
5185 Register index1 = r1;
5186 Register object = r0;
5187 Register tmp1 = r3;
5188 Register tmp2 = r4;
5189
5190 frame_->EmitPop(index2);
5191 frame_->EmitPop(index1);
5192 frame_->EmitPop(object);
5193
5194 DeferredSwapElements* deferred =
5195 new DeferredSwapElements(object, index1, index2);
5196
5197 // Fetch the map and check if array is in fast case.
5198 // Check that object doesn't require security checks and
5199 // has no indexed interceptor.
5200 __ CompareObjectType(object, tmp1, tmp2, FIRST_JS_OBJECT_TYPE);
5201 deferred->Branch(lt);
5202 __ ldrb(tmp2, FieldMemOperand(tmp1, Map::kBitFieldOffset));
5203 __ tst(tmp2, Operand(KeyedLoadIC::kSlowCaseBitFieldMask));
5204 deferred->Branch(nz);
5205
5206 // Check the object's elements are in fast case.
5207 __ ldr(tmp1, FieldMemOperand(object, JSObject::kElementsOffset));
5208 __ ldr(tmp2, FieldMemOperand(tmp1, HeapObject::kMapOffset));
5209 __ LoadRoot(ip, Heap::kFixedArrayMapRootIndex);
5210 __ cmp(tmp2, ip);
5211 deferred->Branch(ne);
5212
5213 // Smi-tagging is equivalent to multiplying by 2.
5214 STATIC_ASSERT(kSmiTag == 0);
5215 STATIC_ASSERT(kSmiTagSize == 1);
5216
5217 // Check that both indices are smis.
5218 __ mov(tmp2, index1);
5219 __ orr(tmp2, tmp2, index2);
5220 __ tst(tmp2, Operand(kSmiTagMask));
5221 deferred->Branch(nz);
5222
5223 // Bring the offsets into the fixed array in tmp1 into index1 and
5224 // index2.
5225 __ mov(tmp2, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
5226 __ add(index1, tmp2, Operand(index1, LSL, kPointerSizeLog2 - kSmiTagSize));
5227 __ add(index2, tmp2, Operand(index2, LSL, kPointerSizeLog2 - kSmiTagSize));
5228
5229 // Swap elements.
5230 Register tmp3 = object;
5231 object = no_reg;
5232 __ ldr(tmp3, MemOperand(tmp1, index1));
5233 __ ldr(tmp2, MemOperand(tmp1, index2));
5234 __ str(tmp3, MemOperand(tmp1, index2));
5235 __ str(tmp2, MemOperand(tmp1, index1));
5236
5237 Label done;
5238 __ InNewSpace(tmp1, tmp2, eq, &done);
5239 // Possible optimization: do a check that both values are Smis
5240 // (or them and test against Smi mask.)
5241
5242 __ mov(tmp2, tmp1);
5243 RecordWriteStub recordWrite1(tmp1, index1, tmp3);
5244 __ CallStub(&recordWrite1);
5245
5246 RecordWriteStub recordWrite2(tmp2, index2, tmp3);
5247 __ CallStub(&recordWrite2);
5248
5249 __ bind(&done);
5250
5251 deferred->BindExit();
5252 __ LoadRoot(tmp1, Heap::kUndefinedValueRootIndex);
5253 frame_->EmitPush(tmp1);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00005254}
5255
5256
ager@chromium.org357bf652010-04-12 11:30:10 +00005257void CodeGenerator::GenerateCallFunction(ZoneList<Expression*>* args) {
5258 Comment cmnt(masm_, "[ GenerateCallFunction");
5259
5260 ASSERT(args->length() >= 2);
5261
5262 int n_args = args->length() - 2; // for receiver and function.
5263 Load(args->at(0)); // receiver
5264 for (int i = 0; i < n_args; i++) {
5265 Load(args->at(i + 1));
5266 }
5267 Load(args->at(n_args + 1)); // function
5268 frame_->CallJSFunction(n_args);
5269 frame_->EmitPush(r0);
5270}
5271
5272
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005273void CodeGenerator::GenerateMathSin(ZoneList<Expression*>* args) {
5274 ASSERT_EQ(args->length(), 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005275 Load(args->at(0));
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00005276 if (CpuFeatures::IsSupported(VFP3)) {
5277 TranscendentalCacheStub stub(TranscendentalCache::SIN);
5278 frame_->SpillAllButCopyTOSToR0();
5279 frame_->CallStub(&stub, 1);
5280 } else {
5281 frame_->CallRuntime(Runtime::kMath_sin, 1);
5282 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005283 frame_->EmitPush(r0);
5284}
5285
5286
5287void CodeGenerator::GenerateMathCos(ZoneList<Expression*>* args) {
5288 ASSERT_EQ(args->length(), 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005289 Load(args->at(0));
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00005290 if (CpuFeatures::IsSupported(VFP3)) {
5291 TranscendentalCacheStub stub(TranscendentalCache::COS);
5292 frame_->SpillAllButCopyTOSToR0();
5293 frame_->CallStub(&stub, 1);
5294 } else {
5295 frame_->CallRuntime(Runtime::kMath_cos, 1);
5296 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005297 frame_->EmitPush(r0);
5298}
5299
5300
ager@chromium.org7c537e22008-10-16 08:43:32 +00005301void CodeGenerator::GenerateObjectEquals(ZoneList<Expression*>* args) {
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005302 ASSERT(args->length() == 2);
5303
5304 // Load the two objects into registers and perform the comparison.
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00005305 Load(args->at(0));
5306 Load(args->at(1));
5307 Register lhs = frame_->PopToRegister();
5308 Register rhs = frame_->PopToRegister(lhs);
5309 __ cmp(lhs, rhs);
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005310 cc_reg_ = eq;
5311}
5312
5313
ager@chromium.org7c537e22008-10-16 08:43:32 +00005314void CodeGenerator::VisitCallRuntime(CallRuntime* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005315#ifdef DEBUG
5316 int original_height = frame_->height();
5317#endif
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005318 if (CheckForInlineRuntimeCall(node)) {
5319 ASSERT((has_cc() && frame_->height() == original_height) ||
5320 (!has_cc() && frame_->height() == original_height + 1));
5321 return;
5322 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005323
5324 ZoneList<Expression*>* args = node->arguments();
5325 Comment cmnt(masm_, "[ CallRuntime");
5326 Runtime::Function* function = node->function();
5327
ager@chromium.org41826e72009-03-30 13:30:57 +00005328 if (function == NULL) {
mads.s.ager31e71382008-08-13 09:32:07 +00005329 // Prepare stack for calling JS runtime function.
mads.s.ager31e71382008-08-13 09:32:07 +00005330 // Push the builtins object found in the current global object.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005331 Register scratch = VirtualFrame::scratch0();
5332 __ ldr(scratch, GlobalObject());
5333 Register builtins = frame_->GetTOSRegister();
5334 __ ldr(builtins, FieldMemOperand(scratch, GlobalObject::kBuiltinsOffset));
5335 frame_->EmitPush(builtins);
ager@chromium.org41826e72009-03-30 13:30:57 +00005336 }
mads.s.ager31e71382008-08-13 09:32:07 +00005337
ager@chromium.org41826e72009-03-30 13:30:57 +00005338 // Push the arguments ("left-to-right").
5339 int arg_count = args->length();
5340 for (int i = 0; i < arg_count; i++) {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005341 Load(args->at(i));
ager@chromium.org41826e72009-03-30 13:30:57 +00005342 }
mads.s.ager31e71382008-08-13 09:32:07 +00005343
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005344 VirtualFrame::SpilledScope spilled_scope(frame_);
5345
ager@chromium.org41826e72009-03-30 13:30:57 +00005346 if (function == NULL) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005347 // Call the JS runtime function.
ager@chromium.org5c838252010-02-19 08:53:10 +00005348 __ mov(r2, Operand(node->name()));
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005349 InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP;
5350 Handle<Code> stub = ComputeCallInitialize(arg_count, in_loop);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005351 frame_->CallCodeObject(stub, RelocInfo::CODE_TARGET, arg_count + 1);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005352 __ ldr(cp, frame_->Context());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005353 frame_->EmitPush(r0);
ager@chromium.org41826e72009-03-30 13:30:57 +00005354 } else {
5355 // Call the C runtime function.
5356 frame_->CallRuntime(function, arg_count);
5357 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005358 }
ager@chromium.orgac091b72010-05-05 07:34:42 +00005359 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005360}
5361
5362
ager@chromium.org7c537e22008-10-16 08:43:32 +00005363void CodeGenerator::VisitUnaryOperation(UnaryOperation* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005364#ifdef DEBUG
5365 int original_height = frame_->height();
5366#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005367 Comment cmnt(masm_, "[ UnaryOperation");
5368
5369 Token::Value op = node->op();
5370
5371 if (op == Token::NOT) {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005372 LoadCondition(node->expression(), false_target(), true_target(), true);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00005373 // LoadCondition may (and usually does) leave a test and branch to
5374 // be emitted by the caller. In that case, negate the condition.
5375 if (has_cc()) cc_reg_ = NegateCondition(cc_reg_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005376
5377 } else if (op == Token::DELETE) {
5378 Property* property = node->expression()->AsProperty();
mads.s.ager31e71382008-08-13 09:32:07 +00005379 Variable* variable = node->expression()->AsVariableProxy()->AsVariable();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005380 if (property != NULL) {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005381 Load(property->obj());
5382 Load(property->key());
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
mads.s.ager31e71382008-08-13 09:32:07 +00005386 } else if (variable != NULL) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005387 Slot* slot = variable->slot();
5388 if (variable->is_global()) {
5389 LoadGlobal();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005390 frame_->EmitPush(Operand(variable->name()));
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00005391 frame_->InvokeBuiltin(Builtins::DELETE, CALL_JS, 2);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005392 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005393
5394 } else if (slot != NULL && slot->type() == Slot::LOOKUP) {
5395 // lookup the context holding the named variable
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005396 frame_->EmitPush(cp);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005397 frame_->EmitPush(Operand(variable->name()));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005398 frame_->CallRuntime(Runtime::kLookupContext, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005399 // r0: context
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005400 frame_->EmitPush(r0);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005401 frame_->EmitPush(Operand(variable->name()));
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00005402 frame_->InvokeBuiltin(Builtins::DELETE, CALL_JS, 2);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005403 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005404
mads.s.ager31e71382008-08-13 09:32:07 +00005405 } else {
5406 // Default: Result of deleting non-global, not dynamically
5407 // introduced variables is false.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005408 frame_->EmitPushRoot(Heap::kFalseValueRootIndex);
mads.s.ager31e71382008-08-13 09:32:07 +00005409 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005410
5411 } else {
5412 // Default: Result of deleting expressions is true.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005413 Load(node->expression()); // may have side-effects
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005414 frame_->Drop();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005415 frame_->EmitPushRoot(Heap::kTrueValueRootIndex);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005416 }
5417
5418 } else if (op == Token::TYPEOF) {
5419 // Special case for loading the typeof expression; see comment on
5420 // LoadTypeofExpression().
5421 LoadTypeofExpression(node->expression());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005422 frame_->CallRuntime(Runtime::kTypeof, 1);
5423 frame_->EmitPush(r0); // r0 has result
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005424
5425 } else {
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00005426 bool can_overwrite =
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005427 (node->expression()->AsBinaryOperation() != NULL &&
5428 node->expression()->AsBinaryOperation()->ResultOverwriteAllowed());
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00005429 UnaryOverwriteMode overwrite =
5430 can_overwrite ? UNARY_OVERWRITE : UNARY_NO_OVERWRITE;
5431
5432 bool no_negative_zero = node->expression()->no_negative_zero();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005433 Load(node->expression());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005434 switch (op) {
5435 case Token::NOT:
5436 case Token::DELETE:
5437 case Token::TYPEOF:
5438 UNREACHABLE(); // handled above
5439 break;
5440
5441 case Token::SUB: {
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00005442 frame_->PopToR0();
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00005443 GenericUnaryOpStub stub(
5444 Token::SUB,
5445 overwrite,
5446 no_negative_zero ? kIgnoreNegativeZero : kStrictNegativeZero);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005447 frame_->CallStub(&stub, 0);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005448 frame_->EmitPush(r0); // r0 has result
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005449 break;
5450 }
5451
5452 case Token::BIT_NOT: {
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00005453 Register tos = frame_->PopToRegister();
5454 JumpTarget not_smi_label;
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005455 JumpTarget continue_label;
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00005456 // Smi check.
5457 __ tst(tos, Operand(kSmiTagMask));
5458 not_smi_label.Branch(ne);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005459
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00005460 __ mvn(tos, Operand(tos));
5461 __ bic(tos, tos, Operand(kSmiTagMask)); // Bit-clear inverted smi-tag.
5462 frame_->EmitPush(tos);
5463 // The fast case is the first to jump to the continue label, so it gets
5464 // to decide the virtual frame layout.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005465 continue_label.Jump();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005466
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00005467 not_smi_label.Bind();
5468 frame_->SpillAll();
5469 __ Move(r0, tos);
5470 GenericUnaryOpStub stub(Token::BIT_NOT, overwrite);
5471 frame_->CallStub(&stub, 0);
5472 frame_->EmitPush(r0);
5473
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005474 continue_label.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005475 break;
5476 }
5477
5478 case Token::VOID:
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005479 frame_->Drop();
5480 frame_->EmitPushRoot(Heap::kUndefinedValueRootIndex);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005481 break;
5482
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005483 case Token::ADD: {
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00005484 Register tos = frame_->Peek();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005485 // Smi check.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005486 JumpTarget continue_label;
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00005487 __ tst(tos, Operand(kSmiTagMask));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005488 continue_label.Branch(eq);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00005489
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00005490 frame_->InvokeBuiltin(Builtins::TO_NUMBER, CALL_JS, 1);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00005491 frame_->EmitPush(r0);
5492
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005493 continue_label.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005494 break;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005495 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005496 default:
5497 UNREACHABLE();
5498 }
5499 }
kasperl@chromium.orge959c182009-07-27 08:59:04 +00005500 ASSERT(!has_valid_frame() ||
5501 (has_cc() && frame_->height() == original_height) ||
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005502 (!has_cc() && frame_->height() == original_height + 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005503}
5504
5505
ager@chromium.org7c537e22008-10-16 08:43:32 +00005506void CodeGenerator::VisitCountOperation(CountOperation* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005507#ifdef DEBUG
5508 int original_height = frame_->height();
5509#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005510 Comment cmnt(masm_, "[ CountOperation");
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00005511 VirtualFrame::RegisterAllocationScope scope(this);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005512
5513 bool is_postfix = node->is_postfix();
5514 bool is_increment = node->op() == Token::INC;
5515
5516 Variable* var = node->expression()->AsVariableProxy()->AsVariable();
5517 bool is_const = (var != NULL && var->mode() == Variable::CONST);
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00005518 bool is_slot = (var != NULL && var->mode() == Variable::VAR);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005519
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00005520 if (!is_const && is_slot && type_info(var->slot()).IsSmi()) {
5521 // The type info declares that this variable is always a Smi. That
5522 // means it is a Smi both before and after the increment/decrement.
5523 // Lets make use of that to make a very minimal count.
5524 Reference target(this, node->expression(), !is_const);
5525 ASSERT(!target.is_illegal());
5526 target.GetValue(); // Pushes the value.
5527 Register value = frame_->PopToRegister();
5528 if (is_postfix) frame_->EmitPush(value);
5529 if (is_increment) {
5530 __ add(value, value, Operand(Smi::FromInt(1)));
5531 } else {
5532 __ sub(value, value, Operand(Smi::FromInt(1)));
5533 }
5534 frame_->EmitPush(value);
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00005535 target.SetValue(NOT_CONST_INIT, LIKELY_SMI);
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00005536 if (is_postfix) frame_->Pop();
5537 ASSERT_EQ(original_height + 1, frame_->height());
5538 return;
5539 }
5540
5541 // If it's a postfix expression and its result is not ignored and the
5542 // reference is non-trivial, then push a placeholder on the stack now
5543 // to hold the result of the expression.
5544 bool placeholder_pushed = false;
5545 if (!is_slot && is_postfix) {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005546 frame_->EmitPush(Operand(Smi::FromInt(0)));
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00005547 placeholder_pushed = true;
mads.s.ager31e71382008-08-13 09:32:07 +00005548 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005549
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005550 // A constant reference is not saved to, so a constant reference is not a
5551 // compound assignment reference.
5552 { Reference target(this, node->expression(), !is_const);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005553 if (target.is_illegal()) {
5554 // Spoof the virtual frame to have the expected height (one higher
5555 // than on entry).
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00005556 if (!placeholder_pushed) frame_->EmitPush(Operand(Smi::FromInt(0)));
ager@chromium.orgac091b72010-05-05 07:34:42 +00005557 ASSERT_EQ(original_height + 1, frame_->height());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005558 return;
5559 }
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00005560
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005561 // This pushes 0, 1 or 2 words on the object to be used later when updating
5562 // the target. It also pushes the current value of the target.
ager@chromium.org357bf652010-04-12 11:30:10 +00005563 target.GetValue();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005564
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005565 JumpTarget slow;
5566 JumpTarget exit;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005567
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005568 Register value = frame_->PopToRegister();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005569
5570 // Postfix: Store the old value as the result.
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00005571 if (placeholder_pushed) {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005572 frame_->SetElementAt(value, target.size());
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00005573 } else if (is_postfix) {
5574 frame_->EmitPush(value);
5575 __ mov(VirtualFrame::scratch0(), value);
5576 value = VirtualFrame::scratch0();
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005577 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005578
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00005579 // Check for smi operand.
5580 __ tst(value, Operand(kSmiTagMask));
5581 slow.Branch(ne);
5582
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005583 // Perform optimistic increment/decrement.
5584 if (is_increment) {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005585 __ add(value, value, Operand(Smi::FromInt(1)), SetCC);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005586 } else {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005587 __ sub(value, value, Operand(Smi::FromInt(1)), SetCC);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005588 }
5589
5590 // If the increment/decrement didn't overflow, we're done.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005591 exit.Branch(vc);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005592
5593 // Revert optimistic increment/decrement.
5594 if (is_increment) {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005595 __ sub(value, value, Operand(Smi::FromInt(1)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005596 } else {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005597 __ add(value, value, Operand(Smi::FromInt(1)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005598 }
5599
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005600 // Slow case: Convert to number. At this point the
5601 // value to be incremented is in the value register..
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005602 slow.Bind();
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005603
5604 // Convert the operand to a number.
5605 frame_->EmitPush(value);
5606
kasperl@chromium.org8ccb0be2009-04-07 07:21:39 +00005607 {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005608 VirtualFrame::SpilledScope spilled(frame_);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00005609 frame_->InvokeBuiltin(Builtins::TO_NUMBER, CALL_JS, 1);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005610
5611 if (is_postfix) {
5612 // Postfix: store to result (on the stack).
5613 __ str(r0, frame_->ElementAt(target.size()));
5614 }
5615
5616 // Compute the new value.
5617 frame_->EmitPush(r0);
5618 frame_->EmitPush(Operand(Smi::FromInt(1)));
5619 if (is_increment) {
5620 frame_->CallRuntime(Runtime::kNumberAdd, 2);
5621 } else {
5622 frame_->CallRuntime(Runtime::kNumberSub, 2);
5623 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005624 }
5625
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005626 __ Move(value, r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005627 // Store the new value in the target if not const.
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005628 // At this point the answer is in the value register.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005629 exit.Bind();
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005630 frame_->EmitPush(value);
5631 // Set the target with the result, leaving the result on
5632 // top of the stack. Removes the target from the stack if
5633 // it has a non-zero size.
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00005634 if (!is_const) target.SetValue(NOT_CONST_INIT, LIKELY_SMI);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005635 }
5636
5637 // Postfix: Discard the new value and use the old.
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005638 if (is_postfix) frame_->Pop();
ager@chromium.orgac091b72010-05-05 07:34:42 +00005639 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005640}
5641
5642
ager@chromium.orgb26c50a2010-03-26 09:27:16 +00005643void CodeGenerator::GenerateLogicalBooleanOperation(BinaryOperation* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005644 // According to ECMA-262 section 11.11, page 58, the binary logical
5645 // operators must yield the result of one of the two expressions
5646 // before any ToBoolean() conversions. This means that the value
5647 // produced by a && or || operator is not necessarily a boolean.
5648
5649 // NOTE: If the left hand side produces a materialized value (not in
5650 // the CC register), we force the right hand side to do the
5651 // same. This is necessary because we may have to branch to the exit
5652 // after evaluating the left hand side (due to the shortcut
5653 // semantics), but the compiler must (statically) know if the result
5654 // of compiling the binary operation is materialized or not.
ager@chromium.orgb26c50a2010-03-26 09:27:16 +00005655 if (node->op() == Token::AND) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005656 JumpTarget is_true;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005657 LoadCondition(node->left(), &is_true, false_target(), false);
kasperl@chromium.orge959c182009-07-27 08:59:04 +00005658 if (has_valid_frame() && !has_cc()) {
5659 // The left-hand side result is on top of the virtual frame.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005660 JumpTarget pop_and_continue;
5661 JumpTarget exit;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005662
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00005663 frame_->Dup();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005664 // Avoid popping the result if it converts to 'false' using the
5665 // standard ToBoolean() conversion as described in ECMA-262,
5666 // section 9.2, page 30.
mads.s.ager31e71382008-08-13 09:32:07 +00005667 ToBoolean(&pop_and_continue, &exit);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005668 Branch(false, &exit);
5669
5670 // Pop the result of evaluating the first part.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005671 pop_and_continue.Bind();
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00005672 frame_->Pop();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005673
5674 // Evaluate right side expression.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005675 is_true.Bind();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005676 Load(node->right());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005677
5678 // Exit (always with a materialized value).
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005679 exit.Bind();
kasperl@chromium.orge959c182009-07-27 08:59:04 +00005680 } else if (has_cc() || is_true.is_linked()) {
5681 // The left-hand side is either (a) partially compiled to
5682 // control flow with a final branch left to emit or (b) fully
5683 // compiled to control flow and possibly true.
5684 if (has_cc()) {
5685 Branch(false, false_target());
5686 }
5687 is_true.Bind();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005688 LoadCondition(node->right(), true_target(), false_target(), false);
kasperl@chromium.orge959c182009-07-27 08:59:04 +00005689 } else {
5690 // Nothing to do.
5691 ASSERT(!has_valid_frame() && !has_cc() && !is_true.is_linked());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005692 }
5693
ager@chromium.orgb26c50a2010-03-26 09:27:16 +00005694 } else {
5695 ASSERT(node->op() == Token::OR);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005696 JumpTarget is_false;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005697 LoadCondition(node->left(), true_target(), &is_false, false);
kasperl@chromium.orge959c182009-07-27 08:59:04 +00005698 if (has_valid_frame() && !has_cc()) {
5699 // The left-hand side result is on top of the virtual frame.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005700 JumpTarget pop_and_continue;
5701 JumpTarget exit;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005702
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00005703 frame_->Dup();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005704 // Avoid popping the result if it converts to 'true' using the
5705 // standard ToBoolean() conversion as described in ECMA-262,
5706 // section 9.2, page 30.
mads.s.ager31e71382008-08-13 09:32:07 +00005707 ToBoolean(&exit, &pop_and_continue);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005708 Branch(true, &exit);
5709
5710 // Pop the result of evaluating the first part.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005711 pop_and_continue.Bind();
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00005712 frame_->Pop();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005713
5714 // Evaluate right side expression.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005715 is_false.Bind();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005716 Load(node->right());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005717
5718 // Exit (always with a materialized value).
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005719 exit.Bind();
kasperl@chromium.orge959c182009-07-27 08:59:04 +00005720 } else if (has_cc() || is_false.is_linked()) {
5721 // The left-hand side is either (a) partially compiled to
5722 // control flow with a final branch left to emit or (b) fully
5723 // compiled to control flow and possibly false.
5724 if (has_cc()) {
5725 Branch(true, true_target());
5726 }
5727 is_false.Bind();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005728 LoadCondition(node->right(), true_target(), false_target(), false);
kasperl@chromium.orge959c182009-07-27 08:59:04 +00005729 } else {
5730 // Nothing to do.
5731 ASSERT(!has_valid_frame() && !has_cc() && !is_false.is_linked());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005732 }
ager@chromium.orgb26c50a2010-03-26 09:27:16 +00005733 }
5734}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005735
ager@chromium.orgb26c50a2010-03-26 09:27:16 +00005736
5737void CodeGenerator::VisitBinaryOperation(BinaryOperation* node) {
5738#ifdef DEBUG
5739 int original_height = frame_->height();
5740#endif
ager@chromium.orgb26c50a2010-03-26 09:27:16 +00005741 Comment cmnt(masm_, "[ BinaryOperation");
5742
5743 if (node->op() == Token::AND || node->op() == Token::OR) {
5744 GenerateLogicalBooleanOperation(node);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005745 } else {
5746 // Optimize for the case where (at least) one of the expressions
5747 // is a literal small integer.
5748 Literal* lliteral = node->left()->AsLiteral();
5749 Literal* rliteral = node->right()->AsLiteral();
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005750 // NOTE: The code below assumes that the slow cases (calls to runtime)
5751 // never return a constant/immutable object.
5752 bool overwrite_left =
5753 (node->left()->AsBinaryOperation() != NULL &&
5754 node->left()->AsBinaryOperation()->ResultOverwriteAllowed());
5755 bool overwrite_right =
5756 (node->right()->AsBinaryOperation() != NULL &&
5757 node->right()->AsBinaryOperation()->ResultOverwriteAllowed());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005758
5759 if (rliteral != NULL && rliteral->handle()->IsSmi()) {
ager@chromium.org357bf652010-04-12 11:30:10 +00005760 VirtualFrame::RegisterAllocationScope scope(this);
5761 Load(node->left());
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00005762 if (frame_->KnownSmiAt(0)) overwrite_left = false;
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00005763 SmiOperation(node->op(),
5764 rliteral->handle(),
5765 false,
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00005766 overwrite_left ? OVERWRITE_LEFT : NO_OVERWRITE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005767 } else if (lliteral != NULL && lliteral->handle()->IsSmi()) {
ager@chromium.org357bf652010-04-12 11:30:10 +00005768 VirtualFrame::RegisterAllocationScope scope(this);
5769 Load(node->right());
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00005770 if (frame_->KnownSmiAt(0)) overwrite_right = false;
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00005771 SmiOperation(node->op(),
5772 lliteral->handle(),
5773 true,
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00005774 overwrite_right ? OVERWRITE_RIGHT : NO_OVERWRITE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005775 } else {
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00005776 GenerateInlineSmi inline_smi =
5777 loop_nesting() > 0 ? GENERATE_INLINE_SMI : DONT_GENERATE_INLINE_SMI;
5778 if (lliteral != NULL) {
5779 ASSERT(!lliteral->handle()->IsSmi());
5780 inline_smi = DONT_GENERATE_INLINE_SMI;
5781 }
5782 if (rliteral != NULL) {
5783 ASSERT(!rliteral->handle()->IsSmi());
5784 inline_smi = DONT_GENERATE_INLINE_SMI;
5785 }
ager@chromium.org357bf652010-04-12 11:30:10 +00005786 VirtualFrame::RegisterAllocationScope scope(this);
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005787 OverwriteMode overwrite_mode = NO_OVERWRITE;
5788 if (overwrite_left) {
5789 overwrite_mode = OVERWRITE_LEFT;
5790 } else if (overwrite_right) {
5791 overwrite_mode = OVERWRITE_RIGHT;
5792 }
ager@chromium.org357bf652010-04-12 11:30:10 +00005793 Load(node->left());
5794 Load(node->right());
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00005795 GenericBinaryOperation(node->op(), overwrite_mode, inline_smi);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005796 }
5797 }
kasperl@chromium.orge959c182009-07-27 08:59:04 +00005798 ASSERT(!has_valid_frame() ||
5799 (has_cc() && frame_->height() == original_height) ||
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005800 (!has_cc() && frame_->height() == original_height + 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005801}
5802
5803
ager@chromium.org7c537e22008-10-16 08:43:32 +00005804void CodeGenerator::VisitThisFunction(ThisFunction* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005805#ifdef DEBUG
5806 int original_height = frame_->height();
5807#endif
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00005808 frame_->EmitPush(MemOperand(frame_->Function()));
ager@chromium.orgac091b72010-05-05 07:34:42 +00005809 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005810}
5811
5812
ager@chromium.org7c537e22008-10-16 08:43:32 +00005813void CodeGenerator::VisitCompareOperation(CompareOperation* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005814#ifdef DEBUG
5815 int original_height = frame_->height();
5816#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005817 Comment cmnt(masm_, "[ CompareOperation");
5818
ager@chromium.org357bf652010-04-12 11:30:10 +00005819 VirtualFrame::RegisterAllocationScope nonspilled_scope(this);
5820
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005821 // Get the expressions from the node.
5822 Expression* left = node->left();
5823 Expression* right = node->right();
5824 Token::Value op = node->op();
5825
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005826 // To make null checks efficient, we check if either left or right is the
5827 // literal 'null'. If so, we optimize the code by inlining a null check
5828 // instead of calling the (very) general runtime routine for checking
5829 // equality.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005830 if (op == Token::EQ || op == Token::EQ_STRICT) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005831 bool left_is_null =
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005832 left->AsLiteral() != NULL && left->AsLiteral()->IsNull();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005833 bool right_is_null =
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005834 right->AsLiteral() != NULL && right->AsLiteral()->IsNull();
5835 // The 'null' value can only be equal to 'null' or 'undefined'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005836 if (left_is_null || right_is_null) {
ager@chromium.org357bf652010-04-12 11:30:10 +00005837 Load(left_is_null ? right : left);
5838 Register tos = frame_->PopToRegister();
ager@chromium.orgab99eea2009-08-25 07:05:41 +00005839 __ LoadRoot(ip, Heap::kNullValueRootIndex);
ager@chromium.org357bf652010-04-12 11:30:10 +00005840 __ cmp(tos, ip);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005841
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005842 // The 'null' value is only equal to 'undefined' if using non-strict
5843 // comparisons.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005844 if (op != Token::EQ_STRICT) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005845 true_target()->Branch(eq);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005846
ager@chromium.orgab99eea2009-08-25 07:05:41 +00005847 __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
ager@chromium.org357bf652010-04-12 11:30:10 +00005848 __ cmp(tos, Operand(ip));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005849 true_target()->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005850
ager@chromium.org357bf652010-04-12 11:30:10 +00005851 __ tst(tos, Operand(kSmiTagMask));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005852 false_target()->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005853
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005854 // It can be an undetectable object.
ager@chromium.org357bf652010-04-12 11:30:10 +00005855 __ ldr(tos, FieldMemOperand(tos, HeapObject::kMapOffset));
5856 __ ldrb(tos, FieldMemOperand(tos, Map::kBitFieldOffset));
5857 __ and_(tos, tos, Operand(1 << Map::kIsUndetectable));
5858 __ cmp(tos, Operand(1 << Map::kIsUndetectable));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005859 }
5860
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005861 cc_reg_ = eq;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005862 ASSERT(has_cc() && frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005863 return;
5864 }
5865 }
5866
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005867 // To make typeof testing for natives implemented in JavaScript really
5868 // efficient, we generate special code for expressions of the form:
5869 // 'typeof <expression> == <string>'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005870 UnaryOperation* operation = left->AsUnaryOperation();
5871 if ((op == Token::EQ || op == Token::EQ_STRICT) &&
5872 (operation != NULL && operation->op() == Token::TYPEOF) &&
5873 (right->AsLiteral() != NULL &&
5874 right->AsLiteral()->handle()->IsString())) {
5875 Handle<String> check(String::cast(*right->AsLiteral()->handle()));
5876
ager@chromium.org357bf652010-04-12 11:30:10 +00005877 // Load the operand, move it to a register.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005878 LoadTypeofExpression(operation->expression());
ager@chromium.org357bf652010-04-12 11:30:10 +00005879 Register tos = frame_->PopToRegister();
5880
ager@chromium.org357bf652010-04-12 11:30:10 +00005881 Register scratch = VirtualFrame::scratch0();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005882
5883 if (check->Equals(Heap::number_symbol())) {
ager@chromium.org357bf652010-04-12 11:30:10 +00005884 __ tst(tos, Operand(kSmiTagMask));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005885 true_target()->Branch(eq);
ager@chromium.org357bf652010-04-12 11:30:10 +00005886 __ ldr(tos, FieldMemOperand(tos, HeapObject::kMapOffset));
ager@chromium.orgab99eea2009-08-25 07:05:41 +00005887 __ LoadRoot(ip, Heap::kHeapNumberMapRootIndex);
ager@chromium.org357bf652010-04-12 11:30:10 +00005888 __ cmp(tos, ip);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005889 cc_reg_ = eq;
5890
5891 } else if (check->Equals(Heap::string_symbol())) {
ager@chromium.org357bf652010-04-12 11:30:10 +00005892 __ tst(tos, Operand(kSmiTagMask));
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 __ ldr(tos, FieldMemOperand(tos, HeapObject::kMapOffset));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005896
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005897 // It can be an undetectable string object.
ager@chromium.org357bf652010-04-12 11:30:10 +00005898 __ ldrb(scratch, FieldMemOperand(tos, Map::kBitFieldOffset));
5899 __ and_(scratch, scratch, Operand(1 << Map::kIsUndetectable));
5900 __ cmp(scratch, Operand(1 << Map::kIsUndetectable));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005901 false_target()->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005902
ager@chromium.org357bf652010-04-12 11:30:10 +00005903 __ ldrb(scratch, FieldMemOperand(tos, Map::kInstanceTypeOffset));
5904 __ cmp(scratch, Operand(FIRST_NONSTRING_TYPE));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005905 cc_reg_ = lt;
5906
5907 } else if (check->Equals(Heap::boolean_symbol())) {
ager@chromium.orgab99eea2009-08-25 07:05:41 +00005908 __ LoadRoot(ip, Heap::kTrueValueRootIndex);
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);
ager@chromium.orgab99eea2009-08-25 07:05:41 +00005911 __ LoadRoot(ip, Heap::kFalseValueRootIndex);
ager@chromium.org357bf652010-04-12 11:30:10 +00005912 __ cmp(tos, ip);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005913 cc_reg_ = eq;
5914
5915 } else if (check->Equals(Heap::undefined_symbol())) {
ager@chromium.orgab99eea2009-08-25 07:05:41 +00005916 __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
ager@chromium.org357bf652010-04-12 11:30:10 +00005917 __ cmp(tos, ip);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005918 true_target()->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005919
ager@chromium.org357bf652010-04-12 11:30:10 +00005920 __ tst(tos, Operand(kSmiTagMask));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005921 false_target()->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005922
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005923 // It can be an undetectable object.
ager@chromium.org357bf652010-04-12 11:30:10 +00005924 __ ldr(tos, FieldMemOperand(tos, HeapObject::kMapOffset));
5925 __ ldrb(scratch, FieldMemOperand(tos, Map::kBitFieldOffset));
5926 __ and_(scratch, scratch, Operand(1 << Map::kIsUndetectable));
5927 __ cmp(scratch, Operand(1 << Map::kIsUndetectable));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005928
5929 cc_reg_ = eq;
5930
5931 } else if (check->Equals(Heap::function_symbol())) {
ager@chromium.org357bf652010-04-12 11:30:10 +00005932 __ tst(tos, Operand(kSmiTagMask));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005933 false_target()->Branch(eq);
ager@chromium.org357bf652010-04-12 11:30:10 +00005934 Register map_reg = scratch;
5935 __ CompareObjectType(tos, map_reg, tos, JS_FUNCTION_TYPE);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005936 true_target()->Branch(eq);
5937 // Regular expressions are callable so typeof == 'function'.
ager@chromium.org357bf652010-04-12 11:30:10 +00005938 __ CompareInstanceType(map_reg, tos, JS_REGEXP_TYPE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005939 cc_reg_ = eq;
5940
5941 } else if (check->Equals(Heap::object_symbol())) {
ager@chromium.org357bf652010-04-12 11:30:10 +00005942 __ tst(tos, Operand(kSmiTagMask));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005943 false_target()->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005944
ager@chromium.orgab99eea2009-08-25 07:05:41 +00005945 __ LoadRoot(ip, Heap::kNullValueRootIndex);
ager@chromium.org357bf652010-04-12 11:30:10 +00005946 __ cmp(tos, ip);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005947 true_target()->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005948
ager@chromium.org357bf652010-04-12 11:30:10 +00005949 Register map_reg = scratch;
5950 __ CompareObjectType(tos, map_reg, tos, JS_REGEXP_TYPE);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005951 false_target()->Branch(eq);
5952
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005953 // It can be an undetectable object.
ager@chromium.org357bf652010-04-12 11:30:10 +00005954 __ ldrb(tos, FieldMemOperand(map_reg, Map::kBitFieldOffset));
5955 __ and_(tos, tos, Operand(1 << Map::kIsUndetectable));
5956 __ cmp(tos, Operand(1 << Map::kIsUndetectable));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005957 false_target()->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005958
ager@chromium.org357bf652010-04-12 11:30:10 +00005959 __ ldrb(tos, FieldMemOperand(map_reg, Map::kInstanceTypeOffset));
5960 __ cmp(tos, Operand(FIRST_JS_OBJECT_TYPE));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005961 false_target()->Branch(lt);
ager@chromium.org357bf652010-04-12 11:30:10 +00005962 __ cmp(tos, Operand(LAST_JS_OBJECT_TYPE));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005963 cc_reg_ = le;
5964
5965 } else {
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005966 // Uncommon case: typeof testing against a string literal that is
5967 // never returned from the typeof operator.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005968 false_target()->Jump();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005969 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005970 ASSERT(!has_valid_frame() ||
5971 (has_cc() && frame_->height() == original_height));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005972 return;
5973 }
5974
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005975 switch (op) {
5976 case Token::EQ:
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005977 Comparison(eq, left, right, false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005978 break;
5979
5980 case Token::LT:
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005981 Comparison(lt, left, right);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005982 break;
5983
5984 case Token::GT:
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005985 Comparison(gt, left, right);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005986 break;
5987
5988 case Token::LTE:
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005989 Comparison(le, left, right);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005990 break;
5991
5992 case Token::GTE:
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005993 Comparison(ge, left, right);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005994 break;
5995
5996 case Token::EQ_STRICT:
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005997 Comparison(eq, left, right, true);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005998 break;
5999
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006000 case Token::IN: {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006001 Load(left);
6002 Load(right);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00006003 frame_->InvokeBuiltin(Builtins::IN, CALL_JS, 2);
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00006004 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006005 break;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006006 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006007
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006008 case Token::INSTANCEOF: {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006009 Load(left);
6010 Load(right);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006011 InstanceofStub stub;
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00006012 frame_->CallStub(&stub, 2);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006013 // At this point if instanceof succeeded then r0 == 0.
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00006014 __ tst(r0, Operand(r0));
ager@chromium.org7c537e22008-10-16 08:43:32 +00006015 cc_reg_ = eq;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006016 break;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006017 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006018
6019 default:
6020 UNREACHABLE();
6021 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006022 ASSERT((has_cc() && frame_->height() == original_height) ||
6023 (!has_cc() && frame_->height() == original_height + 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006024}
6025
6026
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006027class DeferredReferenceGetNamedValue: public DeferredCode {
6028 public:
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006029 explicit DeferredReferenceGetNamedValue(Register receiver,
6030 Handle<String> name)
6031 : receiver_(receiver), name_(name) {
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006032 set_comment("[ DeferredReferenceGetNamedValue");
6033 }
6034
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006035 virtual void Generate();
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006036
6037 private:
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006038 Register receiver_;
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006039 Handle<String> name_;
6040};
6041
6042
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006043// Convention for this is that on entry the receiver is in a register that
6044// is not used by the stack. On exit the answer is found in that same
6045// register and the stack has the same height.
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006046void DeferredReferenceGetNamedValue::Generate() {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006047#ifdef DEBUG
6048 int expected_height = frame_state()->frame()->height();
6049#endif
6050 VirtualFrame copied_frame(*frame_state()->frame());
6051 copied_frame.SpillAll();
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006052
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006053 Register scratch1 = VirtualFrame::scratch0();
6054 Register scratch2 = VirtualFrame::scratch1();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006055 ASSERT(!receiver_.is(scratch1) && !receiver_.is(scratch2));
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006056 __ DecrementCounter(&Counters::named_load_inline, 1, scratch1, scratch2);
6057 __ IncrementCounter(&Counters::named_load_inline_miss, 1, scratch1, scratch2);
6058
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006059 // Ensure receiver in r0 and name in r2 to match load ic calling convention.
6060 __ Move(r0, receiver_);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006061 __ mov(r2, Operand(name_));
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006062
6063 // The rest of the instructions in the deferred code must be together.
6064 { Assembler::BlockConstPoolScope block_const_pool(masm_);
6065 Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize));
6066 __ Call(ic, RelocInfo::CODE_TARGET);
6067 // The call must be followed by a nop(1) instruction to indicate that the
6068 // in-object has been inlined.
6069 __ nop(PROPERTY_ACCESS_INLINED);
6070
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006071 // At this point the answer is in r0. We move it to the expected register
6072 // if necessary.
6073 __ Move(receiver_, r0);
6074
6075 // Now go back to the frame that we entered with. This will not overwrite
6076 // the receiver register since that register was not in use when we came
6077 // in. The instructions emitted by this merge are skipped over by the
6078 // inline load patching mechanism when looking for the branch instruction
6079 // that tells it where the code to patch is.
6080 copied_frame.MergeTo(frame_state()->frame());
6081
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006082 // Block the constant pool for one more instruction after leaving this
6083 // constant pool block scope to include the branch instruction ending the
6084 // deferred code.
6085 __ BlockConstPoolFor(1);
6086 }
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006087 ASSERT_EQ(expected_height, frame_state()->frame()->height());
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006088}
6089
6090
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006091class DeferredReferenceGetKeyedValue: public DeferredCode {
6092 public:
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00006093 DeferredReferenceGetKeyedValue(Register key, Register receiver)
6094 : key_(key), receiver_(receiver) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006095 set_comment("[ DeferredReferenceGetKeyedValue");
6096 }
6097
6098 virtual void Generate();
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00006099
6100 private:
6101 Register key_;
6102 Register receiver_;
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006103};
6104
6105
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00006106// Takes key and register in r0 and r1 or vice versa. Returns result
6107// in r0.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006108void DeferredReferenceGetKeyedValue::Generate() {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00006109 ASSERT((key_.is(r0) && receiver_.is(r1)) ||
6110 (key_.is(r1) && receiver_.is(r0)));
6111
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00006112 VirtualFrame copied_frame(*frame_state()->frame());
6113 copied_frame.SpillAll();
6114
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006115 Register scratch1 = VirtualFrame::scratch0();
6116 Register scratch2 = VirtualFrame::scratch1();
6117 __ DecrementCounter(&Counters::keyed_load_inline, 1, scratch1, scratch2);
6118 __ IncrementCounter(&Counters::keyed_load_inline_miss, 1, scratch1, scratch2);
6119
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00006120 // Ensure key in r0 and receiver in r1 to match keyed load ic calling
6121 // convention.
6122 if (key_.is(r1)) {
6123 __ Swap(r0, r1, ip);
6124 }
6125
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006126 // The rest of the instructions in the deferred code must be together.
6127 { Assembler::BlockConstPoolScope block_const_pool(masm_);
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00006128 // Call keyed load IC. It has the arguments key and receiver in r0 and r1.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006129 Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
6130 __ Call(ic, RelocInfo::CODE_TARGET);
6131 // The call must be followed by a nop instruction to indicate that the
6132 // keyed load has been inlined.
6133 __ nop(PROPERTY_ACCESS_INLINED);
6134
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00006135 // Now go back to the frame that we entered with. This will not overwrite
6136 // the receiver or key registers since they were not in use when we came
6137 // in. The instructions emitted by this merge are skipped over by the
6138 // inline load patching mechanism when looking for the branch instruction
6139 // that tells it where the code to patch is.
6140 copied_frame.MergeTo(frame_state()->frame());
6141
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006142 // Block the constant pool for one more instruction after leaving this
6143 // constant pool block scope to include the branch instruction ending the
6144 // deferred code.
6145 __ BlockConstPoolFor(1);
6146 }
6147}
6148
6149
6150class DeferredReferenceSetKeyedValue: public DeferredCode {
6151 public:
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006152 DeferredReferenceSetKeyedValue(Register value,
6153 Register key,
6154 Register receiver)
6155 : value_(value), key_(key), receiver_(receiver) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006156 set_comment("[ DeferredReferenceSetKeyedValue");
6157 }
6158
6159 virtual void Generate();
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006160
6161 private:
6162 Register value_;
6163 Register key_;
6164 Register receiver_;
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006165};
6166
6167
6168void DeferredReferenceSetKeyedValue::Generate() {
6169 Register scratch1 = VirtualFrame::scratch0();
6170 Register scratch2 = VirtualFrame::scratch1();
6171 __ DecrementCounter(&Counters::keyed_store_inline, 1, scratch1, scratch2);
6172 __ IncrementCounter(
6173 &Counters::keyed_store_inline_miss, 1, scratch1, scratch2);
6174
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006175 // Ensure value in r0, key in r1 and receiver in r2 to match keyed store ic
6176 // calling convention.
6177 if (value_.is(r1)) {
6178 __ Swap(r0, r1, ip);
6179 }
6180 ASSERT(receiver_.is(r2));
6181
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006182 // The rest of the instructions in the deferred code must be together.
6183 { Assembler::BlockConstPoolScope block_const_pool(masm_);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006184 // Call keyed store IC. It has the arguments value, key and receiver in r0,
6185 // r1 and r2.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006186 Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize));
6187 __ Call(ic, RelocInfo::CODE_TARGET);
6188 // The call must be followed by a nop instruction to indicate that the
6189 // keyed store has been inlined.
6190 __ nop(PROPERTY_ACCESS_INLINED);
6191
6192 // Block the constant pool for one more instruction after leaving this
6193 // constant pool block scope to include the branch instruction ending the
6194 // deferred code.
6195 __ BlockConstPoolFor(1);
6196 }
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006197}
6198
6199
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006200// Consumes the top of stack (the receiver) and pushes the result instead.
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006201void CodeGenerator::EmitNamedLoad(Handle<String> name, bool is_contextual) {
6202 if (is_contextual || scope()->is_global_scope() || loop_nesting() == 0) {
6203 Comment cmnt(masm(), "[ Load from named Property");
6204 // Setup the name register and call load IC.
ager@chromium.orgac091b72010-05-05 07:34:42 +00006205 frame_->CallLoadIC(name,
6206 is_contextual
6207 ? RelocInfo::CODE_TARGET_CONTEXT
6208 : RelocInfo::CODE_TARGET);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006209 frame_->EmitPush(r0); // Push answer.
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006210 } else {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006211 // Inline the in-object property case.
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006212 Comment cmnt(masm(), "[ Inlined named property load");
6213
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006214 // Counter will be decremented in the deferred code. Placed here to avoid
6215 // having it in the instruction stream below where patching will occur.
6216 __ IncrementCounter(&Counters::named_load_inline, 1,
6217 frame_->scratch0(), frame_->scratch1());
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006218
6219 // The following instructions are the inlined load of an in-object property.
6220 // Parts of this code is patched, so the exact instructions generated needs
6221 // to be fixed. Therefore the instruction pool is blocked when generating
6222 // this code
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006223
6224 // Load the receiver from the stack.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006225 Register receiver = frame_->PopToRegister();
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006226
6227 DeferredReferenceGetNamedValue* deferred =
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006228 new DeferredReferenceGetNamedValue(receiver, name);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006229
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006230#ifdef DEBUG
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006231 int kInlinedNamedLoadInstructions = 7;
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006232 Label check_inlined_codesize;
6233 masm_->bind(&check_inlined_codesize);
6234#endif
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006235
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006236 { Assembler::BlockConstPoolScope block_const_pool(masm_);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006237 // Check that the receiver is a heap object.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006238 __ tst(receiver, Operand(kSmiTagMask));
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006239 deferred->Branch(eq);
6240
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006241 Register scratch = VirtualFrame::scratch0();
6242 Register scratch2 = VirtualFrame::scratch1();
6243
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006244 // Check the map. The null map used below is patched by the inline cache
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006245 // code. Therefore we can't use a LoadRoot call.
6246 __ ldr(scratch, FieldMemOperand(receiver, HeapObject::kMapOffset));
6247 __ mov(scratch2, Operand(Factory::null_value()));
6248 __ cmp(scratch, scratch2);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006249 deferred->Branch(ne);
6250
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006251 // Initially use an invalid index. The index will be patched by the
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006252 // inline cache code.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006253 __ ldr(receiver, MemOperand(receiver, 0));
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006254
6255 // Make sure that the expected number of instructions are generated.
6256 ASSERT_EQ(kInlinedNamedLoadInstructions,
6257 masm_->InstructionsGeneratedSince(&check_inlined_codesize));
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006258 }
6259
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006260 deferred->BindExit();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006261 // At this point the receiver register has the result, either from the
6262 // deferred code or from the inlined code.
6263 frame_->EmitPush(receiver);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006264 }
6265}
6266
6267
ager@chromium.orgac091b72010-05-05 07:34:42 +00006268void CodeGenerator::EmitNamedStore(Handle<String> name, bool is_contextual) {
6269#ifdef DEBUG
6270 int expected_height = frame_->height() - (is_contextual ? 1 : 2);
6271#endif
6272 frame_->CallStoreIC(name, is_contextual);
6273
6274 ASSERT_EQ(expected_height, frame_->height());
6275}
6276
6277
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006278void CodeGenerator::EmitKeyedLoad() {
6279 if (loop_nesting() == 0) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006280 Comment cmnt(masm_, "[ Load from keyed property");
6281 frame_->CallKeyedLoadIC();
6282 } else {
6283 // Inline the keyed load.
6284 Comment cmnt(masm_, "[ Inlined load from keyed property");
6285
6286 // Counter will be decremented in the deferred code. Placed here to avoid
6287 // having it in the instruction stream below where patching will occur.
6288 __ IncrementCounter(&Counters::keyed_load_inline, 1,
6289 frame_->scratch0(), frame_->scratch1());
6290
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00006291 // Load the key and receiver from the stack.
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00006292 bool key_is_known_smi = frame_->KnownSmiAt(0);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00006293 Register key = frame_->PopToRegister();
6294 Register receiver = frame_->PopToRegister(key);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006295
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00006296 // The deferred code expects key and receiver in registers.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006297 DeferredReferenceGetKeyedValue* deferred =
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00006298 new DeferredReferenceGetKeyedValue(key, receiver);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006299
6300 // Check that the receiver is a heap object.
6301 __ tst(receiver, Operand(kSmiTagMask));
6302 deferred->Branch(eq);
6303
6304 // The following instructions are the part of the inlined load keyed
6305 // property code which can be patched. Therefore the exact number of
6306 // instructions generated need to be fixed, so the constant pool is blocked
6307 // while generating this code.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006308 { Assembler::BlockConstPoolScope block_const_pool(masm_);
6309 Register scratch1 = VirtualFrame::scratch0();
6310 Register scratch2 = VirtualFrame::scratch1();
6311 // Check the map. The null map used below is patched by the inline cache
6312 // code.
6313 __ ldr(scratch1, FieldMemOperand(receiver, HeapObject::kMapOffset));
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00006314
6315 // Check that the key is a smi.
6316 if (!key_is_known_smi) {
6317 __ tst(key, Operand(kSmiTagMask));
6318 deferred->Branch(ne);
6319 }
6320
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00006321#ifdef DEBUG
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00006322 Label check_inlined_codesize;
6323 masm_->bind(&check_inlined_codesize);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00006324#endif
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006325 __ mov(scratch2, Operand(Factory::null_value()));
6326 __ cmp(scratch1, scratch2);
6327 deferred->Branch(ne);
6328
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006329 // Get the elements array from the receiver and check that it
6330 // is not a dictionary.
6331 __ ldr(scratch1, FieldMemOperand(receiver, JSObject::kElementsOffset));
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00006332 if (FLAG_debug_code) {
6333 __ ldr(scratch2, FieldMemOperand(scratch1, JSObject::kMapOffset));
6334 __ LoadRoot(ip, Heap::kFixedArrayMapRootIndex);
6335 __ cmp(scratch2, ip);
6336 __ Assert(eq, "JSObject with fast elements map has slow elements");
6337 }
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006338
6339 // Check that key is within bounds. Use unsigned comparison to handle
6340 // negative keys.
6341 __ ldr(scratch2, FieldMemOperand(scratch1, FixedArray::kLengthOffset));
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006342 __ cmp(scratch2, key);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006343 deferred->Branch(ls); // Unsigned less equal.
6344
6345 // Load and check that the result is not the hole (key is a smi).
6346 __ LoadRoot(scratch2, Heap::kTheHoleValueRootIndex);
6347 __ add(scratch1,
6348 scratch1,
6349 Operand(FixedArray::kHeaderSize - kHeapObjectTag));
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00006350 __ ldr(scratch1,
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006351 MemOperand(scratch1, key, LSL,
6352 kPointerSizeLog2 - (kSmiTagSize + kSmiShiftSize)));
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00006353 __ cmp(scratch1, scratch2);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006354 deferred->Branch(eq);
6355
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00006356 __ mov(r0, scratch1);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006357 // Make sure that the expected number of instructions are generated.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00006358 ASSERT_EQ(GetInlinedKeyedLoadInstructionsAfterPatch(),
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006359 masm_->InstructionsGeneratedSince(&check_inlined_codesize));
6360 }
6361
6362 deferred->BindExit();
6363 }
6364}
6365
6366
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00006367void CodeGenerator::EmitKeyedStore(StaticType* key_type,
6368 WriteBarrierCharacter wb_info) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006369 // Generate inlined version of the keyed store if the code is in a loop
6370 // and the key is likely to be a smi.
6371 if (loop_nesting() > 0 && key_type->IsLikelySmi()) {
6372 // Inline the keyed store.
6373 Comment cmnt(masm_, "[ Inlined store to keyed property");
6374
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006375 Register scratch1 = VirtualFrame::scratch0();
6376 Register scratch2 = VirtualFrame::scratch1();
6377 Register scratch3 = r3;
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006378
6379 // Counter will be decremented in the deferred code. Placed here to avoid
6380 // having it in the instruction stream below where patching will occur.
6381 __ IncrementCounter(&Counters::keyed_store_inline, 1,
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006382 scratch1, scratch2);
6383
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00006384
6385
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006386 // Load the value, key and receiver from the stack.
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00006387 bool value_is_harmless = frame_->KnownSmiAt(0);
6388 if (wb_info == NEVER_NEWSPACE) value_is_harmless = true;
6389 bool key_is_smi = frame_->KnownSmiAt(1);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006390 Register value = frame_->PopToRegister();
6391 Register key = frame_->PopToRegister(value);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00006392 VirtualFrame::SpilledScope spilled(frame_);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006393 Register receiver = r2;
6394 frame_->EmitPop(receiver);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006395
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00006396#ifdef DEBUG
6397 bool we_remembered_the_write_barrier = value_is_harmless;
6398#endif
6399
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006400 // The deferred code expects value, key and receiver in registers.
6401 DeferredReferenceSetKeyedValue* deferred =
6402 new DeferredReferenceSetKeyedValue(value, key, receiver);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006403
6404 // Check that the value is a smi. As this inlined code does not set the
6405 // write barrier it is only possible to store smi values.
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00006406 if (!value_is_harmless) {
6407 // If the value is not likely to be a Smi then let's test the fixed array
6408 // for new space instead. See below.
6409 if (wb_info == LIKELY_SMI) {
6410 __ tst(value, Operand(kSmiTagMask));
6411 deferred->Branch(ne);
6412#ifdef DEBUG
6413 we_remembered_the_write_barrier = true;
6414#endif
6415 }
6416 }
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006417
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00006418 if (!key_is_smi) {
6419 // Check that the key is a smi.
6420 __ tst(key, Operand(kSmiTagMask));
6421 deferred->Branch(ne);
6422 }
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006423
6424 // Check that the receiver is a heap object.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006425 __ tst(receiver, Operand(kSmiTagMask));
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006426 deferred->Branch(eq);
6427
6428 // Check that the receiver is a JSArray.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006429 __ CompareObjectType(receiver, scratch1, scratch1, JS_ARRAY_TYPE);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006430 deferred->Branch(ne);
6431
6432 // Check that the key is within bounds. Both the key and the length of
6433 // the JSArray are smis. Use unsigned comparison to handle negative keys.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006434 __ ldr(scratch1, FieldMemOperand(receiver, JSArray::kLengthOffset));
6435 __ cmp(scratch1, key);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006436 deferred->Branch(ls); // Unsigned less equal.
6437
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00006438 // Get the elements array from the receiver.
6439 __ ldr(scratch1, FieldMemOperand(receiver, JSObject::kElementsOffset));
6440 if (!value_is_harmless && wb_info != LIKELY_SMI) {
6441 Label ok;
6442 __ and_(scratch2, scratch1, Operand(ExternalReference::new_space_mask()));
6443 __ cmp(scratch2, Operand(ExternalReference::new_space_start()));
6444 __ tst(value, Operand(kSmiTagMask), ne);
6445 deferred->Branch(ne);
6446#ifdef DEBUG
6447 we_remembered_the_write_barrier = true;
6448#endif
6449 }
6450 // Check that the elements array is not a dictionary.
6451 __ ldr(scratch2, FieldMemOperand(scratch1, JSObject::kMapOffset));
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006452 // The following instructions are the part of the inlined store keyed
6453 // property code which can be patched. Therefore the exact number of
6454 // instructions generated need to be fixed, so the constant pool is blocked
6455 // while generating this code.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006456 { Assembler::BlockConstPoolScope block_const_pool(masm_);
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00006457#ifdef DEBUG
6458 Label check_inlined_codesize;
6459 masm_->bind(&check_inlined_codesize);
6460#endif
6461
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006462 // Read the fixed array map from the constant pool (not from the root
6463 // array) so that the value can be patched. When debugging, we patch this
6464 // comparison to always fail so that we will hit the IC call in the
6465 // deferred code which will allow the debugger to break for fast case
6466 // stores.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006467 __ mov(scratch3, Operand(Factory::fixed_array_map()));
6468 __ cmp(scratch2, scratch3);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006469 deferred->Branch(ne);
6470
6471 // Store the value.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006472 __ add(scratch1, scratch1,
6473 Operand(FixedArray::kHeaderSize - kHeapObjectTag));
6474 __ str(value,
6475 MemOperand(scratch1, key, LSL,
6476 kPointerSizeLog2 - (kSmiTagSize + kSmiShiftSize)));
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006477
6478 // Make sure that the expected number of instructions are generated.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006479 ASSERT_EQ(kInlinedKeyedStoreInstructionsAfterPatch,
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006480 masm_->InstructionsGeneratedSince(&check_inlined_codesize));
6481 }
6482
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00006483 ASSERT(we_remembered_the_write_barrier);
6484
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006485 deferred->BindExit();
6486 } else {
6487 frame()->CallKeyedStoreIC();
6488 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006489}
6490
6491
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006492#ifdef DEBUG
6493bool CodeGenerator::HasValidEntryRegisters() { return true; }
6494#endif
6495
6496
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006497#undef __
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006498#define __ ACCESS_MASM(masm)
6499
ager@chromium.org7c537e22008-10-16 08:43:32 +00006500Handle<String> Reference::GetName() {
6501 ASSERT(type_ == NAMED);
6502 Property* property = expression_->AsProperty();
6503 if (property == NULL) {
6504 // Global variable reference treated as a named property reference.
6505 VariableProxy* proxy = expression_->AsVariableProxy();
6506 ASSERT(proxy->AsVariable() != NULL);
6507 ASSERT(proxy->AsVariable()->is_global());
6508 return proxy->name();
6509 } else {
6510 Literal* raw_name = property->key()->AsLiteral();
6511 ASSERT(raw_name != NULL);
6512 return Handle<String>(String::cast(*raw_name->handle()));
6513 }
6514}
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006515
ager@chromium.org7c537e22008-10-16 08:43:32 +00006516
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006517void Reference::DupIfPersist() {
6518 if (persist_after_get_) {
6519 switch (type_) {
6520 case KEYED:
6521 cgen_->frame()->Dup2();
6522 break;
6523 case NAMED:
6524 cgen_->frame()->Dup();
6525 // Fall through.
6526 case UNLOADED:
6527 case ILLEGAL:
6528 case SLOT:
6529 // Do nothing.
6530 ;
6531 }
6532 } else {
6533 set_unloaded();
6534 }
6535}
6536
6537
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006538void Reference::GetValue() {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006539 ASSERT(cgen_->HasValidEntryRegisters());
ager@chromium.org7c537e22008-10-16 08:43:32 +00006540 ASSERT(!is_illegal());
6541 ASSERT(!cgen_->has_cc());
6542 MacroAssembler* masm = cgen_->masm();
6543 Property* property = expression_->AsProperty();
6544 if (property != NULL) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006545 cgen_->CodeForSourcePosition(property->position());
ager@chromium.org7c537e22008-10-16 08:43:32 +00006546 }
6547
6548 switch (type_) {
6549 case SLOT: {
6550 Comment cmnt(masm, "[ Load from Slot");
6551 Slot* slot = expression_->AsVariableProxy()->AsVariable()->slot();
6552 ASSERT(slot != NULL);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006553 DupIfPersist();
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006554 cgen_->LoadFromSlotCheckForArguments(slot, NOT_INSIDE_TYPEOF);
ager@chromium.org7c537e22008-10-16 08:43:32 +00006555 break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006556 }
6557
ager@chromium.org7c537e22008-10-16 08:43:32 +00006558 case NAMED: {
ager@chromium.org7c537e22008-10-16 08:43:32 +00006559 Variable* var = expression_->AsVariableProxy()->AsVariable();
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006560 bool is_global = var != NULL;
6561 ASSERT(!is_global || var->is_global());
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006562 Handle<String> name = GetName();
6563 DupIfPersist();
6564 cgen_->EmitNamedLoad(name, is_global);
ager@chromium.org7c537e22008-10-16 08:43:32 +00006565 break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006566 }
6567
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006568 case KEYED: {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006569 ASSERT(property != NULL);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006570 DupIfPersist();
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006571 cgen_->EmitKeyedLoad();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006572 cgen_->frame()->EmitPush(r0);
6573 break;
6574 }
6575
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006576 default:
6577 UNREACHABLE();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006578 }
6579}
6580
6581
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00006582void Reference::SetValue(InitState init_state, WriteBarrierCharacter wb_info) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00006583 ASSERT(!is_illegal());
6584 ASSERT(!cgen_->has_cc());
6585 MacroAssembler* masm = cgen_->masm();
ager@chromium.org3bf7b912008-11-17 09:09:45 +00006586 VirtualFrame* frame = cgen_->frame();
ager@chromium.org7c537e22008-10-16 08:43:32 +00006587 Property* property = expression_->AsProperty();
6588 if (property != NULL) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006589 cgen_->CodeForSourcePosition(property->position());
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006590 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006591
ager@chromium.org7c537e22008-10-16 08:43:32 +00006592 switch (type_) {
6593 case SLOT: {
6594 Comment cmnt(masm, "[ Store to Slot");
6595 Slot* slot = expression_->AsVariableProxy()->AsVariable()->slot();
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006596 cgen_->StoreToSlot(slot, init_state);
ager@chromium.orgac091b72010-05-05 07:34:42 +00006597 set_unloaded();
ager@chromium.org7c537e22008-10-16 08:43:32 +00006598 break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006599 }
6600
ager@chromium.org7c537e22008-10-16 08:43:32 +00006601 case NAMED: {
6602 Comment cmnt(masm, "[ Store to named Property");
ager@chromium.orgac091b72010-05-05 07:34:42 +00006603 cgen_->EmitNamedStore(GetName(), false);
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00006604 frame->EmitPush(r0);
ager@chromium.org5c838252010-02-19 08:53:10 +00006605 set_unloaded();
ager@chromium.org7c537e22008-10-16 08:43:32 +00006606 break;
6607 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006608
ager@chromium.org7c537e22008-10-16 08:43:32 +00006609 case KEYED: {
6610 Comment cmnt(masm, "[ Store to keyed Property");
6611 Property* property = expression_->AsProperty();
6612 ASSERT(property != NULL);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006613 cgen_->CodeForSourcePosition(property->position());
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00006614 cgen_->EmitKeyedStore(property->key()->type(), wb_info);
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00006615 frame->EmitPush(r0);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006616 set_unloaded();
ager@chromium.org7c537e22008-10-16 08:43:32 +00006617 break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006618 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00006619
6620 default:
6621 UNREACHABLE();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006622 }
6623}
6624
6625
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006626void FastNewClosureStub::Generate(MacroAssembler* masm) {
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006627 // Create a new closure from the given function info in new
6628 // space. Set the context to the current context in cp.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006629 Label gc;
6630
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006631 // Pop the function info from the stack.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006632 __ pop(r3);
6633
6634 // Attempt to allocate new JSFunction in new space.
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00006635 __ AllocateInNewSpace(JSFunction::kSize,
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006636 r0,
6637 r1,
6638 r2,
6639 &gc,
6640 TAG_OBJECT);
6641
6642 // Compute the function map in the current global context and set that
6643 // as the map of the allocated object.
6644 __ ldr(r2, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_INDEX)));
6645 __ ldr(r2, FieldMemOperand(r2, GlobalObject::kGlobalContextOffset));
6646 __ ldr(r2, MemOperand(r2, Context::SlotOffset(Context::FUNCTION_MAP_INDEX)));
6647 __ str(r2, FieldMemOperand(r0, HeapObject::kMapOffset));
6648
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006649 // Initialize the rest of the function. We don't have to update the
6650 // write barrier because the allocated object is in new space.
6651 __ LoadRoot(r1, Heap::kEmptyFixedArrayRootIndex);
6652 __ LoadRoot(r2, Heap::kTheHoleValueRootIndex);
6653 __ str(r1, FieldMemOperand(r0, JSObject::kPropertiesOffset));
6654 __ str(r1, FieldMemOperand(r0, JSObject::kElementsOffset));
6655 __ str(r2, FieldMemOperand(r0, JSFunction::kPrototypeOrInitialMapOffset));
6656 __ str(r3, FieldMemOperand(r0, JSFunction::kSharedFunctionInfoOffset));
6657 __ str(cp, FieldMemOperand(r0, JSFunction::kContextOffset));
6658 __ str(r1, FieldMemOperand(r0, JSFunction::kLiteralsOffset));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006659
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006660 // Return result. The argument function info has been popped already.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006661 __ Ret();
6662
6663 // Create a new closure through the slower runtime call.
6664 __ bind(&gc);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006665 __ Push(cp, r3);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006666 __ TailCallRuntime(Runtime::kNewClosure, 2, 1);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006667}
6668
6669
6670void FastNewContextStub::Generate(MacroAssembler* masm) {
6671 // Try to allocate the context in new space.
6672 Label gc;
6673 int length = slots_ + Context::MIN_CONTEXT_SLOTS;
6674
6675 // Attempt to allocate the context in new space.
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00006676 __ AllocateInNewSpace(FixedArray::SizeFor(length),
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006677 r0,
6678 r1,
6679 r2,
6680 &gc,
6681 TAG_OBJECT);
6682
6683 // Load the function from the stack.
ager@chromium.org5c838252010-02-19 08:53:10 +00006684 __ ldr(r3, MemOperand(sp, 0));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006685
6686 // Setup the object header.
6687 __ LoadRoot(r2, Heap::kContextMapRootIndex);
6688 __ str(r2, FieldMemOperand(r0, HeapObject::kMapOffset));
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006689 __ mov(r2, Operand(Smi::FromInt(length)));
6690 __ str(r2, FieldMemOperand(r0, FixedArray::kLengthOffset));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006691
6692 // Setup the fixed slots.
6693 __ mov(r1, Operand(Smi::FromInt(0)));
6694 __ str(r3, MemOperand(r0, Context::SlotOffset(Context::CLOSURE_INDEX)));
6695 __ str(r0, MemOperand(r0, Context::SlotOffset(Context::FCONTEXT_INDEX)));
6696 __ str(r1, MemOperand(r0, Context::SlotOffset(Context::PREVIOUS_INDEX)));
6697 __ str(r1, MemOperand(r0, Context::SlotOffset(Context::EXTENSION_INDEX)));
6698
6699 // Copy the global object from the surrounding context.
6700 __ ldr(r1, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_INDEX)));
6701 __ str(r1, MemOperand(r0, Context::SlotOffset(Context::GLOBAL_INDEX)));
6702
6703 // Initialize the rest of the slots to undefined.
6704 __ LoadRoot(r1, Heap::kUndefinedValueRootIndex);
6705 for (int i = Context::MIN_CONTEXT_SLOTS; i < length; i++) {
6706 __ str(r1, MemOperand(r0, Context::SlotOffset(i)));
6707 }
6708
6709 // Remove the on-stack argument and return.
6710 __ mov(cp, r0);
6711 __ pop();
6712 __ Ret();
6713
6714 // Need to collect. Call into runtime system.
6715 __ bind(&gc);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006716 __ TailCallRuntime(Runtime::kNewContext, 1, 1);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006717}
6718
6719
ager@chromium.org5c838252010-02-19 08:53:10 +00006720void FastCloneShallowArrayStub::Generate(MacroAssembler* masm) {
6721 // Stack layout on entry:
6722 //
6723 // [sp]: constant elements.
6724 // [sp + kPointerSize]: literal index.
6725 // [sp + (2 * kPointerSize)]: literals array.
6726
6727 // All sizes here are multiples of kPointerSize.
6728 int elements_size = (length_ > 0) ? FixedArray::SizeFor(length_) : 0;
6729 int size = JSArray::kSize + elements_size;
6730
6731 // Load boilerplate object into r3 and check if we need to create a
6732 // boilerplate.
6733 Label slow_case;
6734 __ ldr(r3, MemOperand(sp, 2 * kPointerSize));
6735 __ ldr(r0, MemOperand(sp, 1 * kPointerSize));
6736 __ add(r3, r3, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
6737 __ ldr(r3, MemOperand(r3, r0, LSL, kPointerSizeLog2 - kSmiTagSize));
6738 __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
6739 __ cmp(r3, ip);
6740 __ b(eq, &slow_case);
6741
6742 // Allocate both the JS array and the elements array in one big
6743 // allocation. This avoids multiple limit checks.
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00006744 __ AllocateInNewSpace(size,
ager@chromium.org5c838252010-02-19 08:53:10 +00006745 r0,
6746 r1,
6747 r2,
6748 &slow_case,
6749 TAG_OBJECT);
6750
6751 // Copy the JS array part.
6752 for (int i = 0; i < JSArray::kSize; i += kPointerSize) {
6753 if ((i != JSArray::kElementsOffset) || (length_ == 0)) {
6754 __ ldr(r1, FieldMemOperand(r3, i));
6755 __ str(r1, FieldMemOperand(r0, i));
6756 }
6757 }
6758
6759 if (length_ > 0) {
6760 // Get hold of the elements array of the boilerplate and setup the
6761 // elements pointer in the resulting object.
6762 __ ldr(r3, FieldMemOperand(r3, JSArray::kElementsOffset));
6763 __ add(r2, r0, Operand(JSArray::kSize));
6764 __ str(r2, FieldMemOperand(r0, JSArray::kElementsOffset));
6765
6766 // Copy the elements array.
6767 for (int i = 0; i < elements_size; i += kPointerSize) {
6768 __ ldr(r1, FieldMemOperand(r3, i));
6769 __ str(r1, FieldMemOperand(r2, i));
6770 }
6771 }
6772
6773 // Return and remove the on-stack parameters.
6774 __ add(sp, sp, Operand(3 * kPointerSize));
6775 __ Ret();
6776
6777 __ bind(&slow_case);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006778 __ TailCallRuntime(Runtime::kCreateArrayLiteralShallow, 3, 1);
ager@chromium.org5c838252010-02-19 08:53:10 +00006779}
6780
6781
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006782// Takes a Smi and converts to an IEEE 64 bit floating point value in two
6783// registers. The format is 1 sign bit, 11 exponent bits (biased 1023) and
6784// 52 fraction bits (20 in the first word, 32 in the second). Zeros is a
6785// scratch register. Destroys the source register. No GC occurs during this
6786// stub so you don't have to set up the frame.
6787class ConvertToDoubleStub : public CodeStub {
6788 public:
6789 ConvertToDoubleStub(Register result_reg_1,
6790 Register result_reg_2,
6791 Register source_reg,
6792 Register scratch_reg)
6793 : result1_(result_reg_1),
6794 result2_(result_reg_2),
6795 source_(source_reg),
6796 zeros_(scratch_reg) { }
6797
6798 private:
6799 Register result1_;
6800 Register result2_;
6801 Register source_;
6802 Register zeros_;
6803
6804 // Minor key encoding in 16 bits.
6805 class ModeBits: public BitField<OverwriteMode, 0, 2> {};
6806 class OpBits: public BitField<Token::Value, 2, 14> {};
6807
6808 Major MajorKey() { return ConvertToDouble; }
6809 int MinorKey() {
6810 // Encode the parameters in a unique 16 bit value.
6811 return result1_.code() +
6812 (result2_.code() << 4) +
6813 (source_.code() << 8) +
6814 (zeros_.code() << 12);
6815 }
6816
6817 void Generate(MacroAssembler* masm);
6818
6819 const char* GetName() { return "ConvertToDoubleStub"; }
6820
6821#ifdef DEBUG
6822 void Print() { PrintF("ConvertToDoubleStub\n"); }
6823#endif
6824};
6825
6826
6827void ConvertToDoubleStub::Generate(MacroAssembler* masm) {
6828#ifndef BIG_ENDIAN_FLOATING_POINT
6829 Register exponent = result1_;
6830 Register mantissa = result2_;
6831#else
6832 Register exponent = result2_;
6833 Register mantissa = result1_;
6834#endif
6835 Label not_special;
6836 // Convert from Smi to integer.
6837 __ mov(source_, Operand(source_, ASR, kSmiTagSize));
6838 // Move sign bit from source to destination. This works because the sign bit
6839 // in the exponent word of the double has the same position and polarity as
6840 // the 2's complement sign bit in a Smi.
6841 ASSERT(HeapNumber::kSignMask == 0x80000000u);
6842 __ and_(exponent, source_, Operand(HeapNumber::kSignMask), SetCC);
6843 // Subtract from 0 if source was negative.
6844 __ rsb(source_, source_, Operand(0), LeaveCC, ne);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006845
6846 // We have -1, 0 or 1, which we treat specially. Register source_ contains
6847 // absolute value: it is either equal to 1 (special case of -1 and 1),
6848 // greater than 1 (not a special case) or less than 1 (special case of 0).
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006849 __ cmp(source_, Operand(1));
6850 __ b(gt, &not_special);
6851
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006852 // For 1 or -1 we need to or in the 0 exponent (biased to 1023).
6853 static const uint32_t exponent_word_for_1 =
6854 HeapNumber::kExponentBias << HeapNumber::kExponentShift;
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006855 __ orr(exponent, exponent, Operand(exponent_word_for_1), LeaveCC, eq);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006856 // 1, 0 and -1 all have 0 for the second word.
6857 __ mov(mantissa, Operand(0));
6858 __ Ret();
6859
6860 __ bind(&not_special);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006861 // Count leading zeros. Uses mantissa for a scratch register on pre-ARM5.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006862 // Gets the wrong answer for 0, but we already checked for that case above.
lrn@chromium.org32d961d2010-06-30 09:09:34 +00006863 __ CountLeadingZeros(zeros_, source_, mantissa);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006864 // Compute exponent and or it into the exponent register.
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00006865 // We use mantissa as a scratch register here. Use a fudge factor to
6866 // divide the constant 31 + HeapNumber::kExponentBias, 0x41d, into two parts
6867 // that fit in the ARM's constant field.
6868 int fudge = 0x400;
6869 __ rsb(mantissa, zeros_, Operand(31 + HeapNumber::kExponentBias - fudge));
6870 __ add(mantissa, mantissa, Operand(fudge));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006871 __ orr(exponent,
6872 exponent,
6873 Operand(mantissa, LSL, HeapNumber::kExponentShift));
6874 // Shift up the source chopping the top bit off.
6875 __ add(zeros_, zeros_, Operand(1));
6876 // This wouldn't work for 1.0 or -1.0 as the shift would be 32 which means 0.
6877 __ mov(source_, Operand(source_, LSL, zeros_));
6878 // Compute lower part of fraction (last 12 bits).
6879 __ mov(mantissa, Operand(source_, LSL, HeapNumber::kMantissaBitsInTopWord));
6880 // And the top (top 20 bits).
6881 __ orr(exponent,
6882 exponent,
6883 Operand(source_, LSR, 32 - HeapNumber::kMantissaBitsInTopWord));
6884 __ Ret();
6885}
6886
6887
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006888// See comment for class.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006889void WriteInt32ToHeapNumberStub::Generate(MacroAssembler* masm) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006890 Label max_negative_int;
6891 // the_int_ has the answer which is a signed int32 but not a Smi.
6892 // We test for the special value that has a different exponent. This test
6893 // has the neat side effect of setting the flags according to the sign.
6894 ASSERT(HeapNumber::kSignMask == 0x80000000u);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00006895 __ cmp(the_int_, Operand(0x80000000u));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006896 __ b(eq, &max_negative_int);
6897 // Set up the correct exponent in scratch_. All non-Smi int32s have the same.
6898 // A non-Smi integer is 1.xxx * 2^30 so the exponent is 30 (biased).
6899 uint32_t non_smi_exponent =
6900 (HeapNumber::kExponentBias + 30) << HeapNumber::kExponentShift;
6901 __ mov(scratch_, Operand(non_smi_exponent));
6902 // Set the sign bit in scratch_ if the value was negative.
6903 __ orr(scratch_, scratch_, Operand(HeapNumber::kSignMask), LeaveCC, cs);
6904 // Subtract from 0 if the value was negative.
6905 __ rsb(the_int_, the_int_, Operand(0), LeaveCC, cs);
6906 // We should be masking the implict first digit of the mantissa away here,
6907 // but it just ends up combining harmlessly with the last digit of the
6908 // exponent that happens to be 1. The sign bit is 0 so we shift 10 to get
6909 // the most significant 1 to hit the last bit of the 12 bit sign and exponent.
6910 ASSERT(((1 << HeapNumber::kExponentShift) & non_smi_exponent) != 0);
6911 const int shift_distance = HeapNumber::kNonMantissaBitsInTopWord - 2;
6912 __ orr(scratch_, scratch_, Operand(the_int_, LSR, shift_distance));
6913 __ str(scratch_, FieldMemOperand(the_heap_number_,
6914 HeapNumber::kExponentOffset));
6915 __ mov(scratch_, Operand(the_int_, LSL, 32 - shift_distance));
6916 __ str(scratch_, FieldMemOperand(the_heap_number_,
6917 HeapNumber::kMantissaOffset));
6918 __ Ret();
6919
6920 __ bind(&max_negative_int);
6921 // The max negative int32 is stored as a positive number in the mantissa of
6922 // a double because it uses a sign bit instead of using two's complement.
6923 // The actual mantissa bits stored are all 0 because the implicit most
6924 // significant 1 bit is not stored.
6925 non_smi_exponent += 1 << HeapNumber::kExponentShift;
6926 __ mov(ip, Operand(HeapNumber::kSignMask | non_smi_exponent));
6927 __ str(ip, FieldMemOperand(the_heap_number_, HeapNumber::kExponentOffset));
6928 __ mov(ip, Operand(0));
6929 __ str(ip, FieldMemOperand(the_heap_number_, HeapNumber::kMantissaOffset));
6930 __ Ret();
6931}
6932
6933
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006934// Handle the case where the lhs and rhs are the same object.
6935// Equality is almost reflexive (everything but NaN), so this is a test
6936// for "identity and not NaN".
6937static void EmitIdenticalObjectComparison(MacroAssembler* masm,
6938 Label* slow,
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006939 Condition cc,
6940 bool never_nan_nan) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006941 Label not_identical;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006942 Label heap_number, return_equal;
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006943 __ cmp(r0, r1);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006944 __ b(ne, &not_identical);
6945
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006946 // The two objects are identical. If we know that one of them isn't NaN then
6947 // we now know they test equal.
6948 if (cc != eq || !never_nan_nan) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006949 // Test for NaN. Sadly, we can't just compare to Factory::nan_value(),
6950 // so we do the second best thing - test it ourselves.
6951 // They are both equal and they are not both Smis so both of them are not
6952 // Smis. If it's not a heap number, then return equal.
6953 if (cc == lt || cc == gt) {
6954 __ CompareObjectType(r0, r4, r4, FIRST_JS_OBJECT_TYPE);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006955 __ b(ge, slow);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006956 } else {
6957 __ CompareObjectType(r0, r4, r4, HEAP_NUMBER_TYPE);
6958 __ b(eq, &heap_number);
6959 // Comparing JS objects with <=, >= is complicated.
6960 if (cc != eq) {
6961 __ cmp(r4, Operand(FIRST_JS_OBJECT_TYPE));
6962 __ b(ge, slow);
6963 // Normally here we fall through to return_equal, but undefined is
6964 // special: (undefined == undefined) == true, but
6965 // (undefined <= undefined) == false! See ECMAScript 11.8.5.
6966 if (cc == le || cc == ge) {
6967 __ cmp(r4, Operand(ODDBALL_TYPE));
6968 __ b(ne, &return_equal);
6969 __ LoadRoot(r2, Heap::kUndefinedValueRootIndex);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006970 __ cmp(r0, r2);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006971 __ b(ne, &return_equal);
6972 if (cc == le) {
6973 // undefined <= undefined should fail.
6974 __ mov(r0, Operand(GREATER));
6975 } else {
6976 // undefined >= undefined should fail.
6977 __ mov(r0, Operand(LESS));
6978 }
6979 __ mov(pc, Operand(lr)); // Return.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006980 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006981 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006982 }
6983 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006984
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006985 __ bind(&return_equal);
6986 if (cc == lt) {
6987 __ mov(r0, Operand(GREATER)); // Things aren't less than themselves.
6988 } else if (cc == gt) {
6989 __ mov(r0, Operand(LESS)); // Things aren't greater than themselves.
6990 } else {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006991 __ mov(r0, Operand(EQUAL)); // Things are <=, >=, ==, === themselves.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006992 }
6993 __ mov(pc, Operand(lr)); // Return.
6994
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006995 if (cc != eq || !never_nan_nan) {
6996 // For less and greater we don't have to check for NaN since the result of
6997 // x < x is false regardless. For the others here is some code to check
6998 // for NaN.
6999 if (cc != lt && cc != gt) {
7000 __ bind(&heap_number);
7001 // It is a heap number, so return non-equal if it's NaN and equal if it's
7002 // not NaN.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007003
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007004 // The representation of NaN values has all exponent bits (52..62) set,
7005 // and not all mantissa bits (0..51) clear.
7006 // Read top bits of double representation (second word of value).
7007 __ ldr(r2, FieldMemOperand(r0, HeapNumber::kExponentOffset));
7008 // Test that exponent bits are all set.
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00007009 __ Sbfx(r3, r2, HeapNumber::kExponentShift, HeapNumber::kExponentBits);
7010 // NaNs have all-one exponents so they sign extend to -1.
7011 __ cmp(r3, Operand(-1));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007012 __ b(ne, &return_equal);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007013
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007014 // Shift out flag and all exponent bits, retaining only mantissa.
7015 __ mov(r2, Operand(r2, LSL, HeapNumber::kNonMantissaBitsInTopWord));
7016 // Or with all low-bits of mantissa.
7017 __ ldr(r3, FieldMemOperand(r0, HeapNumber::kMantissaOffset));
7018 __ orr(r0, r3, Operand(r2), SetCC);
7019 // For equal we already have the right value in r0: Return zero (equal)
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007020 // if all bits in mantissa are zero (it's an Infinity) and non-zero if
7021 // not (it's a NaN). For <= and >= we need to load r0 with the failing
7022 // value if it's a NaN.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007023 if (cc != eq) {
7024 // All-zero means Infinity means equal.
7025 __ mov(pc, Operand(lr), LeaveCC, eq); // Return equal
7026 if (cc == le) {
7027 __ mov(r0, Operand(GREATER)); // NaN <= NaN should fail.
7028 } else {
7029 __ mov(r0, Operand(LESS)); // NaN >= NaN should fail.
7030 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007031 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007032 __ mov(pc, Operand(lr)); // Return.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007033 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007034 // No fall through here.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007035 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007036
7037 __ bind(&not_identical);
7038}
7039
7040
7041// See comment at call site.
7042static void EmitSmiNonsmiComparison(MacroAssembler* masm,
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007043 Label* lhs_not_nan,
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007044 Label* slow,
7045 bool strict) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007046 Label rhs_is_smi;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007047 __ tst(r0, Operand(kSmiTagMask));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007048 __ b(eq, &rhs_is_smi);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007049
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007050 // Lhs is a Smi. Check whether the rhs is a heap number.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007051 __ CompareObjectType(r0, r4, r4, HEAP_NUMBER_TYPE);
7052 if (strict) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007053 // If rhs is not a number and lhs is a Smi then strict equality cannot
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007054 // succeed. Return non-equal (r0 is already not zero)
7055 __ mov(pc, Operand(lr), LeaveCC, ne); // Return.
7056 } else {
7057 // Smi compared non-strictly with a non-Smi non-heap-number. Call
7058 // the runtime.
7059 __ b(ne, slow);
7060 }
7061
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007062 // Lhs (r1) is a smi, rhs (r0) is a number.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007063 if (CpuFeatures::IsSupported(VFP3)) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007064 // Convert lhs to a double in d7 .
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007065 CpuFeatures::Scope scope(VFP3);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007066 __ mov(r7, Operand(r1, ASR, kSmiTagSize));
7067 __ vmov(s15, r7);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007068 __ vcvt_f64_s32(d7, s15);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007069 // Load the double from rhs, tagged HeapNumber r0, to d6.
7070 __ sub(r7, r0, Operand(kHeapObjectTag));
7071 __ vldr(d6, r7, HeapNumber::kValueOffset);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007072 } else {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007073 __ push(lr);
7074 // Convert lhs to a double in r2, r3.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007075 __ mov(r7, Operand(r1));
7076 ConvertToDoubleStub stub1(r3, r2, r7, r6);
7077 __ Call(stub1.GetCode(), RelocInfo::CODE_TARGET);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007078 // Load rhs to a double in r0, r1.
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00007079 __ Ldrd(r0, r1, FieldMemOperand(r0, HeapNumber::kValueOffset));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007080 __ pop(lr);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007081 }
7082
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007083 // We now have both loaded as doubles but we can skip the lhs nan check
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007084 // since it's a smi.
7085 __ jmp(lhs_not_nan);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007086
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007087 __ bind(&rhs_is_smi);
7088 // Rhs is a smi. Check whether the non-smi lhs is a heap number.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007089 __ CompareObjectType(r1, r4, r4, HEAP_NUMBER_TYPE);
7090 if (strict) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007091 // If lhs is not a number and rhs is a smi then strict equality cannot
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007092 // succeed. Return non-equal.
7093 __ mov(r0, Operand(1), LeaveCC, ne); // Non-zero indicates not equal.
7094 __ mov(pc, Operand(lr), LeaveCC, ne); // Return.
7095 } else {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007096 // Smi compared non-strictly with a non-smi non-heap-number. Call
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007097 // the runtime.
7098 __ b(ne, slow);
7099 }
7100
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007101 // Rhs (r0) is a smi, lhs (r1) is a heap number.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007102 if (CpuFeatures::IsSupported(VFP3)) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007103 // Convert rhs to a double in d6 .
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007104 CpuFeatures::Scope scope(VFP3);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007105 // Load the double from lhs, tagged HeapNumber r1, to d7.
7106 __ sub(r7, r1, Operand(kHeapObjectTag));
7107 __ vldr(d7, r7, HeapNumber::kValueOffset);
7108 __ mov(r7, Operand(r0, ASR, kSmiTagSize));
7109 __ vmov(s13, r7);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007110 __ vcvt_f64_s32(d6, s13);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007111 } else {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007112 __ push(lr);
7113 // Load lhs to a double in r2, r3.
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00007114 __ Ldrd(r2, r3, FieldMemOperand(r1, HeapNumber::kValueOffset));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007115 // Convert rhs to a double in r0, r1.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007116 __ mov(r7, Operand(r0));
7117 ConvertToDoubleStub stub2(r1, r0, r7, r6);
7118 __ Call(stub2.GetCode(), RelocInfo::CODE_TARGET);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007119 __ pop(lr);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007120 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007121 // Fall through to both_loaded_as_doubles.
7122}
7123
7124
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007125void EmitNanCheck(MacroAssembler* masm, Label* lhs_not_nan, Condition cc) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007126 bool exp_first = (HeapNumber::kExponentOffset == HeapNumber::kValueOffset);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007127 Register rhs_exponent = exp_first ? r0 : r1;
7128 Register lhs_exponent = exp_first ? r2 : r3;
7129 Register rhs_mantissa = exp_first ? r1 : r0;
7130 Register lhs_mantissa = exp_first ? r3 : r2;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007131 Label one_is_nan, neither_is_nan;
7132
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00007133 __ Sbfx(r4,
7134 lhs_exponent,
7135 HeapNumber::kExponentShift,
7136 HeapNumber::kExponentBits);
7137 // NaNs have all-one exponents so they sign extend to -1.
7138 __ cmp(r4, Operand(-1));
7139 __ b(ne, lhs_not_nan);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007140 __ mov(r4,
7141 Operand(lhs_exponent, LSL, HeapNumber::kNonMantissaBitsInTopWord),
7142 SetCC);
7143 __ b(ne, &one_is_nan);
7144 __ cmp(lhs_mantissa, Operand(0));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007145 __ b(ne, &one_is_nan);
7146
7147 __ bind(lhs_not_nan);
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00007148 __ Sbfx(r4,
7149 rhs_exponent,
7150 HeapNumber::kExponentShift,
7151 HeapNumber::kExponentBits);
7152 // NaNs have all-one exponents so they sign extend to -1.
7153 __ cmp(r4, Operand(-1));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007154 __ b(ne, &neither_is_nan);
7155 __ mov(r4,
7156 Operand(rhs_exponent, LSL, HeapNumber::kNonMantissaBitsInTopWord),
7157 SetCC);
7158 __ b(ne, &one_is_nan);
7159 __ cmp(rhs_mantissa, Operand(0));
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007160 __ b(eq, &neither_is_nan);
7161
7162 __ bind(&one_is_nan);
7163 // NaN comparisons always fail.
7164 // Load whatever we need in r0 to make the comparison fail.
7165 if (cc == lt || cc == le) {
7166 __ mov(r0, Operand(GREATER));
7167 } else {
7168 __ mov(r0, Operand(LESS));
7169 }
7170 __ mov(pc, Operand(lr)); // Return.
7171
7172 __ bind(&neither_is_nan);
7173}
7174
7175
7176// See comment at call site.
7177static void EmitTwoNonNanDoubleComparison(MacroAssembler* masm, Condition cc) {
7178 bool exp_first = (HeapNumber::kExponentOffset == HeapNumber::kValueOffset);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007179 Register rhs_exponent = exp_first ? r0 : r1;
7180 Register lhs_exponent = exp_first ? r2 : r3;
7181 Register rhs_mantissa = exp_first ? r1 : r0;
7182 Register lhs_mantissa = exp_first ? r3 : r2;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007183
7184 // r0, r1, r2, r3 have the two doubles. Neither is a NaN.
7185 if (cc == eq) {
7186 // Doubles are not equal unless they have the same bit pattern.
7187 // Exception: 0 and -0.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007188 __ cmp(rhs_mantissa, Operand(lhs_mantissa));
7189 __ orr(r0, rhs_mantissa, Operand(lhs_mantissa), LeaveCC, ne);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007190 // Return non-zero if the numbers are unequal.
7191 __ mov(pc, Operand(lr), LeaveCC, ne);
7192
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007193 __ sub(r0, rhs_exponent, Operand(lhs_exponent), SetCC);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007194 // If exponents are equal then return 0.
7195 __ mov(pc, Operand(lr), LeaveCC, eq);
7196
7197 // Exponents are unequal. The only way we can return that the numbers
7198 // are equal is if one is -0 and the other is 0. We already dealt
7199 // with the case where both are -0 or both are 0.
7200 // We start by seeing if the mantissas (that are equal) or the bottom
7201 // 31 bits of the rhs exponent are non-zero. If so we return not
7202 // equal.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007203 __ orr(r4, lhs_mantissa, Operand(lhs_exponent, LSL, kSmiTagSize), SetCC);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007204 __ mov(r0, Operand(r4), LeaveCC, ne);
7205 __ mov(pc, Operand(lr), LeaveCC, ne); // Return conditionally.
7206 // Now they are equal if and only if the lhs exponent is zero in its
7207 // low 31 bits.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007208 __ mov(r0, Operand(rhs_exponent, LSL, kSmiTagSize));
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007209 __ mov(pc, Operand(lr));
7210 } else {
7211 // Call a native function to do a comparison between two non-NaNs.
7212 // Call C routine that may not cause GC or other trouble.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00007213 __ push(lr);
7214 __ PrepareCallCFunction(4, r5); // Two doubles count as 4 arguments.
7215 __ CallCFunction(ExternalReference::compare_doubles(), 4);
7216 __ pop(pc); // Return.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007217 }
7218}
7219
7220
7221// See comment at call site.
7222static void EmitStrictTwoHeapObjectCompare(MacroAssembler* masm) {
7223 // If either operand is a JSObject or an oddball value, then they are
7224 // not equal since their pointers are different.
7225 // There is no test for undetectability in strict equality.
7226 ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
7227 Label first_non_object;
7228 // Get the type of the first operand into r2 and compare it with
7229 // FIRST_JS_OBJECT_TYPE.
7230 __ CompareObjectType(r0, r2, r2, FIRST_JS_OBJECT_TYPE);
7231 __ b(lt, &first_non_object);
7232
7233 // Return non-zero (r0 is not zero)
7234 Label return_not_equal;
7235 __ bind(&return_not_equal);
7236 __ mov(pc, Operand(lr)); // Return.
7237
7238 __ bind(&first_non_object);
7239 // Check for oddballs: true, false, null, undefined.
7240 __ cmp(r2, Operand(ODDBALL_TYPE));
7241 __ b(eq, &return_not_equal);
7242
7243 __ CompareObjectType(r1, r3, r3, FIRST_JS_OBJECT_TYPE);
7244 __ b(ge, &return_not_equal);
7245
7246 // Check for oddballs: true, false, null, undefined.
7247 __ cmp(r3, Operand(ODDBALL_TYPE));
7248 __ b(eq, &return_not_equal);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007249
7250 // Now that we have the types we might as well check for symbol-symbol.
7251 // Ensure that no non-strings have the symbol bit set.
7252 ASSERT(kNotStringTag + kIsSymbolMask > LAST_TYPE);
7253 ASSERT(kSymbolTag != 0);
7254 __ and_(r2, r2, Operand(r3));
7255 __ tst(r2, Operand(kIsSymbolMask));
7256 __ b(ne, &return_not_equal);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007257}
7258
7259
7260// See comment at call site.
7261static void EmitCheckForTwoHeapNumbers(MacroAssembler* masm,
7262 Label* both_loaded_as_doubles,
7263 Label* not_heap_numbers,
7264 Label* slow) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007265 __ CompareObjectType(r0, r3, r2, HEAP_NUMBER_TYPE);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007266 __ b(ne, not_heap_numbers);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007267 __ ldr(r2, FieldMemOperand(r1, HeapObject::kMapOffset));
7268 __ cmp(r2, r3);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007269 __ b(ne, slow); // First was a heap number, second wasn't. Go slow case.
7270
7271 // Both are heap numbers. Load them up then jump to the code we have
7272 // for that.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007273 if (CpuFeatures::IsSupported(VFP3)) {
7274 CpuFeatures::Scope scope(VFP3);
7275 __ sub(r7, r0, Operand(kHeapObjectTag));
7276 __ vldr(d6, r7, HeapNumber::kValueOffset);
7277 __ sub(r7, r1, Operand(kHeapObjectTag));
7278 __ vldr(d7, r7, HeapNumber::kValueOffset);
7279 } else {
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00007280 __ Ldrd(r2, r3, FieldMemOperand(r1, HeapNumber::kValueOffset));
7281 __ Ldrd(r0, r1, FieldMemOperand(r0, HeapNumber::kValueOffset));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007282 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007283 __ jmp(both_loaded_as_doubles);
7284}
7285
7286
7287// Fast negative check for symbol-to-symbol equality.
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00007288static void EmitCheckForSymbolsOrObjects(MacroAssembler* masm,
7289 Label* possible_strings,
7290 Label* not_both_strings) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007291 // r2 is object type of r0.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007292 // Ensure that no non-strings have the symbol bit set.
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00007293 Label object_test;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007294 ASSERT(kSymbolTag != 0);
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00007295 __ tst(r2, Operand(kIsNotStringMask));
7296 __ b(ne, &object_test);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007297 __ tst(r2, Operand(kIsSymbolMask));
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00007298 __ b(eq, possible_strings);
7299 __ CompareObjectType(r1, r3, r3, FIRST_NONSTRING_TYPE);
7300 __ b(ge, not_both_strings);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007301 __ tst(r3, Operand(kIsSymbolMask));
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00007302 __ b(eq, possible_strings);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007303
7304 // Both are symbols. We already checked they weren't the same pointer
7305 // so they are not equal.
7306 __ mov(r0, Operand(1)); // Non-zero indicates not equal.
7307 __ mov(pc, Operand(lr)); // Return.
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00007308
7309 __ bind(&object_test);
7310 __ cmp(r2, Operand(FIRST_JS_OBJECT_TYPE));
7311 __ b(lt, not_both_strings);
7312 __ CompareObjectType(r1, r2, r3, FIRST_JS_OBJECT_TYPE);
7313 __ b(lt, not_both_strings);
7314 // If both objects are undetectable, they are equal. Otherwise, they
7315 // are not equal, since they are different objects and an object is not
7316 // equal to undefined.
7317 __ ldr(r3, FieldMemOperand(r0, HeapObject::kMapOffset));
7318 __ ldrb(r2, FieldMemOperand(r2, Map::kBitFieldOffset));
7319 __ ldrb(r3, FieldMemOperand(r3, Map::kBitFieldOffset));
7320 __ and_(r0, r2, Operand(r3));
7321 __ and_(r0, r0, Operand(1 << Map::kIsUndetectable));
7322 __ eor(r0, r0, Operand(1 << Map::kIsUndetectable));
7323 __ mov(pc, Operand(lr)); // Return.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007324}
7325
7326
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007327void NumberToStringStub::GenerateLookupNumberStringCache(MacroAssembler* masm,
7328 Register object,
7329 Register result,
7330 Register scratch1,
7331 Register scratch2,
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00007332 Register scratch3,
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007333 bool object_is_smi,
7334 Label* not_found) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007335 // Use of registers. Register result is used as a temporary.
7336 Register number_string_cache = result;
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00007337 Register mask = scratch3;
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007338
7339 // Load the number string cache.
7340 __ LoadRoot(number_string_cache, Heap::kNumberStringCacheRootIndex);
7341
7342 // Make the hash mask from the length of the number string cache. It
7343 // contains two elements (number and string) for each cache entry.
7344 __ ldr(mask, FieldMemOperand(number_string_cache, FixedArray::kLengthOffset));
ricow@chromium.org30ce4112010-05-31 10:38:25 +00007345 // Divide length by two (length is a smi).
7346 __ mov(mask, Operand(mask, ASR, kSmiTagSize + 1));
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007347 __ sub(mask, mask, Operand(1)); // Make mask.
7348
7349 // Calculate the entry in the number string cache. The hash value in the
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00007350 // number string cache for smis is just the smi value, and the hash for
7351 // doubles is the xor of the upper and lower words. See
7352 // Heap::GetNumberStringCache.
7353 Label is_smi;
7354 Label load_result_from_cache;
7355 if (!object_is_smi) {
7356 __ BranchOnSmi(object, &is_smi);
7357 if (CpuFeatures::IsSupported(VFP3)) {
7358 CpuFeatures::Scope scope(VFP3);
7359 __ CheckMap(object,
7360 scratch1,
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00007361 Heap::kHeapNumberMapRootIndex,
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00007362 not_found,
7363 true);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007364
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00007365 ASSERT_EQ(8, kDoubleSize);
7366 __ add(scratch1,
7367 object,
7368 Operand(HeapNumber::kValueOffset - kHeapObjectTag));
7369 __ ldm(ia, scratch1, scratch1.bit() | scratch2.bit());
7370 __ eor(scratch1, scratch1, Operand(scratch2));
7371 __ and_(scratch1, scratch1, Operand(mask));
7372
7373 // Calculate address of entry in string cache: each entry consists
7374 // of two pointer sized fields.
7375 __ add(scratch1,
7376 number_string_cache,
7377 Operand(scratch1, LSL, kPointerSizeLog2 + 1));
7378
7379 Register probe = mask;
7380 __ ldr(probe,
7381 FieldMemOperand(scratch1, FixedArray::kHeaderSize));
7382 __ BranchOnSmi(probe, not_found);
7383 __ sub(scratch2, object, Operand(kHeapObjectTag));
7384 __ vldr(d0, scratch2, HeapNumber::kValueOffset);
7385 __ sub(probe, probe, Operand(kHeapObjectTag));
7386 __ vldr(d1, probe, HeapNumber::kValueOffset);
7387 __ vcmp(d0, d1);
7388 __ vmrs(pc);
7389 __ b(ne, not_found); // The cache did not contain this value.
7390 __ b(&load_result_from_cache);
7391 } else {
7392 __ b(not_found);
7393 }
7394 }
7395
7396 __ bind(&is_smi);
7397 Register scratch = scratch1;
7398 __ and_(scratch, mask, Operand(object, ASR, 1));
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007399 // Calculate address of entry in string cache: each entry consists
7400 // of two pointer sized fields.
7401 __ add(scratch,
7402 number_string_cache,
7403 Operand(scratch, LSL, kPointerSizeLog2 + 1));
7404
7405 // Check if the entry is the smi we are looking for.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00007406 Register probe = mask;
7407 __ ldr(probe, FieldMemOperand(scratch, FixedArray::kHeaderSize));
7408 __ cmp(object, probe);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007409 __ b(ne, not_found);
7410
7411 // Get the result from the cache.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00007412 __ bind(&load_result_from_cache);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007413 __ ldr(result,
7414 FieldMemOperand(scratch, FixedArray::kHeaderSize + kPointerSize));
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007415 __ IncrementCounter(&Counters::number_to_string_native,
7416 1,
7417 scratch1,
7418 scratch2);
7419}
7420
7421
7422void NumberToStringStub::Generate(MacroAssembler* masm) {
7423 Label runtime;
7424
7425 __ ldr(r1, MemOperand(sp, 0));
7426
7427 // Generate code to lookup number in the number string cache.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00007428 GenerateLookupNumberStringCache(masm, r1, r0, r2, r3, r4, false, &runtime);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007429 __ add(sp, sp, Operand(1 * kPointerSize));
7430 __ Ret();
7431
7432 __ bind(&runtime);
7433 // Handle number to string in the runtime system if not found in the cache.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00007434 __ TailCallRuntime(Runtime::kNumberToStringSkipCache, 1, 1);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007435}
7436
7437
ager@chromium.orgac091b72010-05-05 07:34:42 +00007438void RecordWriteStub::Generate(MacroAssembler* masm) {
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00007439 __ add(offset_, object_, Operand(offset_));
7440 __ RecordWriteHelper(object_, offset_, scratch_);
ager@chromium.orgac091b72010-05-05 07:34:42 +00007441 __ Ret();
7442}
7443
7444
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007445// On entry r0 (rhs) and r1 (lhs) are the values to be compared.
7446// On exit r0 is 0, positive or negative to indicate the result of
7447// the comparison.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007448void CompareStub::Generate(MacroAssembler* masm) {
7449 Label slow; // Call builtin.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007450 Label not_smis, both_loaded_as_doubles, lhs_not_nan;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007451
7452 // NOTICE! This code is only reached after a smi-fast-case check, so
7453 // it is certain that at least one operand isn't a smi.
7454
7455 // Handle the case where the objects are identical. Either returns the answer
7456 // or goes to slow. Only falls through if the objects were not identical.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007457 EmitIdenticalObjectComparison(masm, &slow, cc_, never_nan_nan_);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007458
7459 // If either is a Smi (we know that not both are), then they can only
7460 // be strictly equal if the other is a HeapNumber.
7461 ASSERT_EQ(0, kSmiTag);
7462 ASSERT_EQ(0, Smi::FromInt(0));
7463 __ and_(r2, r0, Operand(r1));
7464 __ tst(r2, Operand(kSmiTagMask));
7465 __ b(ne, &not_smis);
7466 // One operand is a smi. EmitSmiNonsmiComparison generates code that can:
7467 // 1) Return the answer.
7468 // 2) Go to slow.
7469 // 3) Fall through to both_loaded_as_doubles.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007470 // 4) Jump to lhs_not_nan.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007471 // In cases 3 and 4 we have found out we were dealing with a number-number
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007472 // comparison. If VFP3 is supported the double values of the numbers have
7473 // been loaded into d7 and d6. Otherwise, the double values have been loaded
7474 // into r0, r1, r2, and r3.
7475 EmitSmiNonsmiComparison(masm, &lhs_not_nan, &slow, strict_);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007476
7477 __ bind(&both_loaded_as_doubles);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007478 // The arguments have been converted to doubles and stored in d6 and d7, if
7479 // VFP3 is supported, or in r0, r1, r2, and r3.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007480 if (CpuFeatures::IsSupported(VFP3)) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007481 __ bind(&lhs_not_nan);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007482 CpuFeatures::Scope scope(VFP3);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007483 Label no_nan;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007484 // ARMv7 VFP3 instructions to implement double precision comparison.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007485 __ vcmp(d7, d6);
7486 __ vmrs(pc); // Move vector status bits to normal status bits.
7487 Label nan;
7488 __ b(vs, &nan);
7489 __ mov(r0, Operand(EQUAL), LeaveCC, eq);
7490 __ mov(r0, Operand(LESS), LeaveCC, lt);
7491 __ mov(r0, Operand(GREATER), LeaveCC, gt);
7492 __ mov(pc, Operand(lr));
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007493
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007494 __ bind(&nan);
7495 // If one of the sides was a NaN then the v flag is set. Load r0 with
7496 // whatever it takes to make the comparison fail, since comparisons with NaN
7497 // always fail.
7498 if (cc_ == lt || cc_ == le) {
7499 __ mov(r0, Operand(GREATER));
7500 } else {
7501 __ mov(r0, Operand(LESS));
7502 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007503 __ mov(pc, Operand(lr));
7504 } else {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007505 // Checks for NaN in the doubles we have loaded. Can return the answer or
7506 // fall through if neither is a NaN. Also binds lhs_not_nan.
7507 EmitNanCheck(masm, &lhs_not_nan, cc_);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007508 // Compares two doubles in r0, r1, r2, r3 that are not NaNs. Returns the
7509 // answer. Never falls through.
7510 EmitTwoNonNanDoubleComparison(masm, cc_);
7511 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007512
7513 __ bind(&not_smis);
7514 // At this point we know we are dealing with two different objects,
7515 // and neither of them is a Smi. The objects are in r0 and r1.
7516 if (strict_) {
7517 // This returns non-equal for some object types, or falls through if it
7518 // was not lucky.
7519 EmitStrictTwoHeapObjectCompare(masm);
7520 }
7521
7522 Label check_for_symbols;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007523 Label flat_string_check;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007524 // Check for heap-number-heap-number comparison. Can jump to slow case,
7525 // or load both doubles into r0, r1, r2, r3 and jump to the code that handles
7526 // that case. If the inputs are not doubles then jumps to check_for_symbols.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007527 // In this case r2 will contain the type of r0. Never falls through.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007528 EmitCheckForTwoHeapNumbers(masm,
7529 &both_loaded_as_doubles,
7530 &check_for_symbols,
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007531 &flat_string_check);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007532
7533 __ bind(&check_for_symbols);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007534 // In the strict case the EmitStrictTwoHeapObjectCompare already took care of
7535 // symbols.
7536 if (cc_ == eq && !strict_) {
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00007537 // Returns an answer for two symbols or two detectable objects.
7538 // Otherwise jumps to string case or not both strings case.
7539 // Assumes that r2 is the type of r0 on entry.
7540 EmitCheckForSymbolsOrObjects(masm, &flat_string_check, &slow);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007541 }
7542
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007543 // Check for both being sequential ASCII strings, and inline if that is the
7544 // case.
7545 __ bind(&flat_string_check);
7546
7547 __ JumpIfNonSmisNotBothSequentialAsciiStrings(r0, r1, r2, r3, &slow);
7548
7549 __ IncrementCounter(&Counters::string_compare_native, 1, r2, r3);
7550 StringCompareStub::GenerateCompareFlatAsciiStrings(masm,
7551 r1,
7552 r0,
7553 r2,
7554 r3,
7555 r4,
7556 r5);
7557 // Never falls through to here.
7558
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007559 __ bind(&slow);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007560
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00007561 __ Push(r1, r0);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007562 // Figure out which native to call and setup the arguments.
7563 Builtins::JavaScript native;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007564 if (cc_ == eq) {
7565 native = strict_ ? Builtins::STRICT_EQUALS : Builtins::EQUALS;
7566 } else {
7567 native = Builtins::COMPARE;
7568 int ncr; // NaN compare result
7569 if (cc_ == lt || cc_ == le) {
7570 ncr = GREATER;
7571 } else {
7572 ASSERT(cc_ == gt || cc_ == ge); // remaining cases
7573 ncr = LESS;
7574 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007575 __ mov(r0, Operand(Smi::FromInt(ncr)));
7576 __ push(r0);
7577 }
7578
7579 // Call the native; it returns -1 (less), 0 (equal), or 1 (greater)
7580 // tagged as a small integer.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007581 __ InvokeBuiltin(native, JUMP_JS);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007582}
7583
7584
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00007585// We fall into this code if the operands were Smis, but the result was
7586// not (eg. overflow). We branch into this code (to the not_smi label) if
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007587// the operands were not both Smi. The operands are in r0 and r1. In order
7588// to call the C-implemented binary fp operation routines we need to end up
7589// with the double precision floating point operands in r0 and r1 (for the
7590// value in r1) and r2 and r3 (for the value in r0).
ager@chromium.org357bf652010-04-12 11:30:10 +00007591void GenericBinaryOpStub::HandleBinaryOpSlowCases(
7592 MacroAssembler* masm,
7593 Label* not_smi,
7594 Register lhs,
7595 Register rhs,
7596 const Builtins::JavaScript& builtin) {
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00007597 Label slow, slow_reverse, do_the_call;
ager@chromium.org357bf652010-04-12 11:30:10 +00007598 bool use_fp_registers = CpuFeatures::IsSupported(VFP3) && Token::MOD != op_;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007599
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00007600 ASSERT((lhs.is(r0) && rhs.is(r1)) || (lhs.is(r1) && rhs.is(r0)));
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00007601 Register heap_number_map = r6;
ager@chromium.org357bf652010-04-12 11:30:10 +00007602
7603 if (ShouldGenerateSmiCode()) {
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00007604 __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
7605
ager@chromium.org357bf652010-04-12 11:30:10 +00007606 // Smi-smi case (overflow).
7607 // Since both are Smis there is no heap number to overwrite, so allocate.
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00007608 // The new heap number is in r5. r3 and r7 are scratch.
7609 __ AllocateHeapNumber(
7610 r5, r3, r7, heap_number_map, lhs.is(r0) ? &slow_reverse : &slow);
ager@chromium.org357bf652010-04-12 11:30:10 +00007611
7612 // If we have floating point hardware, inline ADD, SUB, MUL, and DIV,
7613 // using registers d7 and d6 for the double values.
lrn@chromium.org32d961d2010-06-30 09:09:34 +00007614 if (CpuFeatures::IsSupported(VFP3)) {
ager@chromium.org357bf652010-04-12 11:30:10 +00007615 CpuFeatures::Scope scope(VFP3);
7616 __ mov(r7, Operand(rhs, ASR, kSmiTagSize));
7617 __ vmov(s15, r7);
7618 __ vcvt_f64_s32(d7, s15);
7619 __ mov(r7, Operand(lhs, ASR, kSmiTagSize));
7620 __ vmov(s13, r7);
7621 __ vcvt_f64_s32(d6, s13);
lrn@chromium.org32d961d2010-06-30 09:09:34 +00007622 if (!use_fp_registers) {
7623 __ vmov(r2, r3, d7);
7624 __ vmov(r0, r1, d6);
7625 }
ager@chromium.org357bf652010-04-12 11:30:10 +00007626 } else {
lrn@chromium.org32d961d2010-06-30 09:09:34 +00007627 // Write Smi from rhs to r3 and r2 in double format. r9 is scratch.
ager@chromium.org357bf652010-04-12 11:30:10 +00007628 __ mov(r7, Operand(rhs));
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00007629 ConvertToDoubleStub stub1(r3, r2, r7, r9);
ager@chromium.org357bf652010-04-12 11:30:10 +00007630 __ push(lr);
7631 __ Call(stub1.GetCode(), RelocInfo::CODE_TARGET);
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00007632 // Write Smi from lhs to r1 and r0 in double format. r9 is scratch.
ager@chromium.org357bf652010-04-12 11:30:10 +00007633 __ mov(r7, Operand(lhs));
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00007634 ConvertToDoubleStub stub2(r1, r0, r7, r9);
ager@chromium.org357bf652010-04-12 11:30:10 +00007635 __ Call(stub2.GetCode(), RelocInfo::CODE_TARGET);
7636 __ pop(lr);
7637 }
7638 __ jmp(&do_the_call); // Tail call. No return.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007639 }
7640
ager@chromium.org357bf652010-04-12 11:30:10 +00007641 // We branch here if at least one of r0 and r1 is not a Smi.
7642 __ bind(not_smi);
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00007643 __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007644
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00007645 // After this point we have the left hand side in r1 and the right hand side
7646 // in r0.
ager@chromium.org357bf652010-04-12 11:30:10 +00007647 if (lhs.is(r0)) {
7648 __ Swap(r0, r1, ip);
7649 }
7650
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00007651 // The type transition also calculates the answer.
7652 bool generate_code_to_calculate_answer = true;
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00007653
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00007654 if (ShouldGenerateFPCode()) {
ager@chromium.org357bf652010-04-12 11:30:10 +00007655 if (runtime_operands_type_ == BinaryOpIC::DEFAULT) {
7656 switch (op_) {
7657 case Token::ADD:
7658 case Token::SUB:
7659 case Token::MUL:
7660 case Token::DIV:
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00007661 GenerateTypeTransition(masm); // Tail call.
7662 generate_code_to_calculate_answer = false;
ager@chromium.org357bf652010-04-12 11:30:10 +00007663 break;
7664
7665 default:
7666 break;
7667 }
7668 }
7669
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00007670 if (generate_code_to_calculate_answer) {
7671 Label r0_is_smi, r1_is_smi, finished_loading_r0, finished_loading_r1;
7672 if (mode_ == NO_OVERWRITE) {
7673 // In the case where there is no chance of an overwritable float we may
7674 // as well do the allocation immediately while r0 and r1 are untouched.
7675 __ AllocateHeapNumber(r5, r3, r7, heap_number_map, &slow);
lrn@chromium.org32d961d2010-06-30 09:09:34 +00007676 }
ager@chromium.org357bf652010-04-12 11:30:10 +00007677
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00007678 // Move r0 to a double in r2-r3.
7679 __ tst(r0, Operand(kSmiTagMask));
7680 __ b(eq, &r0_is_smi); // It's a Smi so don't check it's a heap number.
7681 __ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset));
7682 __ AssertRegisterIsRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
7683 __ cmp(r4, heap_number_map);
7684 __ b(ne, &slow);
7685 if (mode_ == OVERWRITE_RIGHT) {
7686 __ mov(r5, Operand(r0)); // Overwrite this heap number.
lrn@chromium.org32d961d2010-06-30 09:09:34 +00007687 }
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00007688 if (use_fp_registers) {
7689 CpuFeatures::Scope scope(VFP3);
7690 // Load the double from tagged HeapNumber r0 to d7.
7691 __ sub(r7, r0, Operand(kHeapObjectTag));
7692 __ vldr(d7, r7, HeapNumber::kValueOffset);
ager@chromium.org357bf652010-04-12 11:30:10 +00007693 } else {
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00007694 // Calling convention says that second double is in r2 and r3.
7695 __ Ldrd(r2, r3, FieldMemOperand(r0, HeapNumber::kValueOffset));
ager@chromium.org357bf652010-04-12 11:30:10 +00007696 }
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00007697 __ jmp(&finished_loading_r0);
7698 __ bind(&r0_is_smi);
7699 if (mode_ == OVERWRITE_RIGHT) {
7700 // We can't overwrite a Smi so get address of new heap number into r5.
7701 __ AllocateHeapNumber(r5, r4, r7, heap_number_map, &slow);
7702 }
ager@chromium.org357bf652010-04-12 11:30:10 +00007703
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00007704 if (CpuFeatures::IsSupported(VFP3)) {
7705 CpuFeatures::Scope scope(VFP3);
7706 // Convert smi in r0 to double in d7.
7707 __ mov(r7, Operand(r0, ASR, kSmiTagSize));
7708 __ vmov(s15, r7);
7709 __ vcvt_f64_s32(d7, s15);
7710 if (!use_fp_registers) {
7711 __ vmov(r2, r3, d7);
7712 }
7713 } else {
7714 // Write Smi from r0 to r3 and r2 in double format.
7715 __ mov(r7, Operand(r0));
7716 ConvertToDoubleStub stub3(r3, r2, r7, r4);
7717 __ push(lr);
7718 __ Call(stub3.GetCode(), RelocInfo::CODE_TARGET);
7719 __ pop(lr);
7720 }
7721
7722 // HEAP_NUMBERS stub is slower than GENERIC on a pair of smis.
7723 // r0 is known to be a smi. If r1 is also a smi then switch to GENERIC.
7724 Label r1_is_not_smi;
7725 if (runtime_operands_type_ == BinaryOpIC::HEAP_NUMBERS) {
7726 __ tst(r1, Operand(kSmiTagMask));
7727 __ b(ne, &r1_is_not_smi);
7728 GenerateTypeTransition(masm); // Tail call.
7729 }
7730
7731 __ bind(&finished_loading_r0);
7732
7733 // Move r1 to a double in r0-r1.
7734 __ tst(r1, Operand(kSmiTagMask));
7735 __ b(eq, &r1_is_smi); // It's a Smi so don't check it's a heap number.
7736 __ bind(&r1_is_not_smi);
7737 __ ldr(r4, FieldMemOperand(r1, HeapNumber::kMapOffset));
7738 __ AssertRegisterIsRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
7739 __ cmp(r4, heap_number_map);
7740 __ b(ne, &slow);
7741 if (mode_ == OVERWRITE_LEFT) {
7742 __ mov(r5, Operand(r1)); // Overwrite this heap number.
7743 }
7744 if (use_fp_registers) {
7745 CpuFeatures::Scope scope(VFP3);
7746 // Load the double from tagged HeapNumber r1 to d6.
7747 __ sub(r7, r1, Operand(kHeapObjectTag));
7748 __ vldr(d6, r7, HeapNumber::kValueOffset);
7749 } else {
7750 // Calling convention says that first double is in r0 and r1.
7751 __ Ldrd(r0, r1, FieldMemOperand(r1, HeapNumber::kValueOffset));
7752 }
7753 __ jmp(&finished_loading_r1);
7754 __ bind(&r1_is_smi);
7755 if (mode_ == OVERWRITE_LEFT) {
7756 // We can't overwrite a Smi so get address of new heap number into r5.
7757 __ AllocateHeapNumber(r5, r4, r7, heap_number_map, &slow);
7758 }
7759
7760 if (CpuFeatures::IsSupported(VFP3)) {
7761 CpuFeatures::Scope scope(VFP3);
7762 // Convert smi in r1 to double in d6.
7763 __ mov(r7, Operand(r1, ASR, kSmiTagSize));
7764 __ vmov(s13, r7);
7765 __ vcvt_f64_s32(d6, s13);
7766 if (!use_fp_registers) {
7767 __ vmov(r0, r1, d6);
7768 }
7769 } else {
7770 // Write Smi from r1 to r1 and r0 in double format.
7771 __ mov(r7, Operand(r1));
7772 ConvertToDoubleStub stub4(r1, r0, r7, r9);
7773 __ push(lr);
7774 __ Call(stub4.GetCode(), RelocInfo::CODE_TARGET);
7775 __ pop(lr);
7776 }
7777
7778 __ bind(&finished_loading_r1);
ager@chromium.org357bf652010-04-12 11:30:10 +00007779 }
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00007780
7781 if (generate_code_to_calculate_answer || do_the_call.is_linked()) {
7782 __ bind(&do_the_call);
7783 // If we are inlining the operation using VFP3 instructions for
7784 // add, subtract, multiply, or divide, the arguments are in d6 and d7.
7785 if (use_fp_registers) {
7786 CpuFeatures::Scope scope(VFP3);
7787 // ARMv7 VFP3 instructions to implement
7788 // double precision, add, subtract, multiply, divide.
7789
7790 if (Token::MUL == op_) {
7791 __ vmul(d5, d6, d7);
7792 } else if (Token::DIV == op_) {
7793 __ vdiv(d5, d6, d7);
7794 } else if (Token::ADD == op_) {
7795 __ vadd(d5, d6, d7);
7796 } else if (Token::SUB == op_) {
7797 __ vsub(d5, d6, d7);
7798 } else {
7799 UNREACHABLE();
7800 }
7801 __ sub(r0, r5, Operand(kHeapObjectTag));
7802 __ vstr(d5, r0, HeapNumber::kValueOffset);
7803 __ add(r0, r0, Operand(kHeapObjectTag));
7804 __ mov(pc, lr);
7805 } else {
7806 // If we did not inline the operation, then the arguments are in:
7807 // r0: Left value (least significant part of mantissa).
7808 // r1: Left value (sign, exponent, top of mantissa).
7809 // r2: Right value (least significant part of mantissa).
7810 // r3: Right value (sign, exponent, top of mantissa).
7811 // r5: Address of heap number for result.
7812
7813 __ push(lr); // For later.
7814 __ PrepareCallCFunction(4, r4); // Two doubles count as 4 arguments.
7815 // Call C routine that may not cause GC or other trouble. r5 is callee
7816 // save.
7817 __ CallCFunction(ExternalReference::double_fp_operation(op_), 4);
7818 // Store answer in the overwritable heap number.
7819 #if !defined(USE_ARM_EABI)
7820 // Double returned in fp coprocessor register 0 and 1, encoded as
7821 // register cr8. Offsets must be divisible by 4 for coprocessor so we
7822 // need to substract the tag from r5.
7823 __ sub(r4, r5, Operand(kHeapObjectTag));
7824 __ stc(p1, cr8, MemOperand(r4, HeapNumber::kValueOffset));
7825 #else
7826 // Double returned in registers 0 and 1.
7827 __ Strd(r0, r1, FieldMemOperand(r5, HeapNumber::kValueOffset));
7828 #endif
7829 __ mov(r0, Operand(r5));
7830 // And we are done.
7831 __ pop(pc);
7832 }
7833 }
7834 }
7835
7836 if (!generate_code_to_calculate_answer &&
7837 !slow_reverse.is_linked() &&
7838 !slow.is_linked()) {
7839 return;
ager@chromium.org357bf652010-04-12 11:30:10 +00007840 }
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00007841
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00007842 if (lhs.is(r0)) {
7843 __ b(&slow);
7844 __ bind(&slow_reverse);
7845 __ Swap(r0, r1, ip);
7846 }
7847
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00007848 heap_number_map = no_reg; // Don't use this any more from here on.
7849
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007850 // We jump to here if something goes wrong (one param is not a number of any
7851 // sort or new-space allocation fails).
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007852 __ bind(&slow);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007853
7854 // Push arguments to the stack
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00007855 __ Push(r1, r0);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007856
ager@chromium.org357bf652010-04-12 11:30:10 +00007857 if (Token::ADD == op_) {
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007858 // Test for string arguments before calling runtime.
7859 // r1 : first argument
7860 // r0 : second argument
7861 // sp[0] : second argument
ager@chromium.org5c838252010-02-19 08:53:10 +00007862 // sp[4] : first argument
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007863
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007864 Label not_strings, not_string1, string1, string1_smi2;
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007865 __ tst(r1, Operand(kSmiTagMask));
7866 __ b(eq, &not_string1);
7867 __ CompareObjectType(r1, r2, r2, FIRST_NONSTRING_TYPE);
7868 __ b(ge, &not_string1);
7869
7870 // First argument is a a string, test second.
7871 __ tst(r0, Operand(kSmiTagMask));
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007872 __ b(eq, &string1_smi2);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007873 __ CompareObjectType(r0, r2, r2, FIRST_NONSTRING_TYPE);
7874 __ b(ge, &string1);
7875
7876 // First and second argument are strings.
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007877 StringAddStub string_add_stub(NO_STRING_CHECK_IN_STUB);
7878 __ TailCallStub(&string_add_stub);
7879
7880 __ bind(&string1_smi2);
7881 // First argument is a string, second is a smi. Try to lookup the number
7882 // string for the smi in the number string cache.
7883 NumberToStringStub::GenerateLookupNumberStringCache(
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00007884 masm, r0, r2, r4, r5, r6, true, &string1);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007885
7886 // Replace second argument on stack and tailcall string add stub to make
7887 // the result.
7888 __ str(r2, MemOperand(sp, 0));
7889 __ TailCallStub(&string_add_stub);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007890
7891 // Only first argument is a string.
7892 __ bind(&string1);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007893 __ InvokeBuiltin(Builtins::STRING_ADD_LEFT, JUMP_JS);
7894
7895 // First argument was not a string, test second.
7896 __ bind(&not_string1);
7897 __ tst(r0, Operand(kSmiTagMask));
7898 __ b(eq, &not_strings);
7899 __ CompareObjectType(r0, r2, r2, FIRST_NONSTRING_TYPE);
7900 __ b(ge, &not_strings);
7901
7902 // Only second argument is a string.
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007903 __ InvokeBuiltin(Builtins::STRING_ADD_RIGHT, JUMP_JS);
7904
7905 __ bind(&not_strings);
7906 }
7907
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007908 __ InvokeBuiltin(builtin, JUMP_JS); // Tail call. No return.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007909}
7910
7911
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007912// Tries to get a signed int32 out of a double precision floating point heap
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007913// number. Rounds towards 0. Fastest for doubles that are in the ranges
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007914// -0x7fffffff to -0x40000000 or 0x40000000 to 0x7fffffff. This corresponds
7915// almost to the range of signed int32 values that are not Smis. Jumps to the
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007916// label 'slow' if the double isn't in the range -0x80000000.0 to 0x80000000.0
7917// (excluding the endpoints).
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007918static void GetInt32(MacroAssembler* masm,
7919 Register source,
7920 Register dest,
7921 Register scratch,
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007922 Register scratch2,
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007923 Label* slow) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007924 Label right_exponent, done;
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007925 // Get exponent word.
7926 __ ldr(scratch, FieldMemOperand(source, HeapNumber::kExponentOffset));
7927 // Get exponent alone in scratch2.
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00007928 __ Ubfx(scratch2,
7929 scratch,
7930 HeapNumber::kExponentShift,
7931 HeapNumber::kExponentBits);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007932 // Load dest with zero. We use this either for the final shift or
7933 // for the answer.
7934 __ mov(dest, Operand(0));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007935 // Check whether the exponent matches a 32 bit signed int that is not a Smi.
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007936 // A non-Smi integer is 1.xxx * 2^30 so the exponent is 30 (biased). This is
7937 // the exponent that we are fastest at and also the highest exponent we can
7938 // handle here.
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00007939 const uint32_t non_smi_exponent = HeapNumber::kExponentBias + 30;
7940 // The non_smi_exponent, 0x41d, is too big for ARM's immediate field so we
7941 // split it up to avoid a constant pool entry. You can't do that in general
7942 // for cmp because of the overflow flag, but we know the exponent is in the
7943 // range 0-2047 so there is no overflow.
7944 int fudge_factor = 0x400;
7945 __ sub(scratch2, scratch2, Operand(fudge_factor));
7946 __ cmp(scratch2, Operand(non_smi_exponent - fudge_factor));
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007947 // If we have a match of the int32-but-not-Smi exponent then skip some logic.
7948 __ b(eq, &right_exponent);
7949 // If the exponent is higher than that then go to slow case. This catches
7950 // numbers that don't fit in a signed int32, infinities and NaNs.
7951 __ b(gt, slow);
7952
7953 // We know the exponent is smaller than 30 (biased). If it is less than
7954 // 0 (biased) then the number is smaller in magnitude than 1.0 * 2^0, ie
7955 // it rounds to zero.
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00007956 const uint32_t zero_exponent = HeapNumber::kExponentBias + 0;
7957 __ sub(scratch2, scratch2, Operand(zero_exponent - fudge_factor), SetCC);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007958 // Dest already has a Smi zero.
7959 __ b(lt, &done);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007960 if (!CpuFeatures::IsSupported(VFP3)) {
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00007961 // We have an exponent between 0 and 30 in scratch2. Subtract from 30 to
7962 // get how much to shift down.
7963 __ rsb(dest, scratch2, Operand(30));
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007964 }
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007965 __ bind(&right_exponent);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007966 if (CpuFeatures::IsSupported(VFP3)) {
7967 CpuFeatures::Scope scope(VFP3);
7968 // ARMv7 VFP3 instructions implementing double precision to integer
7969 // conversion using round to zero.
7970 __ ldr(scratch2, FieldMemOperand(source, HeapNumber::kMantissaOffset));
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00007971 __ vmov(d7, scratch2, scratch);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007972 __ vcvt_s32_f64(s15, d7);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00007973 __ vmov(dest, s15);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007974 } else {
7975 // Get the top bits of the mantissa.
7976 __ and_(scratch2, scratch, Operand(HeapNumber::kMantissaMask));
7977 // Put back the implicit 1.
7978 __ orr(scratch2, scratch2, Operand(1 << HeapNumber::kExponentShift));
7979 // Shift up the mantissa bits to take up the space the exponent used to
7980 // take. We just orred in the implicit bit so that took care of one and
7981 // we want to leave the sign bit 0 so we subtract 2 bits from the shift
7982 // distance.
7983 const int shift_distance = HeapNumber::kNonMantissaBitsInTopWord - 2;
7984 __ mov(scratch2, Operand(scratch2, LSL, shift_distance));
7985 // Put sign in zero flag.
7986 __ tst(scratch, Operand(HeapNumber::kSignMask));
7987 // Get the second half of the double. For some exponents we don't
7988 // actually need this because the bits get shifted out again, but
7989 // it's probably slower to test than just to do it.
7990 __ ldr(scratch, FieldMemOperand(source, HeapNumber::kMantissaOffset));
7991 // Shift down 22 bits to get the last 10 bits.
7992 __ orr(scratch, scratch2, Operand(scratch, LSR, 32 - shift_distance));
7993 // Move down according to the exponent.
7994 __ mov(dest, Operand(scratch, LSR, dest));
7995 // Fix sign if sign bit was set.
7996 __ rsb(dest, dest, Operand(0), LeaveCC, ne);
7997 }
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007998 __ bind(&done);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007999}
8000
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008001// For bitwise ops where the inputs are not both Smis we here try to determine
8002// whether both inputs are either Smis or at least heap numbers that can be
8003// represented by a 32 bit signed value. We truncate towards zero as required
8004// by the ES spec. If this is the case we do the bitwise op and see if the
8005// result is a Smi. If so, great, otherwise we try to find a heap number to
8006// write the answer into (either by allocating or by overwriting).
ager@chromium.org357bf652010-04-12 11:30:10 +00008007// On entry the operands are in lhs and rhs. On exit the answer is in r0.
8008void GenericBinaryOpStub::HandleNonSmiBitwiseOp(MacroAssembler* masm,
8009 Register lhs,
8010 Register rhs) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008011 Label slow, result_not_a_smi;
ager@chromium.org357bf652010-04-12 11:30:10 +00008012 Label rhs_is_smi, lhs_is_smi;
8013 Label done_checking_rhs, done_checking_lhs;
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008014
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00008015 Register heap_number_map = r6;
8016 __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
8017
ager@chromium.org357bf652010-04-12 11:30:10 +00008018 __ tst(lhs, Operand(kSmiTagMask));
8019 __ 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 +00008020 __ ldr(r4, FieldMemOperand(lhs, HeapNumber::kMapOffset));
8021 __ cmp(r4, heap_number_map);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008022 __ b(ne, &slow);
ager@chromium.org357bf652010-04-12 11:30:10 +00008023 GetInt32(masm, lhs, r3, r5, r4, &slow);
8024 __ jmp(&done_checking_lhs);
8025 __ bind(&lhs_is_smi);
8026 __ mov(r3, Operand(lhs, ASR, 1));
8027 __ bind(&done_checking_lhs);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008028
ager@chromium.org357bf652010-04-12 11:30:10 +00008029 __ tst(rhs, Operand(kSmiTagMask));
8030 __ 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 +00008031 __ ldr(r4, FieldMemOperand(rhs, HeapNumber::kMapOffset));
8032 __ cmp(r4, heap_number_map);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008033 __ b(ne, &slow);
ager@chromium.org357bf652010-04-12 11:30:10 +00008034 GetInt32(masm, rhs, r2, r5, r4, &slow);
8035 __ jmp(&done_checking_rhs);
8036 __ bind(&rhs_is_smi);
8037 __ mov(r2, Operand(rhs, ASR, 1));
8038 __ bind(&done_checking_rhs);
8039
8040 ASSERT(((lhs.is(r0) && rhs.is(r1)) || (lhs.is(r1) && rhs.is(r0))));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008041
8042 // r0 and r1: Original operands (Smi or heap numbers).
8043 // r2 and r3: Signed int32 operands.
8044 switch (op_) {
8045 case Token::BIT_OR: __ orr(r2, r2, Operand(r3)); break;
8046 case Token::BIT_XOR: __ eor(r2, r2, Operand(r3)); break;
8047 case Token::BIT_AND: __ and_(r2, r2, Operand(r3)); break;
8048 case Token::SAR:
8049 // Use only the 5 least significant bits of the shift count.
8050 __ and_(r2, r2, Operand(0x1f));
8051 __ mov(r2, Operand(r3, ASR, r2));
8052 break;
8053 case Token::SHR:
8054 // Use only the 5 least significant bits of the shift count.
8055 __ and_(r2, r2, Operand(0x1f));
8056 __ mov(r2, Operand(r3, LSR, r2), SetCC);
8057 // SHR is special because it is required to produce a positive answer.
8058 // The code below for writing into heap numbers isn't capable of writing
8059 // the register as an unsigned int so we go to slow case if we hit this
8060 // case.
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00008061 if (CpuFeatures::IsSupported(VFP3)) {
8062 __ b(mi, &result_not_a_smi);
8063 } else {
8064 __ b(mi, &slow);
8065 }
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008066 break;
8067 case Token::SHL:
8068 // Use only the 5 least significant bits of the shift count.
8069 __ and_(r2, r2, Operand(0x1f));
8070 __ mov(r2, Operand(r3, LSL, r2));
8071 break;
8072 default: UNREACHABLE();
8073 }
8074 // check that the *signed* result fits in a smi
8075 __ add(r3, r2, Operand(0x40000000), SetCC);
8076 __ b(mi, &result_not_a_smi);
8077 __ mov(r0, Operand(r2, LSL, kSmiTagSize));
8078 __ Ret();
8079
8080 Label have_to_allocate, got_a_heap_number;
8081 __ bind(&result_not_a_smi);
8082 switch (mode_) {
8083 case OVERWRITE_RIGHT: {
ager@chromium.org357bf652010-04-12 11:30:10 +00008084 __ tst(rhs, Operand(kSmiTagMask));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008085 __ b(eq, &have_to_allocate);
ager@chromium.org357bf652010-04-12 11:30:10 +00008086 __ mov(r5, Operand(rhs));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008087 break;
8088 }
8089 case OVERWRITE_LEFT: {
ager@chromium.org357bf652010-04-12 11:30:10 +00008090 __ tst(lhs, Operand(kSmiTagMask));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008091 __ b(eq, &have_to_allocate);
ager@chromium.org357bf652010-04-12 11:30:10 +00008092 __ mov(r5, Operand(lhs));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008093 break;
8094 }
8095 case NO_OVERWRITE: {
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00008096 // Get a new heap number in r5. r4 and r7 are scratch.
8097 __ AllocateHeapNumber(r5, r4, r7, heap_number_map, &slow);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008098 }
8099 default: break;
8100 }
8101 __ bind(&got_a_heap_number);
8102 // r2: Answer as signed int32.
8103 // r5: Heap number to write answer into.
8104
8105 // Nothing can go wrong now, so move the heap number to r0, which is the
8106 // result.
8107 __ mov(r0, Operand(r5));
8108
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00008109 if (CpuFeatures::IsSupported(VFP3)) {
8110 // Convert the int32 in r2 to the heap number in r0. r3 is corrupted.
8111 CpuFeatures::Scope scope(VFP3);
8112 __ vmov(s0, r2);
8113 if (op_ == Token::SHR) {
8114 __ vcvt_f64_u32(d0, s0);
8115 } else {
8116 __ vcvt_f64_s32(d0, s0);
8117 }
8118 __ sub(r3, r0, Operand(kHeapObjectTag));
8119 __ vstr(d0, r3, HeapNumber::kValueOffset);
8120 __ Ret();
8121 } else {
8122 // Tail call that writes the int32 in r2 to the heap number in r0, using
8123 // r3 as scratch. r0 is preserved and returned.
8124 WriteInt32ToHeapNumberStub stub(r2, r0, r3);
8125 __ TailCallStub(&stub);
8126 }
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008127
8128 if (mode_ != NO_OVERWRITE) {
8129 __ bind(&have_to_allocate);
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00008130 // Get a new heap number in r5. r4 and r7 are scratch.
8131 __ AllocateHeapNumber(r5, r4, r7, heap_number_map, &slow);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008132 __ jmp(&got_a_heap_number);
8133 }
8134
8135 // If all else failed then we go to the runtime system.
8136 __ bind(&slow);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00008137 __ Push(lhs, rhs); // Restore stack.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008138 switch (op_) {
8139 case Token::BIT_OR:
8140 __ InvokeBuiltin(Builtins::BIT_OR, JUMP_JS);
8141 break;
8142 case Token::BIT_AND:
8143 __ InvokeBuiltin(Builtins::BIT_AND, JUMP_JS);
8144 break;
8145 case Token::BIT_XOR:
8146 __ InvokeBuiltin(Builtins::BIT_XOR, JUMP_JS);
8147 break;
8148 case Token::SAR:
8149 __ InvokeBuiltin(Builtins::SAR, JUMP_JS);
8150 break;
8151 case Token::SHR:
8152 __ InvokeBuiltin(Builtins::SHR, JUMP_JS);
8153 break;
8154 case Token::SHL:
8155 __ InvokeBuiltin(Builtins::SHL, JUMP_JS);
8156 break;
8157 default:
8158 UNREACHABLE();
8159 }
8160}
8161
8162
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008163// Can we multiply by x with max two shifts and an add.
8164// This answers yes to all integers from 2 to 10.
8165static bool IsEasyToMultiplyBy(int x) {
8166 if (x < 2) return false; // Avoid special cases.
8167 if (x > (Smi::kMaxValue + 1) >> 2) return false; // Almost always overflows.
8168 if (IsPowerOf2(x)) return true; // Simple shift.
8169 if (PopCountLessThanEqual2(x)) return true; // Shift and add and shift.
8170 if (IsPowerOf2(x + 1)) return true; // Patterns like 11111.
8171 return false;
8172}
8173
8174
8175// Can multiply by anything that IsEasyToMultiplyBy returns true for.
8176// Source and destination may be the same register. This routine does
8177// not set carry and overflow the way a mul instruction would.
8178static void MultiplyByKnownInt(MacroAssembler* masm,
8179 Register source,
8180 Register destination,
8181 int known_int) {
8182 if (IsPowerOf2(known_int)) {
8183 __ mov(destination, Operand(source, LSL, BitPosition(known_int)));
8184 } else if (PopCountLessThanEqual2(known_int)) {
8185 int first_bit = BitPosition(known_int);
8186 int second_bit = BitPosition(known_int ^ (1 << first_bit));
8187 __ add(destination, source, Operand(source, LSL, second_bit - first_bit));
8188 if (first_bit != 0) {
8189 __ mov(destination, Operand(destination, LSL, first_bit));
8190 }
8191 } else {
8192 ASSERT(IsPowerOf2(known_int + 1)); // Patterns like 1111.
8193 int the_bit = BitPosition(known_int + 1);
8194 __ rsb(destination, source, Operand(source, LSL, the_bit));
8195 }
8196}
8197
8198
8199// This function (as opposed to MultiplyByKnownInt) takes the known int in a
8200// a register for the cases where it doesn't know a good trick, and may deliver
8201// a result that needs shifting.
8202static void MultiplyByKnownInt2(
8203 MacroAssembler* masm,
8204 Register result,
8205 Register source,
8206 Register known_int_register, // Smi tagged.
8207 int known_int,
8208 int* required_shift) { // Including Smi tag shift
8209 switch (known_int) {
8210 case 3:
8211 __ add(result, source, Operand(source, LSL, 1));
8212 *required_shift = 1;
8213 break;
8214 case 5:
8215 __ add(result, source, Operand(source, LSL, 2));
8216 *required_shift = 1;
8217 break;
8218 case 6:
8219 __ add(result, source, Operand(source, LSL, 1));
8220 *required_shift = 2;
8221 break;
8222 case 7:
8223 __ rsb(result, source, Operand(source, LSL, 3));
8224 *required_shift = 1;
8225 break;
8226 case 9:
8227 __ add(result, source, Operand(source, LSL, 3));
8228 *required_shift = 1;
8229 break;
8230 case 10:
8231 __ add(result, source, Operand(source, LSL, 2));
8232 *required_shift = 2;
8233 break;
8234 default:
8235 ASSERT(!IsPowerOf2(known_int)); // That would be very inefficient.
8236 __ mul(result, source, known_int_register);
8237 *required_shift = 0;
8238 }
8239}
8240
8241
lrn@chromium.org32d961d2010-06-30 09:09:34 +00008242// This uses versions of the sum-of-digits-to-see-if-a-number-is-divisible-by-3
8243// trick. See http://en.wikipedia.org/wiki/Divisibility_rule
8244// Takes the sum of the digits base (mask + 1) repeatedly until we have a
8245// number from 0 to mask. On exit the 'eq' condition flags are set if the
8246// answer is exactly the mask.
8247void IntegerModStub::DigitSum(MacroAssembler* masm,
8248 Register lhs,
8249 int mask,
8250 int shift,
8251 Label* entry) {
8252 ASSERT(mask > 0);
8253 ASSERT(mask <= 0xff); // This ensures we don't need ip to use it.
8254 Label loop;
8255 __ bind(&loop);
8256 __ and_(ip, lhs, Operand(mask));
8257 __ add(lhs, ip, Operand(lhs, LSR, shift));
8258 __ bind(entry);
8259 __ cmp(lhs, Operand(mask));
8260 __ b(gt, &loop);
8261}
8262
8263
8264void IntegerModStub::DigitSum(MacroAssembler* masm,
8265 Register lhs,
8266 Register scratch,
8267 int mask,
8268 int shift1,
8269 int shift2,
8270 Label* entry) {
8271 ASSERT(mask > 0);
8272 ASSERT(mask <= 0xff); // This ensures we don't need ip to use it.
8273 Label loop;
8274 __ bind(&loop);
8275 __ bic(scratch, lhs, Operand(mask));
8276 __ and_(ip, lhs, Operand(mask));
8277 __ add(lhs, ip, Operand(lhs, LSR, shift1));
8278 __ add(lhs, lhs, Operand(scratch, LSR, shift2));
8279 __ bind(entry);
8280 __ cmp(lhs, Operand(mask));
8281 __ b(gt, &loop);
8282}
8283
8284
8285// Splits the number into two halves (bottom half has shift bits). The top
8286// half is subtracted from the bottom half. If the result is negative then
8287// rhs is added.
8288void IntegerModStub::ModGetInRangeBySubtraction(MacroAssembler* masm,
8289 Register lhs,
8290 int shift,
8291 int rhs) {
8292 int mask = (1 << shift) - 1;
8293 __ and_(ip, lhs, Operand(mask));
8294 __ sub(lhs, ip, Operand(lhs, LSR, shift), SetCC);
8295 __ add(lhs, lhs, Operand(rhs), LeaveCC, mi);
8296}
8297
8298
8299void IntegerModStub::ModReduce(MacroAssembler* masm,
8300 Register lhs,
8301 int max,
8302 int denominator) {
8303 int limit = denominator;
8304 while (limit * 2 <= max) limit *= 2;
8305 while (limit >= denominator) {
8306 __ cmp(lhs, Operand(limit));
8307 __ sub(lhs, lhs, Operand(limit), LeaveCC, ge);
8308 limit >>= 1;
8309 }
8310}
8311
8312
8313void IntegerModStub::ModAnswer(MacroAssembler* masm,
8314 Register result,
8315 Register shift_distance,
8316 Register mask_bits,
8317 Register sum_of_digits) {
8318 __ add(result, mask_bits, Operand(sum_of_digits, LSL, shift_distance));
8319 __ Ret();
8320}
8321
8322
8323// See comment for class.
8324void IntegerModStub::Generate(MacroAssembler* masm) {
8325 __ mov(lhs_, Operand(lhs_, LSR, shift_distance_));
8326 __ bic(odd_number_, odd_number_, Operand(1));
8327 __ mov(odd_number_, Operand(odd_number_, LSL, 1));
8328 // We now have (odd_number_ - 1) * 2 in the register.
8329 // Build a switch out of branches instead of data because it avoids
8330 // having to teach the assembler about intra-code-object pointers
8331 // that are not in relative branch instructions.
8332 Label mod3, mod5, mod7, mod9, mod11, mod13, mod15, mod17, mod19;
8333 Label mod21, mod23, mod25;
8334 { Assembler::BlockConstPoolScope block_const_pool(masm);
8335 __ add(pc, pc, Operand(odd_number_));
8336 // When you read pc it is always 8 ahead, but when you write it you always
8337 // write the actual value. So we put in two nops to take up the slack.
8338 __ nop();
8339 __ nop();
8340 __ b(&mod3);
8341 __ b(&mod5);
8342 __ b(&mod7);
8343 __ b(&mod9);
8344 __ b(&mod11);
8345 __ b(&mod13);
8346 __ b(&mod15);
8347 __ b(&mod17);
8348 __ b(&mod19);
8349 __ b(&mod21);
8350 __ b(&mod23);
8351 __ b(&mod25);
8352 }
8353
8354 // For each denominator we find a multiple that is almost only ones
8355 // when expressed in binary. Then we do the sum-of-digits trick for
8356 // that number. If the multiple is not 1 then we have to do a little
8357 // more work afterwards to get the answer into the 0-denominator-1
8358 // range.
8359 DigitSum(masm, lhs_, 3, 2, &mod3); // 3 = b11.
8360 __ sub(lhs_, lhs_, Operand(3), LeaveCC, eq);
8361 ModAnswer(masm, result_, shift_distance_, mask_bits_, lhs_);
8362
8363 DigitSum(masm, lhs_, 0xf, 4, &mod5); // 5 * 3 = b1111.
8364 ModGetInRangeBySubtraction(masm, lhs_, 2, 5);
8365 ModAnswer(masm, result_, shift_distance_, mask_bits_, lhs_);
8366
8367 DigitSum(masm, lhs_, 7, 3, &mod7); // 7 = b111.
8368 __ sub(lhs_, lhs_, Operand(7), LeaveCC, eq);
8369 ModAnswer(masm, result_, shift_distance_, mask_bits_, lhs_);
8370
8371 DigitSum(masm, lhs_, 0x3f, 6, &mod9); // 7 * 9 = b111111.
8372 ModGetInRangeBySubtraction(masm, lhs_, 3, 9);
8373 ModAnswer(masm, result_, shift_distance_, mask_bits_, lhs_);
8374
8375 DigitSum(masm, lhs_, r5, 0x3f, 6, 3, &mod11); // 5 * 11 = b110111.
8376 ModReduce(masm, lhs_, 0x3f, 11);
8377 ModAnswer(masm, result_, shift_distance_, mask_bits_, lhs_);
8378
8379 DigitSum(masm, lhs_, r5, 0xff, 8, 5, &mod13); // 19 * 13 = b11110111.
8380 ModReduce(masm, lhs_, 0xff, 13);
8381 ModAnswer(masm, result_, shift_distance_, mask_bits_, lhs_);
8382
8383 DigitSum(masm, lhs_, 0xf, 4, &mod15); // 15 = b1111.
8384 __ sub(lhs_, lhs_, Operand(15), LeaveCC, eq);
8385 ModAnswer(masm, result_, shift_distance_, mask_bits_, lhs_);
8386
8387 DigitSum(masm, lhs_, 0xff, 8, &mod17); // 15 * 17 = b11111111.
8388 ModGetInRangeBySubtraction(masm, lhs_, 4, 17);
8389 ModAnswer(masm, result_, shift_distance_, mask_bits_, lhs_);
8390
8391 DigitSum(masm, lhs_, r5, 0xff, 8, 5, &mod19); // 13 * 19 = b11110111.
8392 ModReduce(masm, lhs_, 0xff, 19);
8393 ModAnswer(masm, result_, shift_distance_, mask_bits_, lhs_);
8394
8395 DigitSum(masm, lhs_, 0x3f, 6, &mod21); // 3 * 21 = b111111.
8396 ModReduce(masm, lhs_, 0x3f, 21);
8397 ModAnswer(masm, result_, shift_distance_, mask_bits_, lhs_);
8398
8399 DigitSum(masm, lhs_, r5, 0xff, 8, 7, &mod23); // 11 * 23 = b11111101.
8400 ModReduce(masm, lhs_, 0xff, 23);
8401 ModAnswer(masm, result_, shift_distance_, mask_bits_, lhs_);
8402
8403 DigitSum(masm, lhs_, r5, 0x7f, 7, 6, &mod25); // 5 * 25 = b1111101.
8404 ModReduce(masm, lhs_, 0x7f, 25);
8405 ModAnswer(masm, result_, shift_distance_, mask_bits_, lhs_);
8406}
8407
8408
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00008409const char* GenericBinaryOpStub::GetName() {
8410 if (name_ != NULL) return name_;
8411 const int len = 100;
8412 name_ = Bootstrapper::AllocateAutoDeletedArray(len);
8413 if (name_ == NULL) return "OOM";
8414 const char* op_name = Token::Name(op_);
8415 const char* overwrite_name;
8416 switch (mode_) {
8417 case NO_OVERWRITE: overwrite_name = "Alloc"; break;
8418 case OVERWRITE_RIGHT: overwrite_name = "OverwriteRight"; break;
8419 case OVERWRITE_LEFT: overwrite_name = "OverwriteLeft"; break;
8420 default: overwrite_name = "UnknownOverwrite"; break;
8421 }
8422
8423 OS::SNPrintF(Vector<char>(name_, len),
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00008424 "GenericBinaryOpStub_%s_%s%s_%s",
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00008425 op_name,
8426 overwrite_name,
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00008427 specialized_on_rhs_ ? "_ConstantRhs" : "",
8428 BinaryOpIC::GetName(runtime_operands_type_));
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00008429 return name_;
8430}
8431
8432
ager@chromium.org5c838252010-02-19 08:53:10 +00008433
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008434void GenericBinaryOpStub::Generate(MacroAssembler* masm) {
ager@chromium.org357bf652010-04-12 11:30:10 +00008435 // lhs_ : x
8436 // rhs_ : y
8437 // r0 : result
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008438
ager@chromium.org357bf652010-04-12 11:30:10 +00008439 Register result = r0;
8440 Register lhs = lhs_;
8441 Register rhs = rhs_;
8442
8443 // This code can't cope with other register allocations yet.
8444 ASSERT(result.is(r0) &&
8445 ((lhs.is(r0) && rhs.is(r1)) ||
8446 (lhs.is(r1) && rhs.is(r0))));
8447
8448 Register smi_test_reg = VirtualFrame::scratch0();
8449 Register scratch = VirtualFrame::scratch1();
8450
8451 // All ops need to know whether we are dealing with two Smis. Set up
8452 // smi_test_reg to tell us that.
8453 if (ShouldGenerateSmiCode()) {
8454 __ orr(smi_test_reg, lhs, Operand(rhs));
8455 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008456
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008457 switch (op_) {
8458 case Token::ADD: {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008459 Label not_smi;
8460 // Fast path.
ager@chromium.org357bf652010-04-12 11:30:10 +00008461 if (ShouldGenerateSmiCode()) {
8462 ASSERT(kSmiTag == 0); // Adjust code below.
8463 __ tst(smi_test_reg, Operand(kSmiTagMask));
8464 __ b(ne, &not_smi);
8465 __ add(r0, r1, Operand(r0), SetCC); // Add y optimistically.
8466 // Return if no overflow.
8467 __ Ret(vc);
8468 __ sub(r0, r0, Operand(r1)); // Revert optimistic add.
8469 }
8470 HandleBinaryOpSlowCases(masm, &not_smi, lhs, rhs, Builtins::ADD);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008471 break;
8472 }
8473
8474 case Token::SUB: {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008475 Label not_smi;
8476 // Fast path.
ager@chromium.org357bf652010-04-12 11:30:10 +00008477 if (ShouldGenerateSmiCode()) {
8478 ASSERT(kSmiTag == 0); // Adjust code below.
8479 __ tst(smi_test_reg, Operand(kSmiTagMask));
8480 __ b(ne, &not_smi);
8481 if (lhs.is(r1)) {
8482 __ sub(r0, r1, Operand(r0), SetCC); // Subtract y optimistically.
8483 // Return if no overflow.
8484 __ Ret(vc);
8485 __ sub(r0, r1, Operand(r0)); // Revert optimistic subtract.
8486 } else {
8487 __ sub(r0, r0, Operand(r1), SetCC); // Subtract y optimistically.
8488 // Return if no overflow.
8489 __ Ret(vc);
8490 __ add(r0, r0, Operand(r1)); // Revert optimistic subtract.
8491 }
8492 }
8493 HandleBinaryOpSlowCases(masm, &not_smi, lhs, rhs, Builtins::SUB);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008494 break;
8495 }
8496
8497 case Token::MUL: {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008498 Label not_smi, slow;
ager@chromium.org357bf652010-04-12 11:30:10 +00008499 if (ShouldGenerateSmiCode()) {
8500 ASSERT(kSmiTag == 0); // adjust code below
8501 __ tst(smi_test_reg, Operand(kSmiTagMask));
8502 Register scratch2 = smi_test_reg;
8503 smi_test_reg = no_reg;
8504 __ b(ne, &not_smi);
8505 // Remove tag from one operand (but keep sign), so that result is Smi.
8506 __ mov(ip, Operand(rhs, ASR, kSmiTagSize));
8507 // Do multiplication
8508 // scratch = lower 32 bits of ip * lhs.
8509 __ smull(scratch, scratch2, lhs, ip);
8510 // Go slow on overflows (overflow bit is not set).
8511 __ mov(ip, Operand(scratch, ASR, 31));
8512 // No overflow if higher 33 bits are identical.
8513 __ cmp(ip, Operand(scratch2));
8514 __ b(ne, &slow);
8515 // Go slow on zero result to handle -0.
8516 __ tst(scratch, Operand(scratch));
8517 __ mov(result, Operand(scratch), LeaveCC, ne);
8518 __ Ret(ne);
8519 // We need -0 if we were multiplying a negative number with 0 to get 0.
8520 // We know one of them was zero.
8521 __ add(scratch2, rhs, Operand(lhs), SetCC);
8522 __ mov(result, Operand(Smi::FromInt(0)), LeaveCC, pl);
8523 __ Ret(pl); // Return Smi 0 if the non-zero one was positive.
8524 // Slow case. We fall through here if we multiplied a negative number
8525 // with 0, because that would mean we should produce -0.
8526 __ bind(&slow);
8527 }
8528 HandleBinaryOpSlowCases(masm, &not_smi, lhs, rhs, Builtins::MUL);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008529 break;
8530 }
8531
8532 case Token::DIV:
8533 case Token::MOD: {
8534 Label not_smi;
ager@chromium.org357bf652010-04-12 11:30:10 +00008535 if (ShouldGenerateSmiCode() && specialized_on_rhs_) {
lrn@chromium.org32d961d2010-06-30 09:09:34 +00008536 Label lhs_is_unsuitable;
ager@chromium.org357bf652010-04-12 11:30:10 +00008537 __ BranchOnNotSmi(lhs, &not_smi);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008538 if (IsPowerOf2(constant_rhs_)) {
8539 if (op_ == Token::MOD) {
ager@chromium.org357bf652010-04-12 11:30:10 +00008540 __ and_(rhs,
8541 lhs,
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008542 Operand(0x80000000u | ((constant_rhs_ << kSmiTagSize) - 1)),
8543 SetCC);
8544 // We now have the answer, but if the input was negative we also
8545 // have the sign bit. Our work is done if the result is
8546 // positive or zero:
ager@chromium.org357bf652010-04-12 11:30:10 +00008547 if (!rhs.is(r0)) {
8548 __ mov(r0, rhs, LeaveCC, pl);
8549 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008550 __ Ret(pl);
8551 // A mod of a negative left hand side must return a negative number.
8552 // Unfortunately if the answer is 0 then we must return -0. And we
ager@chromium.org357bf652010-04-12 11:30:10 +00008553 // already optimistically trashed rhs so we may need to restore it.
8554 __ eor(rhs, rhs, Operand(0x80000000u), SetCC);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008555 // Next two instructions are conditional on the answer being -0.
ager@chromium.org357bf652010-04-12 11:30:10 +00008556 __ mov(rhs, Operand(Smi::FromInt(constant_rhs_)), LeaveCC, eq);
lrn@chromium.org32d961d2010-06-30 09:09:34 +00008557 __ b(eq, &lhs_is_unsuitable);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008558 // We need to subtract the dividend. Eg. -3 % 4 == -3.
ager@chromium.org357bf652010-04-12 11:30:10 +00008559 __ sub(result, rhs, Operand(Smi::FromInt(constant_rhs_)));
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008560 } else {
8561 ASSERT(op_ == Token::DIV);
ager@chromium.org357bf652010-04-12 11:30:10 +00008562 __ tst(lhs,
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008563 Operand(0x80000000u | ((constant_rhs_ << kSmiTagSize) - 1)));
lrn@chromium.org32d961d2010-06-30 09:09:34 +00008564 __ b(ne, &lhs_is_unsuitable); // Go slow on negative or remainder.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008565 int shift = 0;
8566 int d = constant_rhs_;
8567 while ((d & 1) == 0) {
8568 d >>= 1;
8569 shift++;
8570 }
ager@chromium.org357bf652010-04-12 11:30:10 +00008571 __ mov(r0, Operand(lhs, LSR, shift));
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008572 __ bic(r0, r0, Operand(kSmiTagMask));
8573 }
8574 } else {
8575 // Not a power of 2.
ager@chromium.org357bf652010-04-12 11:30:10 +00008576 __ tst(lhs, Operand(0x80000000u));
lrn@chromium.org32d961d2010-06-30 09:09:34 +00008577 __ b(ne, &lhs_is_unsuitable);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008578 // Find a fixed point reciprocal of the divisor so we can divide by
8579 // multiplying.
8580 double divisor = 1.0 / constant_rhs_;
8581 int shift = 32;
8582 double scale = 4294967296.0; // 1 << 32.
8583 uint32_t mul;
8584 // Maximise the precision of the fixed point reciprocal.
8585 while (true) {
8586 mul = static_cast<uint32_t>(scale * divisor);
8587 if (mul >= 0x7fffffff) break;
8588 scale *= 2.0;
8589 shift++;
8590 }
8591 mul++;
ager@chromium.org357bf652010-04-12 11:30:10 +00008592 Register scratch2 = smi_test_reg;
8593 smi_test_reg = no_reg;
8594 __ mov(scratch2, Operand(mul));
8595 __ umull(scratch, scratch2, scratch2, lhs);
8596 __ mov(scratch2, Operand(scratch2, LSR, shift - 31));
8597 // scratch2 is lhs / rhs. scratch2 is not Smi tagged.
8598 // rhs is still the known rhs. rhs is Smi tagged.
8599 // lhs is still the unkown lhs. lhs is Smi tagged.
8600 int required_scratch_shift = 0; // Including the Smi tag shift of 1.
8601 // scratch = scratch2 * rhs.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008602 MultiplyByKnownInt2(masm,
ager@chromium.org357bf652010-04-12 11:30:10 +00008603 scratch,
8604 scratch2,
8605 rhs,
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008606 constant_rhs_,
ager@chromium.org357bf652010-04-12 11:30:10 +00008607 &required_scratch_shift);
8608 // scratch << required_scratch_shift is now the Smi tagged rhs *
8609 // (lhs / rhs) where / indicates integer division.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008610 if (op_ == Token::DIV) {
ager@chromium.org357bf652010-04-12 11:30:10 +00008611 __ cmp(lhs, Operand(scratch, LSL, required_scratch_shift));
lrn@chromium.org32d961d2010-06-30 09:09:34 +00008612 __ b(ne, &lhs_is_unsuitable); // There was a remainder.
ager@chromium.org357bf652010-04-12 11:30:10 +00008613 __ mov(result, Operand(scratch2, LSL, kSmiTagSize));
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008614 } else {
8615 ASSERT(op_ == Token::MOD);
ager@chromium.org357bf652010-04-12 11:30:10 +00008616 __ sub(result, lhs, Operand(scratch, LSL, required_scratch_shift));
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008617 }
8618 }
8619 __ Ret();
lrn@chromium.org32d961d2010-06-30 09:09:34 +00008620 __ bind(&lhs_is_unsuitable);
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00008621 } else if (op_ == Token::MOD &&
8622 runtime_operands_type_ != BinaryOpIC::HEAP_NUMBERS &&
8623 runtime_operands_type_ != BinaryOpIC::STRINGS) {
8624 // Do generate a bit of smi code for modulus even though the default for
8625 // modulus is not to do it, but as the ARM processor has no coprocessor
lrn@chromium.org32d961d2010-06-30 09:09:34 +00008626 // support for modulus checking for smis makes sense. We can handle
8627 // 1 to 25 times any power of 2. This covers over half the numbers from
8628 // 1 to 100 including all of the first 25. (Actually the constants < 10
8629 // are handled above by reciprocal multiplication. We only get here for
8630 // those cases if the right hand side is not a constant or for cases
8631 // like 192 which is 3*2^6 and ends up in the 3 case in the integer mod
8632 // stub.)
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00008633 Label slow;
lrn@chromium.org32d961d2010-06-30 09:09:34 +00008634 Label not_power_of_2;
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00008635 ASSERT(!ShouldGenerateSmiCode());
8636 ASSERT(kSmiTag == 0); // Adjust code below.
8637 // Check for two positive smis.
8638 __ orr(smi_test_reg, lhs, Operand(rhs));
8639 __ tst(smi_test_reg, Operand(0x80000000u | kSmiTagMask));
8640 __ b(ne, &slow);
8641 // Check that rhs is a power of two and not zero.
lrn@chromium.org32d961d2010-06-30 09:09:34 +00008642 Register mask_bits = r3;
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00008643 __ sub(scratch, rhs, Operand(1), SetCC);
8644 __ b(mi, &slow);
lrn@chromium.org32d961d2010-06-30 09:09:34 +00008645 __ and_(mask_bits, rhs, Operand(scratch), SetCC);
8646 __ b(ne, &not_power_of_2);
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00008647 // Calculate power of two modulus.
8648 __ and_(result, lhs, Operand(scratch));
8649 __ Ret();
lrn@chromium.org32d961d2010-06-30 09:09:34 +00008650
8651 __ bind(&not_power_of_2);
8652 __ eor(scratch, scratch, Operand(mask_bits));
8653 // At least two bits are set in the modulus. The high one(s) are in
8654 // mask_bits and the low one is scratch + 1.
8655 __ and_(mask_bits, scratch, Operand(lhs));
8656 Register shift_distance = scratch;
8657 scratch = no_reg;
8658
8659 // The rhs consists of a power of 2 multiplied by some odd number.
8660 // The power-of-2 part we handle by putting the corresponding bits
8661 // from the lhs in the mask_bits register, and the power in the
8662 // shift_distance register. Shift distance is never 0 due to Smi
8663 // tagging.
8664 __ CountLeadingZeros(r4, shift_distance, shift_distance);
8665 __ rsb(shift_distance, r4, Operand(32));
8666
8667 // Now we need to find out what the odd number is. The last bit is
8668 // always 1.
8669 Register odd_number = r4;
8670 __ mov(odd_number, Operand(rhs, LSR, shift_distance));
8671 __ cmp(odd_number, Operand(25));
8672 __ b(gt, &slow);
8673
8674 IntegerModStub stub(
8675 result, shift_distance, odd_number, mask_bits, lhs, r5);
8676 __ Jump(stub.GetCode(), RelocInfo::CODE_TARGET); // Tail call.
8677
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00008678 __ bind(&slow);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008679 }
ager@chromium.org357bf652010-04-12 11:30:10 +00008680 HandleBinaryOpSlowCases(
8681 masm,
8682 &not_smi,
8683 lhs,
8684 rhs,
8685 op_ == Token::MOD ? Builtins::MOD : Builtins::DIV);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008686 break;
8687 }
8688
8689 case Token::BIT_OR:
8690 case Token::BIT_AND:
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008691 case Token::BIT_XOR:
8692 case Token::SAR:
8693 case Token::SHR:
8694 case Token::SHL: {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008695 Label slow;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008696 ASSERT(kSmiTag == 0); // adjust code below
ager@chromium.org357bf652010-04-12 11:30:10 +00008697 __ tst(smi_test_reg, Operand(kSmiTagMask));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008698 __ b(ne, &slow);
ager@chromium.org357bf652010-04-12 11:30:10 +00008699 Register scratch2 = smi_test_reg;
8700 smi_test_reg = no_reg;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008701 switch (op_) {
ager@chromium.org357bf652010-04-12 11:30:10 +00008702 case Token::BIT_OR: __ orr(result, rhs, Operand(lhs)); break;
8703 case Token::BIT_AND: __ and_(result, rhs, Operand(lhs)); break;
8704 case Token::BIT_XOR: __ eor(result, rhs, Operand(lhs)); break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008705 case Token::SAR:
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008706 // Remove tags from right operand.
ager@chromium.org357bf652010-04-12 11:30:10 +00008707 __ GetLeastBitsFromSmi(scratch2, rhs, 5);
8708 __ mov(result, Operand(lhs, ASR, scratch2));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008709 // Smi tag result.
ager@chromium.org357bf652010-04-12 11:30:10 +00008710 __ bic(result, result, Operand(kSmiTagMask));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008711 break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008712 case Token::SHR:
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008713 // Remove tags from operands. We can't do this on a 31 bit number
8714 // because then the 0s get shifted into bit 30 instead of bit 31.
ager@chromium.org357bf652010-04-12 11:30:10 +00008715 __ mov(scratch, Operand(lhs, ASR, kSmiTagSize)); // x
8716 __ GetLeastBitsFromSmi(scratch2, rhs, 5);
8717 __ mov(scratch, Operand(scratch, LSR, scratch2));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008718 // Unsigned shift is not allowed to produce a negative number, so
8719 // check the sign bit and the sign bit after Smi tagging.
ager@chromium.org357bf652010-04-12 11:30:10 +00008720 __ tst(scratch, Operand(0xc0000000));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008721 __ b(ne, &slow);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008722 // Smi tag result.
ager@chromium.org357bf652010-04-12 11:30:10 +00008723 __ mov(result, Operand(scratch, LSL, kSmiTagSize));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008724 break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008725 case Token::SHL:
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008726 // Remove tags from operands.
ager@chromium.org357bf652010-04-12 11:30:10 +00008727 __ mov(scratch, Operand(lhs, ASR, kSmiTagSize)); // x
8728 __ GetLeastBitsFromSmi(scratch2, rhs, 5);
8729 __ mov(scratch, Operand(scratch, LSL, scratch2));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008730 // Check that the signed result fits in a Smi.
ager@chromium.org357bf652010-04-12 11:30:10 +00008731 __ add(scratch2, scratch, Operand(0x40000000), SetCC);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008732 __ b(mi, &slow);
ager@chromium.org357bf652010-04-12 11:30:10 +00008733 __ mov(result, Operand(scratch, LSL, kSmiTagSize));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008734 break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008735 default: UNREACHABLE();
8736 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008737 __ Ret();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008738 __ bind(&slow);
ager@chromium.org357bf652010-04-12 11:30:10 +00008739 HandleNonSmiBitwiseOp(masm, lhs, rhs);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008740 break;
8741 }
8742
8743 default: UNREACHABLE();
8744 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008745 // This code should be unreachable.
8746 __ stop("Unreachable");
ager@chromium.org357bf652010-04-12 11:30:10 +00008747
8748 // Generate an unreachable reference to the DEFAULT stub so that it can be
8749 // found at the end of this stub when clearing ICs at GC.
8750 // TODO(kaznacheev): Check performance impact and get rid of this.
8751 if (runtime_operands_type_ != BinaryOpIC::DEFAULT) {
8752 GenericBinaryOpStub uninit(MinorKey(), BinaryOpIC::DEFAULT);
8753 __ CallStub(&uninit);
8754 }
8755}
8756
8757
8758void GenericBinaryOpStub::GenerateTypeTransition(MacroAssembler* masm) {
8759 Label get_result;
8760
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00008761 __ Push(r1, r0);
ager@chromium.org357bf652010-04-12 11:30:10 +00008762
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00008763 __ mov(r2, Operand(Smi::FromInt(MinorKey())));
8764 __ mov(r1, Operand(Smi::FromInt(op_)));
ager@chromium.org357bf652010-04-12 11:30:10 +00008765 __ mov(r0, Operand(Smi::FromInt(runtime_operands_type_)));
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00008766 __ Push(r2, r1, r0);
ager@chromium.org357bf652010-04-12 11:30:10 +00008767
8768 __ TailCallExternalReference(
8769 ExternalReference(IC_Utility(IC::kBinaryOp_Patch)),
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00008770 5,
ager@chromium.org357bf652010-04-12 11:30:10 +00008771 1);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008772}
8773
8774
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00008775Handle<Code> GetBinaryOpStub(int key, BinaryOpIC::TypeInfo type_info) {
ager@chromium.org357bf652010-04-12 11:30:10 +00008776 GenericBinaryOpStub stub(key, type_info);
8777 return stub.GetCode();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00008778}
8779
8780
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008781void TranscendentalCacheStub::Generate(MacroAssembler* masm) {
8782 // Argument is a number and is on stack and in r0.
8783 Label runtime_call;
8784 Label input_not_smi;
8785 Label loaded;
8786
8787 if (CpuFeatures::IsSupported(VFP3)) {
8788 // Load argument and check if it is a smi.
8789 __ BranchOnNotSmi(r0, &input_not_smi);
8790
8791 CpuFeatures::Scope scope(VFP3);
8792 // Input is a smi. Convert to double and load the low and high words
8793 // of the double into r2, r3.
8794 __ IntegerToDoubleConversionWithVFP3(r0, r3, r2);
8795 __ b(&loaded);
8796
8797 __ bind(&input_not_smi);
8798 // Check if input is a HeapNumber.
8799 __ CheckMap(r0,
8800 r1,
8801 Heap::kHeapNumberMapRootIndex,
8802 &runtime_call,
8803 true);
8804 // Input is a HeapNumber. Load it to a double register and store the
8805 // low and high words into r2, r3.
8806 __ Ldrd(r2, r3, FieldMemOperand(r0, HeapNumber::kValueOffset));
8807
8808 __ bind(&loaded);
8809 // r2 = low 32 bits of double value
8810 // r3 = high 32 bits of double value
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00008811 // Compute hash (the shifts are arithmetic):
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008812 // h = (low ^ high); h ^= h >> 16; h ^= h >> 8; h = h & (cacheSize - 1);
8813 __ eor(r1, r2, Operand(r3));
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00008814 __ eor(r1, r1, Operand(r1, ASR, 16));
8815 __ eor(r1, r1, Operand(r1, ASR, 8));
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008816 ASSERT(IsPowerOf2(TranscendentalCache::kCacheSize));
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00008817 __ And(r1, r1, Operand(TranscendentalCache::kCacheSize - 1));
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008818
8819 // r2 = low 32 bits of double value.
8820 // r3 = high 32 bits of double value.
8821 // r1 = TranscendentalCache::hash(double value).
8822 __ mov(r0,
8823 Operand(ExternalReference::transcendental_cache_array_address()));
8824 // r0 points to cache array.
8825 __ ldr(r0, MemOperand(r0, type_ * sizeof(TranscendentalCache::caches_[0])));
8826 // r0 points to the cache for the type type_.
8827 // If NULL, the cache hasn't been initialized yet, so go through runtime.
8828 __ cmp(r0, Operand(0));
8829 __ b(eq, &runtime_call);
8830
8831#ifdef DEBUG
8832 // Check that the layout of cache elements match expectations.
8833 { TranscendentalCache::Element test_elem[2];
8834 char* elem_start = reinterpret_cast<char*>(&test_elem[0]);
8835 char* elem2_start = reinterpret_cast<char*>(&test_elem[1]);
8836 char* elem_in0 = reinterpret_cast<char*>(&(test_elem[0].in[0]));
8837 char* elem_in1 = reinterpret_cast<char*>(&(test_elem[0].in[1]));
8838 char* elem_out = reinterpret_cast<char*>(&(test_elem[0].output));
8839 CHECK_EQ(12, elem2_start - elem_start); // Two uint_32's and a pointer.
8840 CHECK_EQ(0, elem_in0 - elem_start);
8841 CHECK_EQ(kIntSize, elem_in1 - elem_start);
8842 CHECK_EQ(2 * kIntSize, elem_out - elem_start);
8843 }
8844#endif
8845
8846 // Find the address of the r1'st entry in the cache, i.e., &r0[r1*12].
8847 __ add(r1, r1, Operand(r1, LSL, 1));
8848 __ add(r0, r0, Operand(r1, LSL, 2));
8849 // Check if cache matches: Double value is stored in uint32_t[2] array.
8850 __ ldm(ia, r0, r4.bit()| r5.bit() | r6.bit());
8851 __ cmp(r2, r4);
8852 __ b(ne, &runtime_call);
8853 __ cmp(r3, r5);
8854 __ b(ne, &runtime_call);
8855 // Cache hit. Load result, pop argument and return.
8856 __ mov(r0, Operand(r6));
8857 __ pop();
8858 __ Ret();
8859 }
8860
8861 __ bind(&runtime_call);
8862 __ TailCallExternalReference(ExternalReference(RuntimeFunction()), 1, 1);
8863}
8864
8865
8866Runtime::FunctionId TranscendentalCacheStub::RuntimeFunction() {
8867 switch (type_) {
8868 // Add more cases when necessary.
8869 case TranscendentalCache::SIN: return Runtime::kMath_sin;
8870 case TranscendentalCache::COS: return Runtime::kMath_cos;
8871 default:
8872 UNIMPLEMENTED();
8873 return Runtime::kAbort;
8874 }
8875}
8876
8877
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008878void StackCheckStub::Generate(MacroAssembler* masm) {
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00008879 // Do tail-call to runtime routine. Runtime routines expect at least one
8880 // argument, so give it a Smi.
8881 __ mov(r0, Operand(Smi::FromInt(0)));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008882 __ push(r0);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00008883 __ TailCallRuntime(Runtime::kStackGuard, 1, 1);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008884
8885 __ StubReturn(1);
8886}
8887
8888
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00008889void GenericUnaryOpStub::Generate(MacroAssembler* masm) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008890 Label slow, done;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00008891
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00008892 Register heap_number_map = r6;
8893 __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
8894
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008895 if (op_ == Token::SUB) {
8896 // Check whether the value is a smi.
8897 Label try_float;
8898 __ tst(r0, Operand(kSmiTagMask));
8899 __ b(ne, &try_float);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008900
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008901 // Go slow case if the value of the expression is zero
8902 // to make sure that we switch between 0 and -0.
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00008903 if (negative_zero_ == kStrictNegativeZero) {
8904 // If we have to check for zero, then we can check for the max negative
8905 // smi while we are at it.
8906 __ bic(ip, r0, Operand(0x80000000), SetCC);
8907 __ b(eq, &slow);
8908 __ rsb(r0, r0, Operand(0));
8909 __ StubReturn(1);
8910 } else {
8911 // The value of the expression is a smi and 0 is OK for -0. Try
8912 // optimistic subtraction '0 - value'.
8913 __ rsb(r0, r0, Operand(0), SetCC);
8914 __ StubReturn(1, vc);
8915 // We don't have to reverse the optimistic neg since the only case
8916 // where we fall through is the minimum negative Smi, which is the case
8917 // where the neg leaves the register unchanged.
8918 __ jmp(&slow); // Go slow on max negative Smi.
8919 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008920
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008921 __ bind(&try_float);
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00008922 __ ldr(r1, FieldMemOperand(r0, HeapObject::kMapOffset));
8923 __ AssertRegisterIsRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
8924 __ cmp(r1, heap_number_map);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008925 __ b(ne, &slow);
8926 // r0 is a heap number. Get a new heap number in r1.
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00008927 if (overwrite_ == UNARY_OVERWRITE) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008928 __ ldr(r2, FieldMemOperand(r0, HeapNumber::kExponentOffset));
8929 __ eor(r2, r2, Operand(HeapNumber::kSignMask)); // Flip sign.
8930 __ str(r2, FieldMemOperand(r0, HeapNumber::kExponentOffset));
8931 } else {
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00008932 __ AllocateHeapNumber(r1, r2, r3, r6, &slow);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008933 __ ldr(r3, FieldMemOperand(r0, HeapNumber::kMantissaOffset));
8934 __ ldr(r2, FieldMemOperand(r0, HeapNumber::kExponentOffset));
8935 __ str(r3, FieldMemOperand(r1, HeapNumber::kMantissaOffset));
8936 __ eor(r2, r2, Operand(HeapNumber::kSignMask)); // Flip sign.
8937 __ str(r2, FieldMemOperand(r1, HeapNumber::kExponentOffset));
8938 __ mov(r0, Operand(r1));
8939 }
8940 } else if (op_ == Token::BIT_NOT) {
8941 // Check if the operand is a heap number.
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00008942 __ ldr(r1, FieldMemOperand(r0, HeapObject::kMapOffset));
8943 __ AssertRegisterIsRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
8944 __ cmp(r1, heap_number_map);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008945 __ b(ne, &slow);
8946
8947 // Convert the heap number is r0 to an untagged integer in r1.
8948 GetInt32(masm, r0, r1, r2, r3, &slow);
8949
8950 // Do the bitwise operation (move negated) and check if the result
8951 // fits in a smi.
8952 Label try_float;
8953 __ mvn(r1, Operand(r1));
8954 __ add(r2, r1, Operand(0x40000000), SetCC);
8955 __ b(mi, &try_float);
8956 __ mov(r0, Operand(r1, LSL, kSmiTagSize));
8957 __ b(&done);
8958
8959 __ bind(&try_float);
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00008960 if (!overwrite_ == UNARY_OVERWRITE) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008961 // Allocate a fresh heap number, but don't overwrite r0 until
8962 // we're sure we can do it without going through the slow case
8963 // that needs the value in r0.
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00008964 __ AllocateHeapNumber(r2, r3, r4, r6, &slow);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008965 __ mov(r0, Operand(r2));
8966 }
8967
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00008968 if (CpuFeatures::IsSupported(VFP3)) {
8969 // Convert the int32 in r1 to the heap number in r0. r2 is corrupted.
8970 CpuFeatures::Scope scope(VFP3);
8971 __ vmov(s0, r1);
8972 __ vcvt_f64_s32(d0, s0);
8973 __ sub(r2, r0, Operand(kHeapObjectTag));
8974 __ vstr(d0, r2, HeapNumber::kValueOffset);
8975 } else {
8976 // WriteInt32ToHeapNumberStub does not trigger GC, so we do not
8977 // have to set up a frame.
8978 WriteInt32ToHeapNumberStub stub(r1, r0, r2);
8979 __ push(lr);
8980 __ Call(stub.GetCode(), RelocInfo::CODE_TARGET);
8981 __ pop(lr);
8982 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008983 } else {
8984 UNIMPLEMENTED();
8985 }
8986
8987 __ bind(&done);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008988 __ StubReturn(1);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008989
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008990 // Handle the slow case by jumping to the JavaScript builtin.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008991 __ bind(&slow);
8992 __ push(r0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008993 switch (op_) {
8994 case Token::SUB:
8995 __ InvokeBuiltin(Builtins::UNARY_MINUS, JUMP_JS);
8996 break;
8997 case Token::BIT_NOT:
8998 __ InvokeBuiltin(Builtins::BIT_NOT, JUMP_JS);
8999 break;
9000 default:
9001 UNREACHABLE();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009002 }
ager@chromium.orga1645e22009-09-09 19:27:10 +00009003}
9004
9005
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009006void CEntryStub::GenerateThrowTOS(MacroAssembler* masm) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009007 // r0 holds the exception.
9008
9009 // Adjust this code if not the case.
9010 ASSERT(StackHandlerConstants::kSize == 4 * kPointerSize);
9011
9012 // Drop the sp to the top of the handler.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009013 __ mov(r3, Operand(ExternalReference(Top::k_handler_address)));
9014 __ ldr(sp, MemOperand(r3));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009015
9016 // Restore the next handler and frame pointer, discard handler state.
9017 ASSERT(StackHandlerConstants::kNextOffset == 0);
9018 __ pop(r2);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009019 __ str(r2, MemOperand(r3));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009020 ASSERT(StackHandlerConstants::kFPOffset == 2 * kPointerSize);
9021 __ ldm(ia_w, sp, r3.bit() | fp.bit()); // r3: discarded state.
9022
9023 // Before returning we restore the context from the frame pointer if
9024 // not NULL. The frame pointer is NULL in the exception handler of a
9025 // JS entry frame.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009026 __ cmp(fp, Operand(0));
9027 // Set cp to NULL if fp is NULL.
9028 __ mov(cp, Operand(0), LeaveCC, eq);
9029 // Restore cp otherwise.
9030 __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset), ne);
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009031#ifdef DEBUG
9032 if (FLAG_debug_code) {
9033 __ mov(lr, Operand(pc));
9034 }
9035#endif
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009036 ASSERT(StackHandlerConstants::kPCOffset == 3 * kPointerSize);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009037 __ pop(pc);
9038}
9039
9040
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00009041void CEntryStub::GenerateThrowUncatchable(MacroAssembler* masm,
9042 UncatchableExceptionType type) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009043 // Adjust this code if not the case.
9044 ASSERT(StackHandlerConstants::kSize == 4 * kPointerSize);
9045
9046 // Drop sp to the top stack handler.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009047 __ mov(r3, Operand(ExternalReference(Top::k_handler_address)));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009048 __ ldr(sp, MemOperand(r3));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009049
9050 // Unwind the handlers until the ENTRY handler is found.
9051 Label loop, done;
9052 __ bind(&loop);
9053 // Load the type of the current stack handler.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009054 const int kStateOffset = StackHandlerConstants::kStateOffset;
9055 __ ldr(r2, MemOperand(sp, kStateOffset));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009056 __ cmp(r2, Operand(StackHandler::ENTRY));
9057 __ b(eq, &done);
9058 // Fetch the next handler in the list.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009059 const int kNextOffset = StackHandlerConstants::kNextOffset;
9060 __ ldr(sp, MemOperand(sp, kNextOffset));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009061 __ jmp(&loop);
9062 __ bind(&done);
9063
9064 // Set the top handler address to next handler past the current ENTRY handler.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009065 ASSERT(StackHandlerConstants::kNextOffset == 0);
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00009066 __ pop(r2);
9067 __ str(r2, MemOperand(r3));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009068
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00009069 if (type == OUT_OF_MEMORY) {
9070 // Set external caught exception to false.
9071 ExternalReference external_caught(Top::k_external_caught_exception_address);
9072 __ mov(r0, Operand(false));
9073 __ mov(r2, Operand(external_caught));
9074 __ str(r0, MemOperand(r2));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009075
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00009076 // Set pending exception and r0 to out of memory exception.
9077 Failure* out_of_memory = Failure::OutOfMemoryException();
9078 __ mov(r0, Operand(reinterpret_cast<int32_t>(out_of_memory)));
9079 __ mov(r2, Operand(ExternalReference(Top::k_pending_exception_address)));
9080 __ str(r0, MemOperand(r2));
9081 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009082
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009083 // Stack layout at this point. See also StackHandlerConstants.
9084 // sp -> state (ENTRY)
9085 // fp
9086 // lr
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009087
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009088 // Discard handler state (r2 is not used) and restore frame pointer.
9089 ASSERT(StackHandlerConstants::kFPOffset == 2 * kPointerSize);
9090 __ ldm(ia_w, sp, r2.bit() | fp.bit()); // r2: discarded state.
9091 // Before returning we restore the context from the frame pointer if
9092 // not NULL. The frame pointer is NULL in the exception handler of a
9093 // JS entry frame.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009094 __ cmp(fp, Operand(0));
9095 // Set cp to NULL if fp is NULL.
9096 __ mov(cp, Operand(0), LeaveCC, eq);
9097 // Restore cp otherwise.
9098 __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset), ne);
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009099#ifdef DEBUG
9100 if (FLAG_debug_code) {
9101 __ mov(lr, Operand(pc));
9102 }
9103#endif
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009104 ASSERT(StackHandlerConstants::kPCOffset == 3 * kPointerSize);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009105 __ pop(pc);
9106}
9107
9108
9109void CEntryStub::GenerateCore(MacroAssembler* masm,
9110 Label* throw_normal_exception,
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00009111 Label* throw_termination_exception,
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009112 Label* throw_out_of_memory_exception,
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00009113 bool do_gc,
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009114 bool always_allocate,
9115 int frame_alignment_skew) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009116 // r0: result parameter for PerformGC, if any
9117 // r4: number of arguments including receiver (C callee-saved)
9118 // r5: pointer to builtin function (C callee-saved)
9119 // r6: pointer to the first argument (C callee-saved)
9120
9121 if (do_gc) {
9122 // Passing r0.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009123 __ PrepareCallCFunction(1, r1);
9124 __ CallCFunction(ExternalReference::perform_gc_function(), 1);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009125 }
9126
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00009127 ExternalReference scope_depth =
9128 ExternalReference::heap_always_allocate_scope_depth();
9129 if (always_allocate) {
9130 __ mov(r0, Operand(scope_depth));
9131 __ ldr(r1, MemOperand(r0));
9132 __ add(r1, r1, Operand(1));
9133 __ str(r1, MemOperand(r0));
9134 }
9135
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009136 // Call C built-in.
9137 // r0 = argc, r1 = argv
9138 __ mov(r0, Operand(r4));
9139 __ mov(r1, Operand(r6));
9140
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009141 int frame_alignment = MacroAssembler::ActivationFrameAlignment();
9142 int frame_alignment_mask = frame_alignment - 1;
9143#if defined(V8_HOST_ARCH_ARM)
9144 if (FLAG_debug_code) {
9145 if (frame_alignment > kPointerSize) {
9146 Label alignment_as_expected;
9147 ASSERT(IsPowerOf2(frame_alignment));
9148 __ sub(r2, sp, Operand(frame_alignment_skew));
9149 __ tst(r2, Operand(frame_alignment_mask));
9150 __ b(eq, &alignment_as_expected);
9151 // Don't use Check here, as it will call Runtime_Abort re-entering here.
9152 __ stop("Unexpected alignment");
9153 __ bind(&alignment_as_expected);
9154 }
9155 }
9156#endif
9157
9158 // Just before the call (jump) below lr is pushed, so the actual alignment is
9159 // adding one to the current skew.
9160 int alignment_before_call =
9161 (frame_alignment_skew + kPointerSize) & frame_alignment_mask;
9162 if (alignment_before_call > 0) {
9163 // Push until the alignment before the call is met.
9164 __ mov(r2, Operand(0));
9165 for (int i = alignment_before_call;
9166 (i & frame_alignment_mask) != 0;
9167 i += kPointerSize) {
9168 __ push(r2);
9169 }
9170 }
9171
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009172 // TODO(1242173): To let the GC traverse the return address of the exit
9173 // frames, we need to know where the return address is. Right now,
9174 // we push it on the stack to be able to find it again, but we never
9175 // restore from it in case of changes, which makes it impossible to
9176 // support moving the C entry code stub. This should be fixed, but currently
9177 // this is OK because the CEntryStub gets generated so early in the V8 boot
9178 // sequence that it is not moving ever.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009179 masm->add(lr, pc, Operand(4)); // Compute return address: (pc + 8) + 4
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009180 masm->push(lr);
9181 masm->Jump(r5);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00009182
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009183 // Restore sp back to before aligning the stack.
9184 if (alignment_before_call > 0) {
9185 __ add(sp, sp, Operand(alignment_before_call));
9186 }
9187
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00009188 if (always_allocate) {
9189 // It's okay to clobber r2 and r3 here. Don't mess with r0 and r1
9190 // though (contain the result).
9191 __ mov(r2, Operand(scope_depth));
9192 __ ldr(r3, MemOperand(r2));
9193 __ sub(r3, r3, Operand(1));
9194 __ str(r3, MemOperand(r2));
9195 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009196
9197 // check for failure result
9198 Label failure_returned;
9199 ASSERT(((kFailureTag + 1) & kFailureTagMask) == 0);
9200 // Lower 2 bits of r2 are 0 iff r0 has failure tag.
9201 __ add(r2, r0, Operand(1));
9202 __ tst(r2, Operand(kFailureTagMask));
9203 __ b(eq, &failure_returned);
9204
9205 // Exit C frame and return.
9206 // r0:r1: result
9207 // sp: stack pointer
9208 // fp: frame pointer
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009209 __ LeaveExitFrame(mode_);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009210
9211 // check if we should retry or throw exception
9212 Label retry;
9213 __ bind(&failure_returned);
9214 ASSERT(Failure::RETRY_AFTER_GC == 0);
9215 __ tst(r0, Operand(((1 << kFailureTypeTagSize) - 1) << kFailureTagSize));
9216 __ b(eq, &retry);
9217
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00009218 // Special handling of out of memory exceptions.
9219 Failure* out_of_memory = Failure::OutOfMemoryException();
9220 __ cmp(r0, Operand(reinterpret_cast<int32_t>(out_of_memory)));
9221 __ b(eq, throw_out_of_memory_exception);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009222
9223 // Retrieve the pending exception and clear the variable.
ager@chromium.org32912102009-01-16 10:38:43 +00009224 __ mov(ip, Operand(ExternalReference::the_hole_value_location()));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009225 __ ldr(r3, MemOperand(ip));
ager@chromium.org32912102009-01-16 10:38:43 +00009226 __ mov(ip, Operand(ExternalReference(Top::k_pending_exception_address)));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009227 __ ldr(r0, MemOperand(ip));
9228 __ str(r3, MemOperand(ip));
9229
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00009230 // Special handling of termination exceptions which are uncatchable
9231 // by javascript code.
9232 __ cmp(r0, Operand(Factory::termination_exception()));
9233 __ b(eq, throw_termination_exception);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009234
9235 // Handle normal exception.
9236 __ jmp(throw_normal_exception);
9237
9238 __ bind(&retry); // pass last failure (r0) as parameter (r0) when retrying
9239}
9240
9241
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009242void CEntryStub::Generate(MacroAssembler* masm) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009243 // Called from JavaScript; parameters are on stack as if calling JS function
9244 // r0: number of arguments including receiver
9245 // r1: pointer to builtin function
9246 // fp: frame pointer (restored after C call)
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009247 // sp: stack pointer (restored as callee's sp after C call)
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009248 // cp: current context (C callee-saved)
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009249
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009250 // Result returned in r0 or r0+r1 by default.
9251
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009252 // NOTE: Invocations of builtins may return failure objects
9253 // instead of a proper result. The builtin entry handles
9254 // this by performing a garbage collection and retrying the
9255 // builtin once.
9256
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009257 // Enter the exit frame that transitions from JavaScript to C++.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009258 __ EnterExitFrame(mode_);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009259
9260 // r4: number of arguments (C callee-saved)
9261 // r5: pointer to builtin function (C callee-saved)
9262 // r6: pointer to first argument (C callee-saved)
9263
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009264 Label throw_normal_exception;
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00009265 Label throw_termination_exception;
9266 Label throw_out_of_memory_exception;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009267
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00009268 // Call into the runtime system.
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00009269 GenerateCore(masm,
9270 &throw_normal_exception,
9271 &throw_termination_exception,
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009272 &throw_out_of_memory_exception,
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00009273 false,
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009274 false,
9275 -kPointerSize);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00009276
9277 // Do space-specific GC and retry runtime call.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009278 GenerateCore(masm,
9279 &throw_normal_exception,
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00009280 &throw_termination_exception,
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009281 &throw_out_of_memory_exception,
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00009282 true,
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009283 false,
9284 0);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00009285
9286 // Do full GC and retry runtime call one final time.
9287 Failure* failure = Failure::InternalError();
9288 __ mov(r0, Operand(reinterpret_cast<int32_t>(failure)));
9289 GenerateCore(masm,
9290 &throw_normal_exception,
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00009291 &throw_termination_exception,
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00009292 &throw_out_of_memory_exception,
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00009293 true,
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009294 true,
9295 kPointerSize);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009296
9297 __ bind(&throw_out_of_memory_exception);
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00009298 GenerateThrowUncatchable(masm, OUT_OF_MEMORY);
9299
9300 __ bind(&throw_termination_exception);
9301 GenerateThrowUncatchable(masm, TERMINATION);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009302
9303 __ bind(&throw_normal_exception);
9304 GenerateThrowTOS(masm);
9305}
9306
9307
9308void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) {
9309 // r0: code entry
9310 // r1: function
9311 // r2: receiver
9312 // r3: argc
9313 // [sp+0]: argv
9314
9315 Label invoke, exit;
9316
9317 // Called from C, so do not pop argc and args on exit (preserve sp)
9318 // No need to save register-passed args
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009319 // Save callee-saved registers (incl. cp and fp), sp, and lr
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009320 __ stm(db_w, sp, kCalleeSaved | lr.bit());
9321
9322 // Get address of argv, see stm above.
9323 // r0: code entry
9324 // r1: function
9325 // r2: receiver
9326 // r3: argc
ager@chromium.org5c838252010-02-19 08:53:10 +00009327 __ ldr(r4, MemOperand(sp, (kNumCalleeSaved + 1) * kPointerSize)); // argv
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009328
9329 // Push a frame with special values setup to mark it as an entry frame.
9330 // r0: code entry
9331 // r1: function
9332 // r2: receiver
9333 // r3: argc
9334 // r4: argv
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009335 __ mov(r8, Operand(-1)); // Push a bad frame pointer to fail if it is used.
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009336 int marker = is_construct ? StackFrame::ENTRY_CONSTRUCT : StackFrame::ENTRY;
9337 __ mov(r7, Operand(Smi::FromInt(marker)));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009338 __ mov(r6, Operand(Smi::FromInt(marker)));
9339 __ mov(r5, Operand(ExternalReference(Top::k_c_entry_fp_address)));
9340 __ ldr(r5, MemOperand(r5));
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00009341 __ Push(r8, r7, r6, r5);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009342
9343 // Setup frame pointer for the frame to be pushed.
9344 __ add(fp, sp, Operand(-EntryFrameConstants::kCallerFPOffset));
9345
9346 // Call a faked try-block that does the invoke.
9347 __ bl(&invoke);
9348
9349 // Caught exception: Store result (exception) in the pending
9350 // exception field in the JSEnv and return a failure sentinel.
9351 // Coming in here the fp will be invalid because the PushTryHandler below
9352 // sets it to 0 to signal the existence of the JSEntry frame.
ager@chromium.org32912102009-01-16 10:38:43 +00009353 __ mov(ip, Operand(ExternalReference(Top::k_pending_exception_address)));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009354 __ str(r0, MemOperand(ip));
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009355 __ mov(r0, Operand(reinterpret_cast<int32_t>(Failure::Exception())));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009356 __ b(&exit);
9357
9358 // Invoke: Link this frame into the handler chain.
9359 __ bind(&invoke);
9360 // Must preserve r0-r4, r5-r7 are available.
9361 __ PushTryHandler(IN_JS_ENTRY, JS_ENTRY_HANDLER);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009362 // If an exception not caught by another handler occurs, this handler
9363 // returns control to the code after the bl(&invoke) above, which
9364 // restores all kCalleeSaved registers (including cp and fp) to their
9365 // saved values before returning a failure to C.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009366
9367 // Clear any pending exceptions.
9368 __ mov(ip, Operand(ExternalReference::the_hole_value_location()));
9369 __ ldr(r5, MemOperand(ip));
ager@chromium.org32912102009-01-16 10:38:43 +00009370 __ mov(ip, Operand(ExternalReference(Top::k_pending_exception_address)));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009371 __ str(r5, MemOperand(ip));
9372
9373 // Invoke the function by calling through JS entry trampoline builtin.
9374 // Notice that we cannot store a reference to the trampoline code directly in
9375 // this stub, because runtime stubs are not traversed when doing GC.
9376
9377 // Expected registers by Builtins::JSEntryTrampoline
9378 // r0: code entry
9379 // r1: function
9380 // r2: receiver
9381 // r3: argc
9382 // r4: argv
9383 if (is_construct) {
9384 ExternalReference construct_entry(Builtins::JSConstructEntryTrampoline);
9385 __ mov(ip, Operand(construct_entry));
9386 } else {
9387 ExternalReference entry(Builtins::JSEntryTrampoline);
9388 __ mov(ip, Operand(entry));
9389 }
9390 __ ldr(ip, MemOperand(ip)); // deref address
9391
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009392 // Branch and link to JSEntryTrampoline. We don't use the double underscore
9393 // macro for the add instruction because we don't want the coverage tool
9394 // inserting instructions here after we read the pc.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009395 __ mov(lr, Operand(pc));
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009396 masm->add(pc, ip, Operand(Code::kHeaderSize - kHeapObjectTag));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009397
9398 // Unlink this frame from the handler chain. When reading the
9399 // address of the next handler, there is no need to use the address
9400 // displacement since the current stack pointer (sp) points directly
9401 // to the stack handler.
9402 __ ldr(r3, MemOperand(sp, StackHandlerConstants::kNextOffset));
9403 __ mov(ip, Operand(ExternalReference(Top::k_handler_address)));
9404 __ str(r3, MemOperand(ip));
9405 // No need to restore registers
9406 __ add(sp, sp, Operand(StackHandlerConstants::kSize));
9407
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009408
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009409 __ bind(&exit); // r0 holds result
9410 // Restore the top frame descriptors from the stack.
9411 __ pop(r3);
9412 __ mov(ip, Operand(ExternalReference(Top::k_c_entry_fp_address)));
9413 __ str(r3, MemOperand(ip));
9414
9415 // Reset the stack to the callee saved registers.
9416 __ add(sp, sp, Operand(-EntryFrameConstants::kCallerFPOffset));
9417
9418 // Restore callee-saved registers and return.
9419#ifdef DEBUG
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009420 if (FLAG_debug_code) {
9421 __ mov(lr, Operand(pc));
9422 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009423#endif
9424 __ ldm(ia_w, sp, kCalleeSaved | pc.bit());
9425}
9426
9427
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009428// This stub performs an instanceof, calling the builtin function if
9429// necessary. Uses r1 for the object, r0 for the function that it may
9430// be an instance of (these are fetched from the stack).
9431void InstanceofStub::Generate(MacroAssembler* masm) {
9432 // Get the object - slow case for smis (we may need to throw an exception
9433 // depending on the rhs).
9434 Label slow, loop, is_instance, is_not_instance;
9435 __ ldr(r0, MemOperand(sp, 1 * kPointerSize));
9436 __ BranchOnSmi(r0, &slow);
9437
9438 // Check that the left hand is a JS object and put map in r3.
9439 __ CompareObjectType(r0, r3, r2, FIRST_JS_OBJECT_TYPE);
9440 __ b(lt, &slow);
9441 __ cmp(r2, Operand(LAST_JS_OBJECT_TYPE));
9442 __ b(gt, &slow);
9443
9444 // Get the prototype of the function (r4 is result, r2 is scratch).
ager@chromium.org5c838252010-02-19 08:53:10 +00009445 __ ldr(r1, MemOperand(sp, 0));
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00009446 // r1 is function, r3 is map.
9447
9448 // Look up the function and the map in the instanceof cache.
9449 Label miss;
9450 __ LoadRoot(ip, Heap::kInstanceofCacheFunctionRootIndex);
9451 __ cmp(r1, ip);
9452 __ b(ne, &miss);
9453 __ LoadRoot(ip, Heap::kInstanceofCacheMapRootIndex);
9454 __ cmp(r3, ip);
9455 __ b(ne, &miss);
9456 __ LoadRoot(r0, Heap::kInstanceofCacheAnswerRootIndex);
9457 __ pop();
9458 __ pop();
9459 __ mov(pc, Operand(lr));
9460
9461 __ bind(&miss);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009462 __ TryGetFunctionPrototype(r1, r4, r2, &slow);
9463
9464 // Check that the function prototype is a JS object.
9465 __ BranchOnSmi(r4, &slow);
9466 __ CompareObjectType(r4, r5, r5, FIRST_JS_OBJECT_TYPE);
9467 __ b(lt, &slow);
9468 __ cmp(r5, Operand(LAST_JS_OBJECT_TYPE));
9469 __ b(gt, &slow);
9470
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00009471 __ StoreRoot(r1, Heap::kInstanceofCacheFunctionRootIndex);
9472 __ StoreRoot(r3, Heap::kInstanceofCacheMapRootIndex);
9473
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009474 // Register mapping: r3 is object map and r4 is function prototype.
9475 // Get prototype of object into r2.
9476 __ ldr(r2, FieldMemOperand(r3, Map::kPrototypeOffset));
9477
9478 // Loop through the prototype chain looking for the function prototype.
9479 __ bind(&loop);
9480 __ cmp(r2, Operand(r4));
9481 __ b(eq, &is_instance);
ager@chromium.orgab99eea2009-08-25 07:05:41 +00009482 __ LoadRoot(ip, Heap::kNullValueRootIndex);
9483 __ cmp(r2, ip);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009484 __ b(eq, &is_not_instance);
9485 __ ldr(r2, FieldMemOperand(r2, HeapObject::kMapOffset));
9486 __ ldr(r2, FieldMemOperand(r2, Map::kPrototypeOffset));
9487 __ jmp(&loop);
9488
9489 __ bind(&is_instance);
9490 __ mov(r0, Operand(Smi::FromInt(0)));
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00009491 __ StoreRoot(r0, Heap::kInstanceofCacheAnswerRootIndex);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009492 __ pop();
9493 __ pop();
9494 __ mov(pc, Operand(lr)); // Return.
9495
9496 __ bind(&is_not_instance);
9497 __ mov(r0, Operand(Smi::FromInt(1)));
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00009498 __ StoreRoot(r0, Heap::kInstanceofCacheAnswerRootIndex);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009499 __ pop();
9500 __ pop();
9501 __ mov(pc, Operand(lr)); // Return.
9502
9503 // Slow-case. Tail call builtin.
9504 __ bind(&slow);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009505 __ InvokeBuiltin(Builtins::INSTANCE_OF, JUMP_JS);
9506}
9507
9508
ager@chromium.org7c537e22008-10-16 08:43:32 +00009509void ArgumentsAccessStub::GenerateReadElement(MacroAssembler* masm) {
9510 // The displacement is the offset of the last parameter (if any)
9511 // relative to the frame pointer.
9512 static const int kDisplacement =
9513 StandardFrameConstants::kCallerSPOffset - kPointerSize;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009514
ager@chromium.org7c537e22008-10-16 08:43:32 +00009515 // Check that the key is a smi.
9516 Label slow;
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009517 __ BranchOnNotSmi(r1, &slow);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009518
ager@chromium.org7c537e22008-10-16 08:43:32 +00009519 // Check if the calling frame is an arguments adaptor frame.
9520 Label adaptor;
9521 __ ldr(r2, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
9522 __ ldr(r3, MemOperand(r2, StandardFrameConstants::kContextOffset));
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009523 __ cmp(r3, Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
ager@chromium.org7c537e22008-10-16 08:43:32 +00009524 __ b(eq, &adaptor);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009525
ager@chromium.org7c537e22008-10-16 08:43:32 +00009526 // Check index against formal parameters count limit passed in
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009527 // through register r0. Use unsigned comparison to get negative
ager@chromium.org7c537e22008-10-16 08:43:32 +00009528 // check for free.
9529 __ cmp(r1, r0);
9530 __ b(cs, &slow);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009531
ager@chromium.org7c537e22008-10-16 08:43:32 +00009532 // Read the argument from the stack and return it.
9533 __ sub(r3, r0, r1);
9534 __ add(r3, fp, Operand(r3, LSL, kPointerSizeLog2 - kSmiTagSize));
9535 __ ldr(r0, MemOperand(r3, kDisplacement));
ager@chromium.org9085a012009-05-11 19:22:57 +00009536 __ Jump(lr);
ager@chromium.org7c537e22008-10-16 08:43:32 +00009537
9538 // Arguments adaptor case: Check index against actual arguments
9539 // limit found in the arguments adaptor frame. Use unsigned
9540 // comparison to get negative check for free.
9541 __ bind(&adaptor);
9542 __ ldr(r0, MemOperand(r2, ArgumentsAdaptorFrameConstants::kLengthOffset));
9543 __ cmp(r1, r0);
9544 __ b(cs, &slow);
9545
9546 // Read the argument from the adaptor frame and return it.
9547 __ sub(r3, r0, r1);
9548 __ add(r3, r2, Operand(r3, LSL, kPointerSizeLog2 - kSmiTagSize));
9549 __ ldr(r0, MemOperand(r3, kDisplacement));
ager@chromium.org9085a012009-05-11 19:22:57 +00009550 __ Jump(lr);
ager@chromium.org7c537e22008-10-16 08:43:32 +00009551
9552 // Slow-case: Handle non-smi or out-of-bounds access to arguments
9553 // by calling the runtime system.
9554 __ bind(&slow);
9555 __ push(r1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009556 __ TailCallRuntime(Runtime::kGetArgumentsProperty, 1, 1);
ager@chromium.org7c537e22008-10-16 08:43:32 +00009557}
9558
9559
9560void ArgumentsAccessStub::GenerateNewObject(MacroAssembler* masm) {
ager@chromium.org5c838252010-02-19 08:53:10 +00009561 // sp[0] : number of parameters
9562 // sp[4] : receiver displacement
9563 // sp[8] : function
9564
ager@chromium.org7c537e22008-10-16 08:43:32 +00009565 // Check if the calling frame is an arguments adaptor frame.
ager@chromium.org5c838252010-02-19 08:53:10 +00009566 Label adaptor_frame, try_allocate, runtime;
ager@chromium.org7c537e22008-10-16 08:43:32 +00009567 __ ldr(r2, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
9568 __ ldr(r3, MemOperand(r2, StandardFrameConstants::kContextOffset));
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009569 __ cmp(r3, Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
ager@chromium.org5c838252010-02-19 08:53:10 +00009570 __ b(eq, &adaptor_frame);
9571
9572 // Get the length from the frame.
9573 __ ldr(r1, MemOperand(sp, 0));
9574 __ b(&try_allocate);
ager@chromium.org7c537e22008-10-16 08:43:32 +00009575
9576 // Patch the arguments.length and the parameters pointer.
ager@chromium.org5c838252010-02-19 08:53:10 +00009577 __ bind(&adaptor_frame);
9578 __ ldr(r1, MemOperand(r2, ArgumentsAdaptorFrameConstants::kLengthOffset));
9579 __ str(r1, MemOperand(sp, 0));
9580 __ add(r3, r2, Operand(r1, LSL, kPointerSizeLog2 - kSmiTagSize));
ager@chromium.org7c537e22008-10-16 08:43:32 +00009581 __ add(r3, r3, Operand(StandardFrameConstants::kCallerSPOffset));
9582 __ str(r3, MemOperand(sp, 1 * kPointerSize));
9583
ager@chromium.org5c838252010-02-19 08:53:10 +00009584 // Try the new space allocation. Start out with computing the size
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00009585 // of the arguments object and the elements array in words.
ager@chromium.org5c838252010-02-19 08:53:10 +00009586 Label add_arguments_object;
9587 __ bind(&try_allocate);
9588 __ cmp(r1, Operand(0));
9589 __ b(eq, &add_arguments_object);
9590 __ mov(r1, Operand(r1, LSR, kSmiTagSize));
9591 __ add(r1, r1, Operand(FixedArray::kHeaderSize / kPointerSize));
9592 __ bind(&add_arguments_object);
9593 __ add(r1, r1, Operand(Heap::kArgumentsObjectSize / kPointerSize));
9594
9595 // Do the allocation of both objects in one go.
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00009596 __ AllocateInNewSpace(
9597 r1,
9598 r0,
9599 r2,
9600 r3,
9601 &runtime,
9602 static_cast<AllocationFlags>(TAG_OBJECT | SIZE_IN_WORDS));
ager@chromium.org5c838252010-02-19 08:53:10 +00009603
9604 // Get the arguments boilerplate from the current (global) context.
9605 int offset = Context::SlotOffset(Context::ARGUMENTS_BOILERPLATE_INDEX);
9606 __ ldr(r4, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_INDEX)));
9607 __ ldr(r4, FieldMemOperand(r4, GlobalObject::kGlobalContextOffset));
9608 __ ldr(r4, MemOperand(r4, offset));
9609
9610 // Copy the JS object part.
9611 for (int i = 0; i < JSObject::kHeaderSize; i += kPointerSize) {
9612 __ ldr(r3, FieldMemOperand(r4, i));
9613 __ str(r3, FieldMemOperand(r0, i));
9614 }
9615
9616 // Setup the callee in-object property.
9617 ASSERT(Heap::arguments_callee_index == 0);
9618 __ ldr(r3, MemOperand(sp, 2 * kPointerSize));
9619 __ str(r3, FieldMemOperand(r0, JSObject::kHeaderSize));
9620
9621 // Get the length (smi tagged) and set that as an in-object property too.
9622 ASSERT(Heap::arguments_length_index == 1);
9623 __ ldr(r1, MemOperand(sp, 0 * kPointerSize));
9624 __ str(r1, FieldMemOperand(r0, JSObject::kHeaderSize + kPointerSize));
9625
9626 // If there are no actual arguments, we're done.
9627 Label done;
9628 __ cmp(r1, Operand(0));
9629 __ b(eq, &done);
9630
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009631 // Get the parameters pointer from the stack.
ager@chromium.org5c838252010-02-19 08:53:10 +00009632 __ ldr(r2, MemOperand(sp, 1 * kPointerSize));
ager@chromium.org5c838252010-02-19 08:53:10 +00009633
9634 // Setup the elements pointer in the allocated arguments object and
9635 // initialize the header in the elements fixed array.
9636 __ add(r4, r0, Operand(Heap::kArgumentsObjectSize));
9637 __ str(r4, FieldMemOperand(r0, JSObject::kElementsOffset));
9638 __ LoadRoot(r3, Heap::kFixedArrayMapRootIndex);
9639 __ str(r3, FieldMemOperand(r4, FixedArray::kMapOffset));
9640 __ str(r1, FieldMemOperand(r4, FixedArray::kLengthOffset));
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009641 __ mov(r1, Operand(r1, LSR, kSmiTagSize)); // Untag the length for the loop.
ager@chromium.org5c838252010-02-19 08:53:10 +00009642
9643 // Copy the fixed array slots.
9644 Label loop;
9645 // Setup r4 to point to the first array slot.
9646 __ add(r4, r4, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
9647 __ bind(&loop);
9648 // Pre-decrement r2 with kPointerSize on each iteration.
9649 // Pre-decrement in order to skip receiver.
9650 __ ldr(r3, MemOperand(r2, kPointerSize, NegPreIndex));
9651 // Post-increment r4 with kPointerSize on each iteration.
9652 __ str(r3, MemOperand(r4, kPointerSize, PostIndex));
9653 __ sub(r1, r1, Operand(1));
9654 __ cmp(r1, Operand(0));
9655 __ b(ne, &loop);
9656
9657 // Return and remove the on-stack parameters.
9658 __ bind(&done);
9659 __ add(sp, sp, Operand(3 * kPointerSize));
9660 __ Ret();
9661
ager@chromium.org7c537e22008-10-16 08:43:32 +00009662 // Do the runtime call to allocate the arguments object.
9663 __ bind(&runtime);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009664 __ TailCallRuntime(Runtime::kNewArgumentsFast, 3, 1);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009665}
9666
9667
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009668void RegExpExecStub::Generate(MacroAssembler* masm) {
9669 // Just jump directly to runtime if native RegExp is not selected at compile
9670 // time or if regexp entry in generated code is turned off runtime switch or
9671 // at compilation.
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00009672#ifdef V8_INTERPRETED_REGEXP
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009673 __ TailCallRuntime(Runtime::kRegExpExec, 4, 1);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00009674#else // V8_INTERPRETED_REGEXP
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009675 if (!FLAG_regexp_entry_native) {
9676 __ TailCallRuntime(Runtime::kRegExpExec, 4, 1);
9677 return;
9678 }
9679
9680 // Stack frame on entry.
9681 // sp[0]: last_match_info (expected JSArray)
9682 // sp[4]: previous index
9683 // sp[8]: subject string
9684 // sp[12]: JSRegExp object
9685
9686 static const int kLastMatchInfoOffset = 0 * kPointerSize;
9687 static const int kPreviousIndexOffset = 1 * kPointerSize;
9688 static const int kSubjectOffset = 2 * kPointerSize;
9689 static const int kJSRegExpOffset = 3 * kPointerSize;
9690
9691 Label runtime, invoke_regexp;
9692
9693 // Allocation of registers for this function. These are in callee save
9694 // registers and will be preserved by the call to the native RegExp code, as
9695 // this code is called using the normal C calling convention. When calling
9696 // directly from generated code the native RegExp code will not do a GC and
9697 // therefore the content of these registers are safe to use after the call.
9698 Register subject = r4;
9699 Register regexp_data = r5;
9700 Register last_match_info_elements = r6;
9701
9702 // Ensure that a RegExp stack is allocated.
9703 ExternalReference address_of_regexp_stack_memory_address =
9704 ExternalReference::address_of_regexp_stack_memory_address();
9705 ExternalReference address_of_regexp_stack_memory_size =
9706 ExternalReference::address_of_regexp_stack_memory_size();
9707 __ mov(r0, Operand(address_of_regexp_stack_memory_size));
9708 __ ldr(r0, MemOperand(r0, 0));
9709 __ tst(r0, Operand(r0));
9710 __ b(eq, &runtime);
9711
9712 // Check that the first argument is a JSRegExp object.
9713 __ ldr(r0, MemOperand(sp, kJSRegExpOffset));
9714 ASSERT_EQ(0, kSmiTag);
9715 __ tst(r0, Operand(kSmiTagMask));
9716 __ b(eq, &runtime);
9717 __ CompareObjectType(r0, r1, r1, JS_REGEXP_TYPE);
9718 __ b(ne, &runtime);
9719
9720 // Check that the RegExp has been compiled (data contains a fixed array).
9721 __ ldr(regexp_data, FieldMemOperand(r0, JSRegExp::kDataOffset));
9722 if (FLAG_debug_code) {
9723 __ tst(regexp_data, Operand(kSmiTagMask));
9724 __ Check(nz, "Unexpected type for RegExp data, FixedArray expected");
9725 __ CompareObjectType(regexp_data, r0, r0, FIXED_ARRAY_TYPE);
9726 __ Check(eq, "Unexpected type for RegExp data, FixedArray expected");
9727 }
9728
9729 // regexp_data: RegExp data (FixedArray)
9730 // Check the type of the RegExp. Only continue if type is JSRegExp::IRREGEXP.
9731 __ ldr(r0, FieldMemOperand(regexp_data, JSRegExp::kDataTagOffset));
9732 __ cmp(r0, Operand(Smi::FromInt(JSRegExp::IRREGEXP)));
9733 __ b(ne, &runtime);
9734
9735 // regexp_data: RegExp data (FixedArray)
9736 // Check that the number of captures fit in the static offsets vector buffer.
9737 __ ldr(r2,
9738 FieldMemOperand(regexp_data, JSRegExp::kIrregexpCaptureCountOffset));
9739 // Calculate number of capture registers (number_of_captures + 1) * 2. This
9740 // uses the asumption that smis are 2 * their untagged value.
9741 ASSERT_EQ(0, kSmiTag);
9742 ASSERT_EQ(1, kSmiTagSize + kSmiShiftSize);
9743 __ add(r2, r2, Operand(2)); // r2 was a smi.
9744 // Check that the static offsets vector buffer is large enough.
9745 __ cmp(r2, Operand(OffsetsVector::kStaticOffsetsVectorSize));
9746 __ b(hi, &runtime);
9747
9748 // r2: Number of capture registers
9749 // regexp_data: RegExp data (FixedArray)
9750 // Check that the second argument is a string.
9751 __ ldr(subject, MemOperand(sp, kSubjectOffset));
9752 __ tst(subject, Operand(kSmiTagMask));
9753 __ b(eq, &runtime);
9754 Condition is_string = masm->IsObjectStringType(subject, r0);
9755 __ b(NegateCondition(is_string), &runtime);
9756 // Get the length of the string to r3.
9757 __ ldr(r3, FieldMemOperand(subject, String::kLengthOffset));
9758
9759 // r2: Number of capture registers
ager@chromium.orgac091b72010-05-05 07:34:42 +00009760 // r3: Length of subject string as a smi
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009761 // subject: Subject string
9762 // regexp_data: RegExp data (FixedArray)
9763 // Check that the third argument is a positive smi less than the subject
9764 // string length. A negative value will be greater (unsigned comparison).
9765 __ ldr(r0, MemOperand(sp, kPreviousIndexOffset));
ager@chromium.orgac091b72010-05-05 07:34:42 +00009766 __ tst(r0, Operand(kSmiTagMask));
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00009767 __ b(ne, &runtime);
ager@chromium.orgac091b72010-05-05 07:34:42 +00009768 __ cmp(r3, Operand(r0));
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00009769 __ b(ls, &runtime);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009770
9771 // r2: Number of capture registers
9772 // subject: Subject string
9773 // regexp_data: RegExp data (FixedArray)
9774 // Check that the fourth object is a JSArray object.
9775 __ ldr(r0, MemOperand(sp, kLastMatchInfoOffset));
9776 __ tst(r0, Operand(kSmiTagMask));
9777 __ b(eq, &runtime);
9778 __ CompareObjectType(r0, r1, r1, JS_ARRAY_TYPE);
9779 __ b(ne, &runtime);
9780 // Check that the JSArray is in fast case.
9781 __ ldr(last_match_info_elements,
9782 FieldMemOperand(r0, JSArray::kElementsOffset));
9783 __ ldr(r0, FieldMemOperand(last_match_info_elements, HeapObject::kMapOffset));
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00009784 __ LoadRoot(ip, Heap::kFixedArrayMapRootIndex);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00009785 __ cmp(r0, ip);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009786 __ b(ne, &runtime);
9787 // Check that the last match info has space for the capture registers and the
9788 // additional information.
9789 __ ldr(r0,
9790 FieldMemOperand(last_match_info_elements, FixedArray::kLengthOffset));
9791 __ add(r2, r2, Operand(RegExpImpl::kLastMatchOverhead));
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009792 __ cmp(r2, Operand(r0, ASR, kSmiTagSize));
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009793 __ b(gt, &runtime);
9794
9795 // subject: Subject string
9796 // regexp_data: RegExp data (FixedArray)
9797 // Check the representation and encoding of the subject string.
9798 Label seq_string;
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009799 __ ldr(r0, FieldMemOperand(subject, HeapObject::kMapOffset));
9800 __ ldrb(r0, FieldMemOperand(r0, Map::kInstanceTypeOffset));
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00009801 // First check for flat string.
9802 __ tst(r0, Operand(kIsNotStringMask | kStringRepresentationMask));
9803 ASSERT_EQ(0, kStringTag | kSeqStringTag);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009804 __ b(eq, &seq_string);
9805
9806 // subject: Subject string
9807 // regexp_data: RegExp data (FixedArray)
9808 // Check for flat cons string.
9809 // A flat cons string is a cons string where the second part is the empty
9810 // string. In that case the subject string is just the first part of the cons
9811 // string. Also in this case the first part of the cons string is known to be
9812 // a sequential string or an external string.
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00009813 ASSERT(kExternalStringTag !=0);
9814 ASSERT_EQ(0, kConsStringTag & kExternalStringTag);
9815 __ tst(r0, Operand(kIsNotStringMask | kExternalStringTag));
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009816 __ b(ne, &runtime);
9817 __ ldr(r0, FieldMemOperand(subject, ConsString::kSecondOffset));
9818 __ LoadRoot(r1, Heap::kEmptyStringRootIndex);
9819 __ cmp(r0, r1);
9820 __ b(ne, &runtime);
9821 __ ldr(subject, FieldMemOperand(subject, ConsString::kFirstOffset));
9822 __ ldr(r0, FieldMemOperand(subject, HeapObject::kMapOffset));
9823 __ ldrb(r0, FieldMemOperand(r0, Map::kInstanceTypeOffset));
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00009824 // Is first part a flat string?
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009825 ASSERT_EQ(0, kSeqStringTag);
9826 __ tst(r0, Operand(kStringRepresentationMask));
9827 __ b(nz, &runtime);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009828
9829 __ bind(&seq_string);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009830 // subject: Subject string
9831 // regexp_data: RegExp data (FixedArray)
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00009832 // r0: Instance type of subject string
9833 ASSERT_EQ(4, kAsciiStringTag);
9834 ASSERT_EQ(0, kTwoByteStringTag);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009835 // Find the code object based on the assumptions above.
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00009836 __ and_(r0, r0, Operand(kStringEncodingMask));
9837 __ mov(r3, Operand(r0, ASR, 2), SetCC);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009838 __ ldr(r7, FieldMemOperand(regexp_data, JSRegExp::kDataAsciiCodeOffset), ne);
9839 __ ldr(r7, FieldMemOperand(regexp_data, JSRegExp::kDataUC16CodeOffset), eq);
9840
9841 // Check that the irregexp code has been generated for the actual string
9842 // encoding. If it has, the field contains a code object otherwise it contains
9843 // the hole.
9844 __ CompareObjectType(r7, r0, r0, CODE_TYPE);
9845 __ b(ne, &runtime);
9846
9847 // r3: encoding of subject string (1 if ascii, 0 if two_byte);
9848 // r7: code
9849 // subject: Subject string
9850 // regexp_data: RegExp data (FixedArray)
9851 // Load used arguments before starting to push arguments for call to native
9852 // RegExp code to avoid handling changing stack height.
9853 __ ldr(r1, MemOperand(sp, kPreviousIndexOffset));
9854 __ mov(r1, Operand(r1, ASR, kSmiTagSize));
9855
9856 // r1: previous index
9857 // r3: encoding of subject string (1 if ascii, 0 if two_byte);
9858 // r7: code
9859 // subject: Subject string
9860 // regexp_data: RegExp data (FixedArray)
9861 // All checks done. Now push arguments for native regexp code.
9862 __ IncrementCounter(&Counters::regexp_entry_native, 1, r0, r2);
9863
9864 static const int kRegExpExecuteArguments = 7;
9865 __ push(lr);
9866 __ PrepareCallCFunction(kRegExpExecuteArguments, r0);
9867
9868 // Argument 7 (sp[8]): Indicate that this is a direct call from JavaScript.
9869 __ mov(r0, Operand(1));
9870 __ str(r0, MemOperand(sp, 2 * kPointerSize));
9871
9872 // Argument 6 (sp[4]): Start (high end) of backtracking stack memory area.
9873 __ mov(r0, Operand(address_of_regexp_stack_memory_address));
9874 __ ldr(r0, MemOperand(r0, 0));
9875 __ mov(r2, Operand(address_of_regexp_stack_memory_size));
9876 __ ldr(r2, MemOperand(r2, 0));
9877 __ add(r0, r0, Operand(r2));
9878 __ str(r0, MemOperand(sp, 1 * kPointerSize));
9879
9880 // Argument 5 (sp[0]): static offsets vector buffer.
9881 __ mov(r0, Operand(ExternalReference::address_of_static_offsets_vector()));
9882 __ str(r0, MemOperand(sp, 0 * kPointerSize));
9883
9884 // For arguments 4 and 3 get string length, calculate start of string data and
9885 // calculate the shift of the index (0 for ASCII and 1 for two byte).
9886 __ ldr(r0, FieldMemOperand(subject, String::kLengthOffset));
ager@chromium.orgac091b72010-05-05 07:34:42 +00009887 __ mov(r0, Operand(r0, ASR, kSmiTagSize));
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009888 ASSERT_EQ(SeqAsciiString::kHeaderSize, SeqTwoByteString::kHeaderSize);
9889 __ add(r9, subject, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
9890 __ eor(r3, r3, Operand(1));
9891 // Argument 4 (r3): End of string data
9892 // Argument 3 (r2): Start of string data
9893 __ add(r2, r9, Operand(r1, LSL, r3));
9894 __ add(r3, r9, Operand(r0, LSL, r3));
9895
9896 // Argument 2 (r1): Previous index.
9897 // Already there
9898
9899 // Argument 1 (r0): Subject string.
9900 __ mov(r0, subject);
9901
9902 // Locate the code entry and call it.
9903 __ add(r7, r7, Operand(Code::kHeaderSize - kHeapObjectTag));
9904 __ CallCFunction(r7, kRegExpExecuteArguments);
9905 __ pop(lr);
9906
9907 // r0: result
9908 // subject: subject string (callee saved)
9909 // regexp_data: RegExp data (callee saved)
9910 // last_match_info_elements: Last match info elements (callee saved)
9911
9912 // Check the result.
9913 Label success;
9914 __ cmp(r0, Operand(NativeRegExpMacroAssembler::SUCCESS));
9915 __ b(eq, &success);
9916 Label failure;
9917 __ cmp(r0, Operand(NativeRegExpMacroAssembler::FAILURE));
9918 __ b(eq, &failure);
9919 __ cmp(r0, Operand(NativeRegExpMacroAssembler::EXCEPTION));
9920 // If not exception it can only be retry. Handle that in the runtime system.
9921 __ b(ne, &runtime);
9922 // Result must now be exception. If there is no pending exception already a
9923 // stack overflow (on the backtrack stack) was detected in RegExp code but
9924 // haven't created the exception yet. Handle that in the runtime system.
9925 // TODO(592): Rerunning the RegExp to get the stack overflow exception.
9926 __ mov(r0, Operand(ExternalReference::the_hole_value_location()));
9927 __ ldr(r0, MemOperand(r0, 0));
9928 __ mov(r1, Operand(ExternalReference(Top::k_pending_exception_address)));
9929 __ ldr(r1, MemOperand(r1, 0));
9930 __ cmp(r0, r1);
9931 __ b(eq, &runtime);
9932 __ bind(&failure);
9933 // For failure and exception return null.
9934 __ mov(r0, Operand(Factory::null_value()));
9935 __ add(sp, sp, Operand(4 * kPointerSize));
9936 __ Ret();
9937
9938 // Process the result from the native regexp code.
9939 __ bind(&success);
9940 __ ldr(r1,
9941 FieldMemOperand(regexp_data, JSRegExp::kIrregexpCaptureCountOffset));
9942 // Calculate number of capture registers (number_of_captures + 1) * 2.
9943 ASSERT_EQ(0, kSmiTag);
9944 ASSERT_EQ(1, kSmiTagSize + kSmiShiftSize);
9945 __ add(r1, r1, Operand(2)); // r1 was a smi.
9946
9947 // r1: number of capture registers
9948 // r4: subject string
9949 // Store the capture count.
9950 __ mov(r2, Operand(r1, LSL, kSmiTagSize + kSmiShiftSize)); // To smi.
9951 __ str(r2, FieldMemOperand(last_match_info_elements,
9952 RegExpImpl::kLastCaptureCountOffset));
9953 // Store last subject and last input.
9954 __ mov(r3, last_match_info_elements); // Moved up to reduce latency.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009955 __ str(subject,
9956 FieldMemOperand(last_match_info_elements,
9957 RegExpImpl::kLastSubjectOffset));
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00009958 __ RecordWrite(r3, Operand(RegExpImpl::kLastSubjectOffset), r2, r7);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009959 __ str(subject,
9960 FieldMemOperand(last_match_info_elements,
9961 RegExpImpl::kLastInputOffset));
9962 __ mov(r3, last_match_info_elements);
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00009963 __ RecordWrite(r3, Operand(RegExpImpl::kLastInputOffset), r2, r7);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009964
9965 // Get the static offsets vector filled by the native regexp code.
9966 ExternalReference address_of_static_offsets_vector =
9967 ExternalReference::address_of_static_offsets_vector();
9968 __ mov(r2, Operand(address_of_static_offsets_vector));
9969
9970 // r1: number of capture registers
9971 // r2: offsets vector
9972 Label next_capture, done;
9973 // Capture register counter starts from number of capture registers and
9974 // counts down until wraping after zero.
9975 __ add(r0,
9976 last_match_info_elements,
9977 Operand(RegExpImpl::kFirstCaptureOffset - kHeapObjectTag));
9978 __ bind(&next_capture);
9979 __ sub(r1, r1, Operand(1), SetCC);
9980 __ b(mi, &done);
9981 // Read the value from the static offsets vector buffer.
9982 __ ldr(r3, MemOperand(r2, kPointerSize, PostIndex));
9983 // Store the smi value in the last match info.
9984 __ mov(r3, Operand(r3, LSL, kSmiTagSize));
9985 __ str(r3, MemOperand(r0, kPointerSize, PostIndex));
9986 __ jmp(&next_capture);
9987 __ bind(&done);
9988
9989 // Return last match info.
9990 __ ldr(r0, MemOperand(sp, kLastMatchInfoOffset));
9991 __ add(sp, sp, Operand(4 * kPointerSize));
9992 __ Ret();
9993
9994 // Do the runtime call to execute the regexp.
9995 __ bind(&runtime);
9996 __ TailCallRuntime(Runtime::kRegExpExec, 4, 1);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00009997#endif // V8_INTERPRETED_REGEXP
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009998}
9999
10000
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000010001void CallFunctionStub::Generate(MacroAssembler* masm) {
10002 Label slow;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010003
10004 // If the receiver might be a value (string, number or boolean) check for this
10005 // and box it if it is.
10006 if (ReceiverMightBeValue()) {
10007 // Get the receiver from the stack.
10008 // function, receiver [, arguments]
10009 Label receiver_is_value, receiver_is_js_object;
10010 __ ldr(r1, MemOperand(sp, argc_ * kPointerSize));
10011
10012 // Check if receiver is a smi (which is a number value).
10013 __ BranchOnSmi(r1, &receiver_is_value);
10014
10015 // Check if the receiver is a valid JS object.
10016 __ CompareObjectType(r1, r2, r2, FIRST_JS_OBJECT_TYPE);
10017 __ b(ge, &receiver_is_js_object);
10018
10019 // Call the runtime to box the value.
10020 __ bind(&receiver_is_value);
10021 __ EnterInternalFrame();
10022 __ push(r1);
10023 __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_JS);
10024 __ LeaveInternalFrame();
10025 __ str(r0, MemOperand(sp, argc_ * kPointerSize));
10026
10027 __ bind(&receiver_is_js_object);
10028 }
10029
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000010030 // Get the function to call from the stack.
10031 // function, receiver [, arguments]
10032 __ ldr(r1, MemOperand(sp, (argc_ + 1) * kPointerSize));
10033
10034 // Check that the function is really a JavaScript function.
10035 // r1: pushed function (to be verified)
ager@chromium.orgeadaf222009-06-16 09:43:10 +000010036 __ BranchOnSmi(r1, &slow);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000010037 // Get the map of the function object.
ager@chromium.orgeadaf222009-06-16 09:43:10 +000010038 __ CompareObjectType(r1, r2, r2, JS_FUNCTION_TYPE);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000010039 __ b(ne, &slow);
10040
10041 // Fast-case: Invoke the function now.
10042 // r1: pushed function
10043 ParameterCount actual(argc_);
10044 __ InvokeFunction(r1, actual, JUMP_FUNCTION);
10045
10046 // Slow-case: Non-function called.
10047 __ bind(&slow);
ager@chromium.org5c838252010-02-19 08:53:10 +000010048 // CALL_NON_FUNCTION expects the non-function callee as receiver (instead
10049 // of the original receiver from the call site).
10050 __ str(r1, MemOperand(sp, argc_ * kPointerSize));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000010051 __ mov(r0, Operand(argc_)); // Setup the number of arguments.
ager@chromium.org3bf7b912008-11-17 09:09:45 +000010052 __ mov(r2, Operand(0));
10053 __ GetBuiltinEntry(r3, Builtins::CALL_NON_FUNCTION);
10054 __ Jump(Handle<Code>(Builtins::builtin(Builtins::ArgumentsAdaptorTrampoline)),
10055 RelocInfo::CODE_TARGET);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000010056}
10057
10058
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +000010059// Unfortunately you have to run without snapshots to see most of these
10060// names in the profile since most compare stubs end up in the snapshot.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000010061const char* CompareStub::GetName() {
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +000010062 if (name_ != NULL) return name_;
10063 const int kMaxNameLength = 100;
10064 name_ = Bootstrapper::AllocateAutoDeletedArray(kMaxNameLength);
10065 if (name_ == NULL) return "OOM";
10066
10067 const char* cc_name;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000010068 switch (cc_) {
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +000010069 case lt: cc_name = "LT"; break;
10070 case gt: cc_name = "GT"; break;
10071 case le: cc_name = "LE"; break;
10072 case ge: cc_name = "GE"; break;
10073 case eq: cc_name = "EQ"; break;
10074 case ne: cc_name = "NE"; break;
10075 default: cc_name = "UnknownCondition"; break;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000010076 }
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +000010077
10078 const char* strict_name = "";
10079 if (strict_ && (cc_ == eq || cc_ == ne)) {
10080 strict_name = "_STRICT";
10081 }
10082
10083 const char* never_nan_nan_name = "";
10084 if (never_nan_nan_ && (cc_ == eq || cc_ == ne)) {
10085 never_nan_nan_name = "_NO_NAN";
10086 }
10087
10088 const char* include_number_compare_name = "";
10089 if (!include_number_compare_) {
10090 include_number_compare_name = "_NO_NUMBER";
10091 }
10092
10093 OS::SNPrintF(Vector<char>(name_, kMaxNameLength),
10094 "CompareStub_%s%s%s%s",
10095 cc_name,
10096 strict_name,
10097 never_nan_nan_name,
10098 include_number_compare_name);
10099 return name_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000010100}
10101
10102
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010103int CompareStub::MinorKey() {
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +000010104 // Encode the three parameters in a unique 16 bit value. To avoid duplicate
10105 // stubs the never NaN NaN condition is only taken into account if the
10106 // condition is equals.
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +000010107 ASSERT((static_cast<unsigned>(cc_) >> 28) < (1 << 13));
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +000010108 return ConditionField::encode(static_cast<unsigned>(cc_) >> 28)
10109 | StrictField::encode(strict_)
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +000010110 | NeverNanNanField::encode(cc_ == eq ? never_nan_nan_ : false)
10111 | IncludeNumberCompareField::encode(include_number_compare_);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010112}
10113
10114
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010115// StringCharCodeAtGenerator
10116
10117void StringCharCodeAtGenerator::GenerateFast(MacroAssembler* masm) {
10118 Label flat_string;
ager@chromium.orgac091b72010-05-05 07:34:42 +000010119 Label ascii_string;
10120 Label got_char_code;
10121
10122 // If the receiver is a smi trigger the non-string case.
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010123 __ BranchOnSmi(object_, receiver_not_string_);
ager@chromium.orgac091b72010-05-05 07:34:42 +000010124
10125 // Fetch the instance type of the receiver into result register.
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010126 __ ldr(result_, FieldMemOperand(object_, HeapObject::kMapOffset));
10127 __ ldrb(result_, FieldMemOperand(result_, Map::kInstanceTypeOffset));
ager@chromium.orgac091b72010-05-05 07:34:42 +000010128 // If the receiver is not a string trigger the non-string case.
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010129 __ tst(result_, Operand(kIsNotStringMask));
10130 __ b(ne, receiver_not_string_);
ager@chromium.orgac091b72010-05-05 07:34:42 +000010131
10132 // If the index is non-smi trigger the non-smi case.
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010133 __ BranchOnNotSmi(index_, &index_not_smi_);
10134
10135 // Put smi-tagged index into scratch register.
10136 __ mov(scratch_, index_);
10137 __ bind(&got_smi_index_);
ager@chromium.orgac091b72010-05-05 07:34:42 +000010138
10139 // Check for index out of range.
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010140 __ ldr(ip, FieldMemOperand(object_, String::kLengthOffset));
10141 __ cmp(ip, Operand(scratch_));
10142 __ b(ls, index_out_of_range_);
ager@chromium.orgac091b72010-05-05 07:34:42 +000010143
10144 // We need special handling for non-flat strings.
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010145 ASSERT(kSeqStringTag == 0);
10146 __ tst(result_, Operand(kStringRepresentationMask));
10147 __ b(eq, &flat_string);
ager@chromium.orgac091b72010-05-05 07:34:42 +000010148
10149 // Handle non-flat strings.
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010150 __ tst(result_, Operand(kIsConsStringMask));
10151 __ b(eq, &call_runtime_);
ager@chromium.orgac091b72010-05-05 07:34:42 +000010152
10153 // ConsString.
10154 // Check whether the right hand side is the empty string (i.e. if
10155 // this is really a flat string in a cons string). If that is not
10156 // the case we would rather go to the runtime system now to flatten
10157 // the string.
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010158 __ ldr(result_, FieldMemOperand(object_, ConsString::kSecondOffset));
10159 __ LoadRoot(ip, Heap::kEmptyStringRootIndex);
10160 __ cmp(result_, Operand(ip));
10161 __ b(ne, &call_runtime_);
ager@chromium.orgac091b72010-05-05 07:34:42 +000010162 // Get the first of the two strings and load its instance type.
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010163 __ ldr(object_, FieldMemOperand(object_, ConsString::kFirstOffset));
10164 __ ldr(result_, FieldMemOperand(object_, HeapObject::kMapOffset));
10165 __ ldrb(result_, FieldMemOperand(result_, Map::kInstanceTypeOffset));
10166 // If the first cons component is also non-flat, then go to runtime.
10167 ASSERT(kSeqStringTag == 0);
10168 __ tst(result_, Operand(kStringRepresentationMask));
10169 __ b(nz, &call_runtime_);
10170
10171 // Check for 1-byte or 2-byte string.
10172 __ bind(&flat_string);
10173 ASSERT(kAsciiStringTag != 0);
10174 __ tst(result_, Operand(kStringEncodingMask));
10175 __ b(nz, &ascii_string);
10176
10177 // 2-byte string.
10178 // Load the 2-byte character code into the result register. We can
10179 // add without shifting since the smi tag size is the log2 of the
10180 // number of bytes in a two-byte character.
10181 ASSERT(kSmiTag == 0 && kSmiTagSize == 1 && kSmiShiftSize == 0);
10182 __ add(scratch_, object_, Operand(scratch_));
10183 __ ldrh(result_, FieldMemOperand(scratch_, SeqTwoByteString::kHeaderSize));
10184 __ jmp(&got_char_code);
ager@chromium.orgac091b72010-05-05 07:34:42 +000010185
10186 // ASCII string.
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010187 // Load the byte into the result register.
ager@chromium.orgac091b72010-05-05 07:34:42 +000010188 __ bind(&ascii_string);
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010189 __ add(scratch_, object_, Operand(scratch_, LSR, kSmiTagSize));
10190 __ ldrb(result_, FieldMemOperand(scratch_, SeqAsciiString::kHeaderSize));
ager@chromium.orgac091b72010-05-05 07:34:42 +000010191
10192 __ bind(&got_char_code);
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010193 __ mov(result_, Operand(result_, LSL, kSmiTagSize));
10194 __ bind(&exit_);
ager@chromium.orgac091b72010-05-05 07:34:42 +000010195}
10196
10197
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010198void StringCharCodeAtGenerator::GenerateSlow(
10199 MacroAssembler* masm, const RuntimeCallHelper& call_helper) {
10200 __ Abort("Unexpected fallthrough to CharCodeAt slow case");
ager@chromium.orgac091b72010-05-05 07:34:42 +000010201
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010202 // Index is not a smi.
10203 __ bind(&index_not_smi_);
10204 // If index is a heap number, try converting it to an integer.
ager@chromium.org2cc82ae2010-06-14 07:35:38 +000010205 __ CheckMap(index_,
10206 scratch_,
10207 Heap::kHeapNumberMapRootIndex,
10208 index_not_number_,
10209 true);
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010210 call_helper.BeforeCall(masm);
sgjesse@chromium.org82dbbab2010-06-02 08:57:44 +000010211 __ Push(object_, index_);
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010212 __ push(index_); // Consumed by runtime conversion function.
10213 if (index_flags_ == STRING_INDEX_IS_NUMBER) {
10214 __ CallRuntime(Runtime::kNumberToIntegerMapMinusZero, 1);
10215 } else {
10216 ASSERT(index_flags_ == STRING_INDEX_IS_ARRAY_INDEX);
10217 // NumberToSmi discards numbers that are not exact integers.
10218 __ CallRuntime(Runtime::kNumberToSmi, 1);
10219 }
10220 if (!scratch_.is(r0)) {
10221 // Save the conversion result before the pop instructions below
10222 // have a chance to overwrite it.
10223 __ mov(scratch_, r0);
10224 }
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010225 __ pop(index_);
10226 __ pop(object_);
sgjesse@chromium.org82dbbab2010-06-02 08:57:44 +000010227 // Reload the instance type.
10228 __ ldr(result_, FieldMemOperand(object_, HeapObject::kMapOffset));
10229 __ ldrb(result_, FieldMemOperand(result_, Map::kInstanceTypeOffset));
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010230 call_helper.AfterCall(masm);
10231 // If index is still not a smi, it must be out of range.
10232 __ BranchOnNotSmi(scratch_, index_out_of_range_);
10233 // Otherwise, return to the fast path.
10234 __ jmp(&got_smi_index_);
ager@chromium.orgac091b72010-05-05 07:34:42 +000010235
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010236 // Call runtime. We get here when the receiver is a string and the
10237 // index is a number, but the code of getting the actual character
10238 // is too complex (e.g., when the string needs to be flattened).
10239 __ bind(&call_runtime_);
10240 call_helper.BeforeCall(masm);
10241 __ Push(object_, index_);
10242 __ CallRuntime(Runtime::kStringCharCodeAt, 2);
10243 if (!result_.is(r0)) {
10244 __ mov(result_, r0);
10245 }
10246 call_helper.AfterCall(masm);
10247 __ jmp(&exit_);
10248
10249 __ Abort("Unexpected fallthrough from CharCodeAt slow case");
10250}
10251
10252
10253// -------------------------------------------------------------------------
10254// StringCharFromCodeGenerator
10255
10256void StringCharFromCodeGenerator::GenerateFast(MacroAssembler* masm) {
ager@chromium.orgac091b72010-05-05 07:34:42 +000010257 // Fast case of Heap::LookupSingleCharacterStringFromCode.
10258 ASSERT(kSmiTag == 0);
10259 ASSERT(kSmiShiftSize == 0);
10260 ASSERT(IsPowerOf2(String::kMaxAsciiCharCode + 1));
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010261 __ tst(code_,
10262 Operand(kSmiTagMask |
10263 ((~String::kMaxAsciiCharCode) << kSmiTagSize)));
10264 __ b(nz, &slow_case_);
ager@chromium.orgac091b72010-05-05 07:34:42 +000010265
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010266 __ LoadRoot(result_, Heap::kSingleCharacterStringCacheRootIndex);
10267 // At this point code register contains smi tagged ascii char code.
ager@chromium.orgac091b72010-05-05 07:34:42 +000010268 ASSERT(kSmiTag == 0);
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010269 __ add(result_, result_, Operand(code_, LSL, kPointerSizeLog2 - kSmiTagSize));
10270 __ ldr(result_, FieldMemOperand(result_, FixedArray::kHeaderSize));
10271 __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
10272 __ cmp(result_, Operand(ip));
10273 __ b(eq, &slow_case_);
10274 __ bind(&exit_);
10275}
ager@chromium.orgac091b72010-05-05 07:34:42 +000010276
ager@chromium.orgac091b72010-05-05 07:34:42 +000010277
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010278void StringCharFromCodeGenerator::GenerateSlow(
10279 MacroAssembler* masm, const RuntimeCallHelper& call_helper) {
10280 __ Abort("Unexpected fallthrough to CharFromCode slow case");
10281
10282 __ bind(&slow_case_);
10283 call_helper.BeforeCall(masm);
10284 __ push(code_);
10285 __ CallRuntime(Runtime::kCharFromCode, 1);
10286 if (!result_.is(r0)) {
10287 __ mov(result_, r0);
ager@chromium.orgac091b72010-05-05 07:34:42 +000010288 }
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010289 call_helper.AfterCall(masm);
10290 __ jmp(&exit_);
10291
10292 __ Abort("Unexpected fallthrough from CharFromCode slow case");
10293}
10294
10295
10296// -------------------------------------------------------------------------
10297// StringCharAtGenerator
10298
10299void StringCharAtGenerator::GenerateFast(MacroAssembler* masm) {
10300 char_code_at_generator_.GenerateFast(masm);
10301 char_from_code_generator_.GenerateFast(masm);
10302}
10303
10304
10305void StringCharAtGenerator::GenerateSlow(
10306 MacroAssembler* masm, const RuntimeCallHelper& call_helper) {
10307 char_code_at_generator_.GenerateSlow(masm, call_helper);
10308 char_from_code_generator_.GenerateSlow(masm, call_helper);
ager@chromium.orgac091b72010-05-05 07:34:42 +000010309}
10310
10311
10312void StringHelper::GenerateCopyCharacters(MacroAssembler* masm,
10313 Register dest,
10314 Register src,
10315 Register count,
10316 Register scratch,
10317 bool ascii) {
ager@chromium.org5c838252010-02-19 08:53:10 +000010318 Label loop;
10319 Label done;
10320 // This loop just copies one character at a time, as it is only used for very
10321 // short strings.
10322 if (!ascii) {
10323 __ add(count, count, Operand(count), SetCC);
10324 } else {
10325 __ cmp(count, Operand(0));
10326 }
10327 __ b(eq, &done);
10328
10329 __ bind(&loop);
10330 __ ldrb(scratch, MemOperand(src, 1, PostIndex));
10331 // Perform sub between load and dependent store to get the load time to
10332 // complete.
10333 __ sub(count, count, Operand(1), SetCC);
10334 __ strb(scratch, MemOperand(dest, 1, PostIndex));
10335 // last iteration.
10336 __ b(gt, &loop);
10337
10338 __ bind(&done);
10339}
10340
10341
10342enum CopyCharactersFlags {
10343 COPY_ASCII = 1,
10344 DEST_ALWAYS_ALIGNED = 2
10345};
10346
10347
ager@chromium.orgac091b72010-05-05 07:34:42 +000010348void StringHelper::GenerateCopyCharactersLong(MacroAssembler* masm,
10349 Register dest,
10350 Register src,
10351 Register count,
10352 Register scratch1,
10353 Register scratch2,
10354 Register scratch3,
10355 Register scratch4,
10356 Register scratch5,
10357 int flags) {
ager@chromium.org5c838252010-02-19 08:53:10 +000010358 bool ascii = (flags & COPY_ASCII) != 0;
10359 bool dest_always_aligned = (flags & DEST_ALWAYS_ALIGNED) != 0;
10360
10361 if (dest_always_aligned && FLAG_debug_code) {
10362 // Check that destination is actually word aligned if the flag says
10363 // that it is.
10364 __ tst(dest, Operand(kPointerAlignmentMask));
10365 __ Check(eq, "Destination of copy not aligned.");
10366 }
10367
10368 const int kReadAlignment = 4;
10369 const int kReadAlignmentMask = kReadAlignment - 1;
10370 // Ensure that reading an entire aligned word containing the last character
10371 // of a string will not read outside the allocated area (because we pad up
10372 // to kObjectAlignment).
10373 ASSERT(kObjectAlignment >= kReadAlignment);
10374 // Assumes word reads and writes are little endian.
10375 // Nothing to do for zero characters.
10376 Label done;
10377 if (!ascii) {
10378 __ add(count, count, Operand(count), SetCC);
10379 } else {
10380 __ cmp(count, Operand(0));
10381 }
10382 __ b(eq, &done);
10383
10384 // Assume that you cannot read (or write) unaligned.
10385 Label byte_loop;
10386 // Must copy at least eight bytes, otherwise just do it one byte at a time.
10387 __ cmp(count, Operand(8));
10388 __ add(count, dest, Operand(count));
10389 Register limit = count; // Read until src equals this.
10390 __ b(lt, &byte_loop);
10391
10392 if (!dest_always_aligned) {
10393 // Align dest by byte copying. Copies between zero and three bytes.
10394 __ and_(scratch4, dest, Operand(kReadAlignmentMask), SetCC);
10395 Label dest_aligned;
10396 __ b(eq, &dest_aligned);
10397 __ cmp(scratch4, Operand(2));
10398 __ ldrb(scratch1, MemOperand(src, 1, PostIndex));
10399 __ ldrb(scratch2, MemOperand(src, 1, PostIndex), le);
10400 __ ldrb(scratch3, MemOperand(src, 1, PostIndex), lt);
10401 __ strb(scratch1, MemOperand(dest, 1, PostIndex));
10402 __ strb(scratch2, MemOperand(dest, 1, PostIndex), le);
10403 __ strb(scratch3, MemOperand(dest, 1, PostIndex), lt);
10404 __ bind(&dest_aligned);
10405 }
10406
10407 Label simple_loop;
10408
10409 __ sub(scratch4, dest, Operand(src));
10410 __ and_(scratch4, scratch4, Operand(0x03), SetCC);
10411 __ b(eq, &simple_loop);
10412 // Shift register is number of bits in a source word that
10413 // must be combined with bits in the next source word in order
10414 // to create a destination word.
10415
10416 // Complex loop for src/dst that are not aligned the same way.
10417 {
10418 Label loop;
10419 __ mov(scratch4, Operand(scratch4, LSL, 3));
10420 Register left_shift = scratch4;
10421 __ and_(src, src, Operand(~3)); // Round down to load previous word.
10422 __ ldr(scratch1, MemOperand(src, 4, PostIndex));
10423 // Store the "shift" most significant bits of scratch in the least
10424 // signficant bits (i.e., shift down by (32-shift)).
10425 __ rsb(scratch2, left_shift, Operand(32));
10426 Register right_shift = scratch2;
10427 __ mov(scratch1, Operand(scratch1, LSR, right_shift));
10428
10429 __ bind(&loop);
10430 __ ldr(scratch3, MemOperand(src, 4, PostIndex));
10431 __ sub(scratch5, limit, Operand(dest));
10432 __ orr(scratch1, scratch1, Operand(scratch3, LSL, left_shift));
10433 __ str(scratch1, MemOperand(dest, 4, PostIndex));
10434 __ mov(scratch1, Operand(scratch3, LSR, right_shift));
10435 // Loop if four or more bytes left to copy.
10436 // Compare to eight, because we did the subtract before increasing dst.
10437 __ sub(scratch5, scratch5, Operand(8), SetCC);
10438 __ b(ge, &loop);
10439 }
10440 // There is now between zero and three bytes left to copy (negative that
10441 // number is in scratch5), and between one and three bytes already read into
10442 // scratch1 (eight times that number in scratch4). We may have read past
10443 // the end of the string, but because objects are aligned, we have not read
10444 // past the end of the object.
10445 // Find the minimum of remaining characters to move and preloaded characters
10446 // and write those as bytes.
10447 __ add(scratch5, scratch5, Operand(4), SetCC);
10448 __ b(eq, &done);
10449 __ cmp(scratch4, Operand(scratch5, LSL, 3), ne);
10450 // Move minimum of bytes read and bytes left to copy to scratch4.
10451 __ mov(scratch5, Operand(scratch4, LSR, 3), LeaveCC, lt);
10452 // Between one and three (value in scratch5) characters already read into
10453 // scratch ready to write.
10454 __ cmp(scratch5, Operand(2));
10455 __ strb(scratch1, MemOperand(dest, 1, PostIndex));
10456 __ mov(scratch1, Operand(scratch1, LSR, 8), LeaveCC, ge);
10457 __ strb(scratch1, MemOperand(dest, 1, PostIndex), ge);
10458 __ mov(scratch1, Operand(scratch1, LSR, 8), LeaveCC, gt);
10459 __ strb(scratch1, MemOperand(dest, 1, PostIndex), gt);
10460 // Copy any remaining bytes.
10461 __ b(&byte_loop);
10462
10463 // Simple loop.
10464 // Copy words from src to dst, until less than four bytes left.
10465 // Both src and dest are word aligned.
10466 __ bind(&simple_loop);
10467 {
10468 Label loop;
10469 __ bind(&loop);
10470 __ ldr(scratch1, MemOperand(src, 4, PostIndex));
10471 __ sub(scratch3, limit, Operand(dest));
10472 __ str(scratch1, MemOperand(dest, 4, PostIndex));
10473 // Compare to 8, not 4, because we do the substraction before increasing
10474 // dest.
10475 __ cmp(scratch3, Operand(8));
10476 __ b(ge, &loop);
10477 }
10478
10479 // Copy bytes from src to dst until dst hits limit.
10480 __ bind(&byte_loop);
10481 __ cmp(dest, Operand(limit));
10482 __ ldrb(scratch1, MemOperand(src, 1, PostIndex), lt);
10483 __ b(ge, &done);
10484 __ strb(scratch1, MemOperand(dest, 1, PostIndex));
10485 __ b(&byte_loop);
10486
10487 __ bind(&done);
10488}
10489
10490
ager@chromium.orgac091b72010-05-05 07:34:42 +000010491void StringHelper::GenerateTwoCharacterSymbolTableProbe(MacroAssembler* masm,
10492 Register c1,
10493 Register c2,
10494 Register scratch1,
10495 Register scratch2,
10496 Register scratch3,
10497 Register scratch4,
10498 Register scratch5,
10499 Label* not_found) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010500 // Register scratch3 is the general scratch register in this function.
10501 Register scratch = scratch3;
10502
10503 // Make sure that both characters are not digits as such strings has a
10504 // different hash algorithm. Don't try to look for these in the symbol table.
10505 Label not_array_index;
10506 __ sub(scratch, c1, Operand(static_cast<int>('0')));
10507 __ cmp(scratch, Operand(static_cast<int>('9' - '0')));
10508 __ b(hi, &not_array_index);
10509 __ sub(scratch, c2, Operand(static_cast<int>('0')));
10510 __ cmp(scratch, Operand(static_cast<int>('9' - '0')));
10511
10512 // If check failed combine both characters into single halfword.
10513 // This is required by the contract of the method: code at the
10514 // not_found branch expects this combination in c1 register
10515 __ orr(c1, c1, Operand(c2, LSL, kBitsPerByte), LeaveCC, ls);
10516 __ b(ls, not_found);
10517
10518 __ bind(&not_array_index);
10519 // Calculate the two character string hash.
10520 Register hash = scratch1;
ager@chromium.orgac091b72010-05-05 07:34:42 +000010521 StringHelper::GenerateHashInit(masm, hash, c1);
10522 StringHelper::GenerateHashAddCharacter(masm, hash, c2);
10523 StringHelper::GenerateHashGetHash(masm, hash);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010524
10525 // Collect the two characters in a register.
10526 Register chars = c1;
10527 __ orr(chars, chars, Operand(c2, LSL, kBitsPerByte));
10528
10529 // chars: two character string, char 1 in byte 0 and char 2 in byte 1.
10530 // hash: hash of two character string.
10531
10532 // Load symbol table
10533 // Load address of first element of the symbol table.
10534 Register symbol_table = c2;
10535 __ LoadRoot(symbol_table, Heap::kSymbolTableRootIndex);
10536
10537 // Load undefined value
10538 Register undefined = scratch4;
10539 __ LoadRoot(undefined, Heap::kUndefinedValueRootIndex);
10540
10541 // Calculate capacity mask from the symbol table capacity.
10542 Register mask = scratch2;
10543 __ ldr(mask, FieldMemOperand(symbol_table, SymbolTable::kCapacityOffset));
10544 __ mov(mask, Operand(mask, ASR, 1));
10545 __ sub(mask, mask, Operand(1));
10546
10547 // Calculate untagged address of the first element of the symbol table.
10548 Register first_symbol_table_element = symbol_table;
10549 __ add(first_symbol_table_element, symbol_table,
10550 Operand(SymbolTable::kElementsStartOffset - kHeapObjectTag));
10551
10552 // Registers
10553 // chars: two character string, char 1 in byte 0 and char 2 in byte 1.
10554 // hash: hash of two character string
10555 // mask: capacity mask
10556 // first_symbol_table_element: address of the first element of
10557 // the symbol table
10558 // scratch: -
10559
10560 // Perform a number of probes in the symbol table.
10561 static const int kProbes = 4;
10562 Label found_in_symbol_table;
10563 Label next_probe[kProbes];
10564 for (int i = 0; i < kProbes; i++) {
10565 Register candidate = scratch5; // Scratch register contains candidate.
10566
10567 // Calculate entry in symbol table.
10568 if (i > 0) {
10569 __ add(candidate, hash, Operand(SymbolTable::GetProbeOffset(i)));
10570 } else {
10571 __ mov(candidate, hash);
10572 }
10573
10574 __ and_(candidate, candidate, Operand(mask));
10575
10576 // Load the entry from the symble table.
10577 ASSERT_EQ(1, SymbolTable::kEntrySize);
10578 __ ldr(candidate,
10579 MemOperand(first_symbol_table_element,
10580 candidate,
10581 LSL,
10582 kPointerSizeLog2));
10583
10584 // If entry is undefined no string with this hash can be found.
10585 __ cmp(candidate, undefined);
10586 __ b(eq, not_found);
10587
10588 // If length is not 2 the string is not a candidate.
10589 __ ldr(scratch, FieldMemOperand(candidate, String::kLengthOffset));
ager@chromium.orgac091b72010-05-05 07:34:42 +000010590 __ cmp(scratch, Operand(Smi::FromInt(2)));
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010591 __ b(ne, &next_probe[i]);
10592
10593 // Check that the candidate is a non-external ascii string.
10594 __ ldr(scratch, FieldMemOperand(candidate, HeapObject::kMapOffset));
10595 __ ldrb(scratch, FieldMemOperand(scratch, Map::kInstanceTypeOffset));
10596 __ JumpIfInstanceTypeIsNotSequentialAscii(scratch, scratch,
10597 &next_probe[i]);
10598
10599 // Check if the two characters match.
10600 // Assumes that word load is little endian.
10601 __ ldrh(scratch, FieldMemOperand(candidate, SeqAsciiString::kHeaderSize));
10602 __ cmp(chars, scratch);
10603 __ b(eq, &found_in_symbol_table);
10604 __ bind(&next_probe[i]);
10605 }
10606
10607 // No matching 2 character string found by probing.
10608 __ jmp(not_found);
10609
10610 // Scratch register contains result when we fall through to here.
10611 Register result = scratch;
10612 __ bind(&found_in_symbol_table);
ager@chromium.org357bf652010-04-12 11:30:10 +000010613 __ Move(r0, result);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010614}
10615
10616
ager@chromium.orgac091b72010-05-05 07:34:42 +000010617void StringHelper::GenerateHashInit(MacroAssembler* masm,
10618 Register hash,
10619 Register character) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010620 // hash = character + (character << 10);
10621 __ add(hash, character, Operand(character, LSL, 10));
10622 // hash ^= hash >> 6;
10623 __ eor(hash, hash, Operand(hash, ASR, 6));
10624}
10625
10626
ager@chromium.orgac091b72010-05-05 07:34:42 +000010627void StringHelper::GenerateHashAddCharacter(MacroAssembler* masm,
10628 Register hash,
10629 Register character) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010630 // hash += character;
10631 __ add(hash, hash, Operand(character));
10632 // hash += hash << 10;
10633 __ add(hash, hash, Operand(hash, LSL, 10));
10634 // hash ^= hash >> 6;
10635 __ eor(hash, hash, Operand(hash, ASR, 6));
10636}
10637
10638
ager@chromium.orgac091b72010-05-05 07:34:42 +000010639void StringHelper::GenerateHashGetHash(MacroAssembler* masm,
10640 Register hash) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010641 // hash += hash << 3;
10642 __ add(hash, hash, Operand(hash, LSL, 3));
10643 // hash ^= hash >> 11;
10644 __ eor(hash, hash, Operand(hash, ASR, 11));
10645 // hash += hash << 15;
10646 __ add(hash, hash, Operand(hash, LSL, 15), SetCC);
10647
10648 // if (hash == 0) hash = 27;
10649 __ mov(hash, Operand(27), LeaveCC, nz);
10650}
10651
10652
ager@chromium.org5c838252010-02-19 08:53:10 +000010653void SubStringStub::Generate(MacroAssembler* masm) {
10654 Label runtime;
10655
10656 // Stack frame on entry.
10657 // lr: return address
10658 // sp[0]: to
10659 // sp[4]: from
10660 // sp[8]: string
10661
10662 // This stub is called from the native-call %_SubString(...), so
10663 // nothing can be assumed about the arguments. It is tested that:
10664 // "string" is a sequential string,
10665 // both "from" and "to" are smis, and
10666 // 0 <= from <= to <= string.length.
10667 // If any of these assumptions fail, we call the runtime system.
10668
10669 static const int kToOffset = 0 * kPointerSize;
10670 static const int kFromOffset = 1 * kPointerSize;
10671 static const int kStringOffset = 2 * kPointerSize;
10672
10673
10674 // Check bounds and smi-ness.
10675 __ ldr(r7, MemOperand(sp, kToOffset));
10676 __ ldr(r6, MemOperand(sp, kFromOffset));
10677 ASSERT_EQ(0, kSmiTag);
10678 ASSERT_EQ(1, kSmiTagSize + kSmiShiftSize);
10679 // I.e., arithmetic shift right by one un-smi-tags.
10680 __ mov(r2, Operand(r7, ASR, 1), SetCC);
10681 __ mov(r3, Operand(r6, ASR, 1), SetCC, cc);
10682 // If either r2 or r6 had the smi tag bit set, then carry is set now.
10683 __ b(cs, &runtime); // Either "from" or "to" is not a smi.
10684 __ b(mi, &runtime); // From is negative.
10685
10686 __ sub(r2, r2, Operand(r3), SetCC);
10687 __ b(mi, &runtime); // Fail if from > to.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010688 // Special handling of sub-strings of length 1 and 2. One character strings
10689 // are handled in the runtime system (looked up in the single character
10690 // cache). Two character strings are looked for in the symbol cache.
ager@chromium.org5c838252010-02-19 08:53:10 +000010691 __ cmp(r2, Operand(2));
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010692 __ b(lt, &runtime);
ager@chromium.org5c838252010-02-19 08:53:10 +000010693
10694 // r2: length
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010695 // r3: from index (untaged smi)
ager@chromium.org5c838252010-02-19 08:53:10 +000010696 // r6: from (smi)
10697 // r7: to (smi)
10698
10699 // Make sure first argument is a sequential (or flat) string.
10700 __ ldr(r5, MemOperand(sp, kStringOffset));
10701 ASSERT_EQ(0, kSmiTag);
10702 __ tst(r5, Operand(kSmiTagMask));
10703 __ b(eq, &runtime);
10704 Condition is_string = masm->IsObjectStringType(r5, r1);
10705 __ b(NegateCondition(is_string), &runtime);
10706
10707 // r1: instance type
10708 // r2: length
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010709 // r3: from index (untaged smi)
ager@chromium.org5c838252010-02-19 08:53:10 +000010710 // r5: string
10711 // r6: from (smi)
10712 // r7: to (smi)
10713 Label seq_string;
10714 __ and_(r4, r1, Operand(kStringRepresentationMask));
10715 ASSERT(kSeqStringTag < kConsStringTag);
10716 ASSERT(kExternalStringTag > kConsStringTag);
10717 __ cmp(r4, Operand(kConsStringTag));
10718 __ b(gt, &runtime); // External strings go to runtime.
10719 __ b(lt, &seq_string); // Sequential strings are handled directly.
10720
10721 // Cons string. Try to recurse (once) on the first substring.
10722 // (This adds a little more generality than necessary to handle flattened
10723 // cons strings, but not much).
10724 __ ldr(r5, FieldMemOperand(r5, ConsString::kFirstOffset));
10725 __ ldr(r4, FieldMemOperand(r5, HeapObject::kMapOffset));
10726 __ ldrb(r1, FieldMemOperand(r4, Map::kInstanceTypeOffset));
10727 __ tst(r1, Operand(kStringRepresentationMask));
10728 ASSERT_EQ(0, kSeqStringTag);
10729 __ b(ne, &runtime); // Cons and External strings go to runtime.
10730
10731 // Definitly a sequential string.
10732 __ bind(&seq_string);
10733
10734 // r1: instance type.
10735 // r2: length
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010736 // r3: from index (untaged smi)
ager@chromium.org5c838252010-02-19 08:53:10 +000010737 // r5: string
10738 // r6: from (smi)
10739 // r7: to (smi)
10740 __ ldr(r4, FieldMemOperand(r5, String::kLengthOffset));
ager@chromium.orgac091b72010-05-05 07:34:42 +000010741 __ cmp(r4, Operand(r7));
ager@chromium.org5c838252010-02-19 08:53:10 +000010742 __ b(lt, &runtime); // Fail if to > length.
10743
10744 // r1: instance type.
10745 // r2: result string length.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010746 // r3: from index (untaged smi)
ager@chromium.org5c838252010-02-19 08:53:10 +000010747 // r5: string.
10748 // r6: from offset (smi)
10749 // Check for flat ascii string.
10750 Label non_ascii_flat;
10751 __ tst(r1, Operand(kStringEncodingMask));
10752 ASSERT_EQ(0, kTwoByteStringTag);
10753 __ b(eq, &non_ascii_flat);
10754
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010755 Label result_longer_than_two;
10756 __ cmp(r2, Operand(2));
10757 __ b(gt, &result_longer_than_two);
10758
10759 // Sub string of length 2 requested.
10760 // Get the two characters forming the sub string.
10761 __ add(r5, r5, Operand(r3));
10762 __ ldrb(r3, FieldMemOperand(r5, SeqAsciiString::kHeaderSize));
10763 __ ldrb(r4, FieldMemOperand(r5, SeqAsciiString::kHeaderSize + 1));
10764
10765 // Try to lookup two character string in symbol table.
10766 Label make_two_character_string;
ager@chromium.orgac091b72010-05-05 07:34:42 +000010767 StringHelper::GenerateTwoCharacterSymbolTableProbe(
10768 masm, r3, r4, r1, r5, r6, r7, r9, &make_two_character_string);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010769 __ IncrementCounter(&Counters::sub_string_native, 1, r3, r4);
10770 __ add(sp, sp, Operand(3 * kPointerSize));
10771 __ Ret();
10772
10773 // r2: result string length.
10774 // r3: two characters combined into halfword in little endian byte order.
10775 __ bind(&make_two_character_string);
10776 __ AllocateAsciiString(r0, r2, r4, r5, r9, &runtime);
10777 __ strh(r3, FieldMemOperand(r0, SeqAsciiString::kHeaderSize));
10778 __ IncrementCounter(&Counters::sub_string_native, 1, r3, r4);
10779 __ add(sp, sp, Operand(3 * kPointerSize));
10780 __ Ret();
10781
10782 __ bind(&result_longer_than_two);
10783
ager@chromium.org5c838252010-02-19 08:53:10 +000010784 // Allocate the result.
10785 __ AllocateAsciiString(r0, r2, r3, r4, r1, &runtime);
10786
10787 // r0: result string.
10788 // r2: result string length.
10789 // r5: string.
10790 // r6: from offset (smi)
10791 // Locate first character of result.
10792 __ add(r1, r0, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
10793 // Locate 'from' character of string.
10794 __ add(r5, r5, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
10795 __ add(r5, r5, Operand(r6, ASR, 1));
10796
10797 // r0: result string.
10798 // r1: first character of result string.
10799 // r2: result string length.
10800 // r5: first character of sub string to copy.
10801 ASSERT_EQ(0, SeqAsciiString::kHeaderSize & kObjectAlignmentMask);
ager@chromium.orgac091b72010-05-05 07:34:42 +000010802 StringHelper::GenerateCopyCharactersLong(masm, r1, r5, r2, r3, r4, r6, r7, r9,
10803 COPY_ASCII | DEST_ALWAYS_ALIGNED);
ager@chromium.org5c838252010-02-19 08:53:10 +000010804 __ IncrementCounter(&Counters::sub_string_native, 1, r3, r4);
10805 __ add(sp, sp, Operand(3 * kPointerSize));
10806 __ Ret();
10807
10808 __ bind(&non_ascii_flat);
10809 // r2: result string length.
10810 // r5: string.
10811 // r6: from offset (smi)
10812 // Check for flat two byte string.
10813
10814 // Allocate the result.
10815 __ AllocateTwoByteString(r0, r2, r1, r3, r4, &runtime);
10816
10817 // r0: result string.
10818 // r2: result string length.
10819 // r5: string.
10820 // Locate first character of result.
10821 __ add(r1, r0, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
10822 // Locate 'from' character of string.
10823 __ add(r5, r5, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
10824 // As "from" is a smi it is 2 times the value which matches the size of a two
10825 // byte character.
10826 __ add(r5, r5, Operand(r6));
10827
10828 // r0: result string.
10829 // r1: first character of result.
10830 // r2: result length.
10831 // r5: first character of string to copy.
10832 ASSERT_EQ(0, SeqTwoByteString::kHeaderSize & kObjectAlignmentMask);
ager@chromium.orgac091b72010-05-05 07:34:42 +000010833 StringHelper::GenerateCopyCharactersLong(masm, r1, r5, r2, r3, r4, r6, r7, r9,
10834 DEST_ALWAYS_ALIGNED);
ager@chromium.org5c838252010-02-19 08:53:10 +000010835 __ IncrementCounter(&Counters::sub_string_native, 1, r3, r4);
10836 __ add(sp, sp, Operand(3 * kPointerSize));
10837 __ Ret();
10838
10839 // Just jump to runtime to create the sub string.
10840 __ bind(&runtime);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010841 __ TailCallRuntime(Runtime::kSubString, 3, 1);
ager@chromium.org5c838252010-02-19 08:53:10 +000010842}
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010843
10844
10845void StringCompareStub::GenerateCompareFlatAsciiStrings(MacroAssembler* masm,
10846 Register left,
10847 Register right,
10848 Register scratch1,
10849 Register scratch2,
10850 Register scratch3,
10851 Register scratch4) {
10852 Label compare_lengths;
10853 // Find minimum length and length difference.
10854 __ ldr(scratch1, FieldMemOperand(left, String::kLengthOffset));
10855 __ ldr(scratch2, FieldMemOperand(right, String::kLengthOffset));
10856 __ sub(scratch3, scratch1, Operand(scratch2), SetCC);
10857 Register length_delta = scratch3;
10858 __ mov(scratch1, scratch2, LeaveCC, gt);
10859 Register min_length = scratch1;
ager@chromium.orgac091b72010-05-05 07:34:42 +000010860 ASSERT(kSmiTag == 0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010861 __ tst(min_length, Operand(min_length));
10862 __ b(eq, &compare_lengths);
10863
ager@chromium.orgac091b72010-05-05 07:34:42 +000010864 // Untag smi.
10865 __ mov(min_length, Operand(min_length, ASR, kSmiTagSize));
10866
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010867 // Setup registers so that we only need to increment one register
10868 // in the loop.
10869 __ add(scratch2, min_length,
10870 Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
10871 __ add(left, left, Operand(scratch2));
10872 __ add(right, right, Operand(scratch2));
10873 // Registers left and right points to the min_length character of strings.
10874 __ rsb(min_length, min_length, Operand(-1));
10875 Register index = min_length;
10876 // Index starts at -min_length.
10877
10878 {
10879 // Compare loop.
10880 Label loop;
10881 __ bind(&loop);
10882 // Compare characters.
10883 __ add(index, index, Operand(1), SetCC);
10884 __ ldrb(scratch2, MemOperand(left, index), ne);
10885 __ ldrb(scratch4, MemOperand(right, index), ne);
10886 // Skip to compare lengths with eq condition true.
10887 __ b(eq, &compare_lengths);
10888 __ cmp(scratch2, scratch4);
10889 __ b(eq, &loop);
10890 // Fallthrough with eq condition false.
10891 }
10892 // Compare lengths - strings up to min-length are equal.
10893 __ bind(&compare_lengths);
10894 ASSERT(Smi::FromInt(EQUAL) == static_cast<Smi*>(0));
10895 // Use zero length_delta as result.
10896 __ mov(r0, Operand(length_delta), SetCC, eq);
10897 // Fall through to here if characters compare not-equal.
10898 __ mov(r0, Operand(Smi::FromInt(GREATER)), LeaveCC, gt);
10899 __ mov(r0, Operand(Smi::FromInt(LESS)), LeaveCC, lt);
10900 __ Ret();
10901}
10902
10903
10904void StringCompareStub::Generate(MacroAssembler* masm) {
10905 Label runtime;
10906
10907 // Stack frame on entry.
ager@chromium.org5c838252010-02-19 08:53:10 +000010908 // sp[0]: right string
10909 // sp[4]: left string
10910 __ ldr(r0, MemOperand(sp, 1 * kPointerSize)); // left
10911 __ ldr(r1, MemOperand(sp, 0 * kPointerSize)); // right
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010912
10913 Label not_same;
10914 __ cmp(r0, r1);
10915 __ b(ne, &not_same);
10916 ASSERT_EQ(0, EQUAL);
10917 ASSERT_EQ(0, kSmiTag);
10918 __ mov(r0, Operand(Smi::FromInt(EQUAL)));
10919 __ IncrementCounter(&Counters::string_compare_native, 1, r1, r2);
10920 __ add(sp, sp, Operand(2 * kPointerSize));
10921 __ Ret();
10922
10923 __ bind(&not_same);
10924
10925 // Check that both objects are sequential ascii strings.
10926 __ JumpIfNotBothSequentialAsciiStrings(r0, r1, r2, r3, &runtime);
10927
10928 // Compare flat ascii strings natively. Remove arguments from stack first.
10929 __ IncrementCounter(&Counters::string_compare_native, 1, r2, r3);
10930 __ add(sp, sp, Operand(2 * kPointerSize));
10931 GenerateCompareFlatAsciiStrings(masm, r0, r1, r2, r3, r4, r5);
10932
10933 // Call the runtime; it returns -1 (less), 0 (equal), or 1 (greater)
10934 // tagged as a small integer.
10935 __ bind(&runtime);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010936 __ TailCallRuntime(Runtime::kStringCompare, 2, 1);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010937}
10938
10939
ager@chromium.org5c838252010-02-19 08:53:10 +000010940void StringAddStub::Generate(MacroAssembler* masm) {
10941 Label string_add_runtime;
10942 // Stack on entry:
10943 // sp[0]: second argument.
10944 // sp[4]: first argument.
10945
10946 // Load the two arguments.
10947 __ ldr(r0, MemOperand(sp, 1 * kPointerSize)); // First argument.
10948 __ ldr(r1, MemOperand(sp, 0 * kPointerSize)); // Second argument.
10949
10950 // Make sure that both arguments are strings if not known in advance.
10951 if (string_check_) {
10952 ASSERT_EQ(0, kSmiTag);
10953 __ JumpIfEitherSmi(r0, r1, &string_add_runtime);
10954 // Load instance types.
10955 __ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset));
10956 __ ldr(r5, FieldMemOperand(r1, HeapObject::kMapOffset));
10957 __ ldrb(r4, FieldMemOperand(r4, Map::kInstanceTypeOffset));
10958 __ ldrb(r5, FieldMemOperand(r5, Map::kInstanceTypeOffset));
10959 ASSERT_EQ(0, kStringTag);
10960 // If either is not a string, go to runtime.
10961 __ tst(r4, Operand(kIsNotStringMask));
10962 __ tst(r5, Operand(kIsNotStringMask), eq);
10963 __ b(ne, &string_add_runtime);
10964 }
10965
10966 // Both arguments are strings.
10967 // r0: first string
10968 // r1: second string
10969 // r4: first string instance type (if string_check_)
10970 // r5: second string instance type (if string_check_)
10971 {
10972 Label strings_not_empty;
10973 // Check if either of the strings are empty. In that case return the other.
10974 __ ldr(r2, FieldMemOperand(r0, String::kLengthOffset));
10975 __ ldr(r3, FieldMemOperand(r1, String::kLengthOffset));
ager@chromium.orgac091b72010-05-05 07:34:42 +000010976 ASSERT(kSmiTag == 0);
10977 __ cmp(r2, Operand(Smi::FromInt(0))); // Test if first string is empty.
ager@chromium.org5c838252010-02-19 08:53:10 +000010978 __ mov(r0, Operand(r1), LeaveCC, eq); // If first is empty, return second.
ager@chromium.orgac091b72010-05-05 07:34:42 +000010979 ASSERT(kSmiTag == 0);
10980 // Else test if second string is empty.
10981 __ cmp(r3, Operand(Smi::FromInt(0)), ne);
ager@chromium.org5c838252010-02-19 08:53:10 +000010982 __ b(ne, &strings_not_empty); // If either string was empty, return r0.
10983
10984 __ IncrementCounter(&Counters::string_add_native, 1, r2, r3);
10985 __ add(sp, sp, Operand(2 * kPointerSize));
10986 __ Ret();
10987
10988 __ bind(&strings_not_empty);
10989 }
10990
ager@chromium.orgac091b72010-05-05 07:34:42 +000010991 __ mov(r2, Operand(r2, ASR, kSmiTagSize));
10992 __ mov(r3, Operand(r3, ASR, kSmiTagSize));
ager@chromium.org5c838252010-02-19 08:53:10 +000010993 // Both strings are non-empty.
10994 // r0: first string
10995 // r1: second string
10996 // r2: length of first string
10997 // r3: length of second string
10998 // r4: first string instance type (if string_check_)
10999 // r5: second string instance type (if string_check_)
11000 // Look at the length of the result of adding the two strings.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000011001 Label string_add_flat_result, longer_than_two;
ager@chromium.org5c838252010-02-19 08:53:10 +000011002 // Adding two lengths can't overflow.
11003 ASSERT(String::kMaxLength * 2 > String::kMaxLength);
11004 __ add(r6, r2, Operand(r3));
11005 // Use the runtime system when adding two one character strings, as it
11006 // contains optimizations for this specific case using the symbol table.
11007 __ cmp(r6, Operand(2));
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000011008 __ b(ne, &longer_than_two);
11009
11010 // Check that both strings are non-external ascii strings.
11011 if (!string_check_) {
11012 __ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset));
11013 __ ldr(r5, FieldMemOperand(r1, HeapObject::kMapOffset));
11014 __ ldrb(r4, FieldMemOperand(r4, Map::kInstanceTypeOffset));
11015 __ ldrb(r5, FieldMemOperand(r5, Map::kInstanceTypeOffset));
11016 }
11017 __ JumpIfBothInstanceTypesAreNotSequentialAscii(r4, r5, r6, r7,
11018 &string_add_runtime);
11019
11020 // Get the two characters forming the sub string.
11021 __ ldrb(r2, FieldMemOperand(r0, SeqAsciiString::kHeaderSize));
11022 __ ldrb(r3, FieldMemOperand(r1, SeqAsciiString::kHeaderSize));
11023
11024 // Try to lookup two character string in symbol table. If it is not found
11025 // just allocate a new one.
11026 Label make_two_character_string;
ager@chromium.orgac091b72010-05-05 07:34:42 +000011027 StringHelper::GenerateTwoCharacterSymbolTableProbe(
11028 masm, r2, r3, r6, r7, r4, r5, r9, &make_two_character_string);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000011029 __ IncrementCounter(&Counters::string_add_native, 1, r2, r3);
11030 __ add(sp, sp, Operand(2 * kPointerSize));
11031 __ Ret();
11032
11033 __ bind(&make_two_character_string);
11034 // Resulting string has length 2 and first chars of two strings
11035 // are combined into single halfword in r2 register.
11036 // So we can fill resulting string without two loops by a single
11037 // halfword store instruction (which assumes that processor is
11038 // in a little endian mode)
11039 __ mov(r6, Operand(2));
11040 __ AllocateAsciiString(r0, r6, r4, r5, r9, &string_add_runtime);
11041 __ strh(r2, FieldMemOperand(r0, SeqAsciiString::kHeaderSize));
11042 __ IncrementCounter(&Counters::string_add_native, 1, r2, r3);
11043 __ add(sp, sp, Operand(2 * kPointerSize));
11044 __ Ret();
11045
11046 __ bind(&longer_than_two);
ager@chromium.org5c838252010-02-19 08:53:10 +000011047 // Check if resulting string will be flat.
11048 __ cmp(r6, Operand(String::kMinNonFlatLength));
11049 __ b(lt, &string_add_flat_result);
11050 // Handle exceptionally long strings in the runtime system.
11051 ASSERT((String::kMaxLength & 0x80000000) == 0);
11052 ASSERT(IsPowerOf2(String::kMaxLength + 1));
11053 // kMaxLength + 1 is representable as shifted literal, kMaxLength is not.
11054 __ cmp(r6, Operand(String::kMaxLength + 1));
11055 __ b(hs, &string_add_runtime);
11056
11057 // If result is not supposed to be flat, allocate a cons string object.
11058 // If both strings are ascii the result is an ascii cons string.
11059 if (!string_check_) {
11060 __ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset));
11061 __ ldr(r5, FieldMemOperand(r1, HeapObject::kMapOffset));
11062 __ ldrb(r4, FieldMemOperand(r4, Map::kInstanceTypeOffset));
11063 __ ldrb(r5, FieldMemOperand(r5, Map::kInstanceTypeOffset));
11064 }
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +000011065 Label non_ascii, allocated, ascii_data;
ager@chromium.org5c838252010-02-19 08:53:10 +000011066 ASSERT_EQ(0, kTwoByteStringTag);
11067 __ tst(r4, Operand(kStringEncodingMask));
11068 __ tst(r5, Operand(kStringEncodingMask), ne);
11069 __ b(eq, &non_ascii);
11070
11071 // Allocate an ASCII cons string.
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +000011072 __ bind(&ascii_data);
ager@chromium.org5c838252010-02-19 08:53:10 +000011073 __ AllocateAsciiConsString(r7, r6, r4, r5, &string_add_runtime);
11074 __ bind(&allocated);
11075 // Fill the fields of the cons string.
11076 __ str(r0, FieldMemOperand(r7, ConsString::kFirstOffset));
11077 __ str(r1, FieldMemOperand(r7, ConsString::kSecondOffset));
11078 __ mov(r0, Operand(r7));
11079 __ IncrementCounter(&Counters::string_add_native, 1, r2, r3);
11080 __ add(sp, sp, Operand(2 * kPointerSize));
11081 __ Ret();
11082
11083 __ bind(&non_ascii);
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +000011084 // At least one of the strings is two-byte. Check whether it happens
11085 // to contain only ascii characters.
11086 // r4: first instance type.
11087 // r5: second instance type.
11088 __ tst(r4, Operand(kAsciiDataHintMask));
11089 __ tst(r5, Operand(kAsciiDataHintMask), ne);
11090 __ b(ne, &ascii_data);
11091 __ eor(r4, r4, Operand(r5));
11092 ASSERT(kAsciiStringTag != 0 && kAsciiDataHintTag != 0);
11093 __ and_(r4, r4, Operand(kAsciiStringTag | kAsciiDataHintTag));
11094 __ cmp(r4, Operand(kAsciiStringTag | kAsciiDataHintTag));
11095 __ b(eq, &ascii_data);
11096
ager@chromium.org5c838252010-02-19 08:53:10 +000011097 // Allocate a two byte cons string.
11098 __ AllocateTwoByteConsString(r7, r6, r4, r5, &string_add_runtime);
11099 __ jmp(&allocated);
11100
11101 // Handle creating a flat result. First check that both strings are
11102 // sequential and that they have the same encoding.
11103 // r0: first string
11104 // r1: second string
11105 // r2: length of first string
11106 // r3: length of second string
11107 // r4: first string instance type (if string_check_)
11108 // r5: second string instance type (if string_check_)
11109 // r6: sum of lengths.
11110 __ bind(&string_add_flat_result);
11111 if (!string_check_) {
11112 __ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset));
11113 __ ldr(r5, FieldMemOperand(r1, HeapObject::kMapOffset));
11114 __ ldrb(r4, FieldMemOperand(r4, Map::kInstanceTypeOffset));
11115 __ ldrb(r5, FieldMemOperand(r5, Map::kInstanceTypeOffset));
11116 }
11117 // Check that both strings are sequential.
11118 ASSERT_EQ(0, kSeqStringTag);
11119 __ tst(r4, Operand(kStringRepresentationMask));
11120 __ tst(r5, Operand(kStringRepresentationMask), eq);
11121 __ b(ne, &string_add_runtime);
11122 // Now check if both strings have the same encoding (ASCII/Two-byte).
11123 // r0: first string.
11124 // r1: second string.
11125 // r2: length of first string.
11126 // r3: length of second string.
11127 // r6: sum of lengths..
11128 Label non_ascii_string_add_flat_result;
11129 ASSERT(IsPowerOf2(kStringEncodingMask)); // Just one bit to test.
11130 __ eor(r7, r4, Operand(r5));
11131 __ tst(r7, Operand(kStringEncodingMask));
11132 __ b(ne, &string_add_runtime);
11133 // And see if it's ASCII or two-byte.
11134 __ tst(r4, Operand(kStringEncodingMask));
11135 __ b(eq, &non_ascii_string_add_flat_result);
11136
11137 // Both strings are sequential ASCII strings. We also know that they are
11138 // short (since the sum of the lengths is less than kMinNonFlatLength).
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000011139 // r6: length of resulting flat string
ager@chromium.org5c838252010-02-19 08:53:10 +000011140 __ AllocateAsciiString(r7, r6, r4, r5, r9, &string_add_runtime);
11141 // Locate first character of result.
11142 __ add(r6, r7, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
11143 // Locate first character of first argument.
11144 __ add(r0, r0, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
11145 // r0: first character of first string.
11146 // r1: second string.
11147 // r2: length of first string.
11148 // r3: length of second string.
11149 // r6: first character of result.
11150 // r7: result string.
ager@chromium.orgac091b72010-05-05 07:34:42 +000011151 StringHelper::GenerateCopyCharacters(masm, r6, r0, r2, r4, true);
ager@chromium.org5c838252010-02-19 08:53:10 +000011152
11153 // Load second argument and locate first character.
11154 __ add(r1, r1, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
11155 // r1: first character of second string.
11156 // r3: length of second string.
11157 // r6: next character of result.
11158 // r7: result string.
ager@chromium.orgac091b72010-05-05 07:34:42 +000011159 StringHelper::GenerateCopyCharacters(masm, r6, r1, r3, r4, true);
ager@chromium.org5c838252010-02-19 08:53:10 +000011160 __ mov(r0, Operand(r7));
11161 __ IncrementCounter(&Counters::string_add_native, 1, r2, r3);
11162 __ add(sp, sp, Operand(2 * kPointerSize));
11163 __ Ret();
11164
11165 __ bind(&non_ascii_string_add_flat_result);
11166 // Both strings are sequential two byte strings.
11167 // r0: first string.
11168 // r1: second string.
11169 // r2: length of first string.
11170 // r3: length of second string.
11171 // r6: sum of length of strings.
11172 __ AllocateTwoByteString(r7, r6, r4, r5, r9, &string_add_runtime);
11173 // r0: first string.
11174 // r1: second string.
11175 // r2: length of first string.
11176 // r3: length of second string.
11177 // r7: result string.
11178
11179 // Locate first character of result.
11180 __ add(r6, r7, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
11181 // Locate first character of first argument.
11182 __ add(r0, r0, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
11183
11184 // r0: first character of first string.
11185 // r1: second string.
11186 // r2: length of first string.
11187 // r3: length of second string.
11188 // r6: first character of result.
11189 // r7: result string.
ager@chromium.orgac091b72010-05-05 07:34:42 +000011190 StringHelper::GenerateCopyCharacters(masm, r6, r0, r2, r4, false);
ager@chromium.org5c838252010-02-19 08:53:10 +000011191
11192 // Locate first character of second argument.
11193 __ add(r1, r1, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
11194
11195 // r1: first character of second string.
11196 // r3: length of second string.
11197 // r6: next character of result (after copy of first string).
11198 // r7: result string.
ager@chromium.orgac091b72010-05-05 07:34:42 +000011199 StringHelper::GenerateCopyCharacters(masm, r6, r1, r3, r4, false);
ager@chromium.org5c838252010-02-19 08:53:10 +000011200
11201 __ mov(r0, Operand(r7));
11202 __ IncrementCounter(&Counters::string_add_native, 1, r2, r3);
11203 __ add(sp, sp, Operand(2 * kPointerSize));
11204 __ Ret();
11205
11206 // Just jump to runtime to add the two strings.
11207 __ bind(&string_add_runtime);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000011208 __ TailCallRuntime(Runtime::kStringAdd, 2, 1);
ager@chromium.org5c838252010-02-19 08:53:10 +000011209}
11210
11211
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000011212#undef __
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011213
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011214} } // namespace v8::internal
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +000011215
11216#endif // V8_TARGET_ARCH_ARM