blob: f923c092d1b3a9131e9f7f26d7939c721b3dc2d4 [file] [log] [blame]
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001// Copyright 2010 the V8 project authors. All rights reserved.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28#include "v8.h"
29
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +000030#if defined(V8_TARGET_ARCH_ARM)
31
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000032#include "bootstrapper.h"
33#include "codegen-inl.h"
ager@chromium.orgc4c92722009-11-18 14:12:51 +000034#include "compiler.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000035#include "debug.h"
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000036#include "ic-inl.h"
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000037#include "jsregexp.h"
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +000038#include "jump-target-light-inl.h"
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000039#include "parser.h"
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000040#include "regexp-macro-assembler.h"
41#include "regexp-stack.h"
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000042#include "register-allocator-inl.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000043#include "runtime.h"
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000044#include "scopes.h"
sgjesse@chromium.org833cdd72010-02-26 10:06:16 +000045#include "virtual-frame-inl.h"
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +000046#include "virtual-frame-arm-inl.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000047
kasperl@chromium.org71affb52009-05-26 05:44:31 +000048namespace v8 {
49namespace internal {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000050
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +000051
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000052static void EmitIdenticalObjectComparison(MacroAssembler* masm,
53 Label* slow,
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000054 Condition cc,
55 bool never_nan_nan);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000056static void EmitSmiNonsmiComparison(MacroAssembler* masm,
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000057 Label* lhs_not_nan,
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000058 Label* slow,
59 bool strict);
60static void EmitTwoNonNanDoubleComparison(MacroAssembler* masm, Condition cc);
61static void EmitStrictTwoHeapObjectCompare(MacroAssembler* masm);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000062static void MultiplyByKnownInt(MacroAssembler* masm,
63 Register source,
64 Register destination,
65 int known_int);
66static bool IsEasyToMultiplyBy(int x);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000067
68
ricow@chromium.org30ce4112010-05-31 10:38:25 +000069#define __ ACCESS_MASM(masm_)
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000070
ager@chromium.orge2902be2009-06-08 12:21:35 +000071// -------------------------------------------------------------------------
72// Platform-specific DeferredCode functions.
73
74void DeferredCode::SaveRegisters() {
ricow@chromium.org30ce4112010-05-31 10:38:25 +000075 // On ARM you either have a completely spilled frame or you
76 // handle it yourself, but at the moment there's no automation
77 // of registers and deferred code.
ager@chromium.orge2902be2009-06-08 12:21:35 +000078}
79
80
81void DeferredCode::RestoreRegisters() {
ricow@chromium.org30ce4112010-05-31 10:38:25 +000082}
83
84
85// -------------------------------------------------------------------------
86// Platform-specific RuntimeCallHelper functions.
87
88void VirtualFrameRuntimeCallHelper::BeforeCall(MacroAssembler* masm) const {
89 frame_state_->frame()->AssertIsSpilled();
90}
91
92
93void VirtualFrameRuntimeCallHelper::AfterCall(MacroAssembler* masm) const {
94}
95
96
97void ICRuntimeCallHelper::BeforeCall(MacroAssembler* masm) const {
98 masm->EnterInternalFrame();
99}
100
101
102void ICRuntimeCallHelper::AfterCall(MacroAssembler* masm) const {
103 masm->LeaveInternalFrame();
ager@chromium.orge2902be2009-06-08 12:21:35 +0000104}
105
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000106
107// -------------------------------------------------------------------------
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000108// CodeGenState implementation.
109
ager@chromium.org7c537e22008-10-16 08:43:32 +0000110CodeGenState::CodeGenState(CodeGenerator* owner)
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000111 : owner_(owner),
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +0000112 previous_(owner->state()) {
113 owner->set_state(this);
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000114}
115
116
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +0000117ConditionCodeGenState::ConditionCodeGenState(CodeGenerator* owner,
118 JumpTarget* true_target,
119 JumpTarget* false_target)
120 : CodeGenState(owner),
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000121 true_target_(true_target),
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +0000122 false_target_(false_target) {
123 owner->set_state(this);
124}
125
126
127TypeInfoCodeGenState::TypeInfoCodeGenState(CodeGenerator* owner,
128 Slot* slot,
129 TypeInfo type_info)
130 : CodeGenState(owner),
131 slot_(slot) {
132 owner->set_state(this);
133 old_type_info_ = owner->set_type_info(slot, type_info);
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000134}
135
136
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000137CodeGenState::~CodeGenState() {
138 ASSERT(owner_->state() == this);
139 owner_->set_state(previous_);
140}
141
142
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +0000143TypeInfoCodeGenState::~TypeInfoCodeGenState() {
144 owner()->set_type_info(slot_, old_type_info_);
145}
146
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000147// -------------------------------------------------------------------------
ager@chromium.org7c537e22008-10-16 08:43:32 +0000148// CodeGenerator implementation
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000149
ager@chromium.org5c838252010-02-19 08:53:10 +0000150CodeGenerator::CodeGenerator(MacroAssembler* masm)
151 : deferred_(8),
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000152 masm_(masm),
ager@chromium.org5c838252010-02-19 08:53:10 +0000153 info_(NULL),
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000154 frame_(NULL),
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000155 allocator_(NULL),
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000156 cc_reg_(al),
157 state_(NULL),
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000158 loop_nesting_(0),
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +0000159 type_info_(NULL),
fschneider@chromium.org40b9da32010-06-28 11:29:21 +0000160 function_return_(JumpTarget::BIDIRECTIONAL),
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +0000161 function_return_is_shadowed_(false) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000162}
163
164
165// Calling conventions:
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000166// fp: caller's frame pointer
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000167// sp: stack pointer
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000168// r1: called JS function
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000169// cp: callee's context
170
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000171void CodeGenerator::Generate(CompilationInfo* info) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000172 // Record the position for debugging purposes.
ager@chromium.org5c838252010-02-19 08:53:10 +0000173 CodeForFunctionPosition(info->function());
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000174 Comment cmnt(masm_, "[ function compiled by virtual frame code generator");
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000175
176 // Initialize state.
ager@chromium.org5c838252010-02-19 08:53:10 +0000177 info_ = info;
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +0000178
179 int slots = scope()->num_parameters() + scope()->num_stack_slots();
180 ScopedVector<TypeInfo> type_info_array(slots);
181 type_info_ = &type_info_array;
182
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000183 ASSERT(allocator_ == NULL);
184 RegisterAllocator register_allocator(this);
185 allocator_ = &register_allocator;
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000186 ASSERT(frame_ == NULL);
kasperl@chromium.org71affb52009-05-26 05:44:31 +0000187 frame_ = new VirtualFrame();
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000188 cc_reg_ = al;
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000189
190 // Adjust for function-level loop nesting.
191 ASSERT_EQ(0, loop_nesting_);
192 loop_nesting_ = info->loop_nesting();
193
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000194 {
195 CodeGenState state(this);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000196
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000197 // Entry:
198 // Stack: receiver, arguments
199 // lr: return address
200 // fp: caller's frame pointer
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000201 // sp: stack pointer
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000202 // r1: called JS function
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000203 // cp: callee's context
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000204 allocator_->Initialize();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000205
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000206#ifdef DEBUG
207 if (strlen(FLAG_stop_at) > 0 &&
ager@chromium.org5c838252010-02-19 08:53:10 +0000208 info->function()->name()->IsEqualTo(CStrVector(FLAG_stop_at))) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000209 frame_->SpillAll();
kasper.lund7276f142008-07-30 08:49:36 +0000210 __ stop("stop-at");
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000211 }
212#endif
213
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000214 if (info->mode() == CompilationInfo::PRIMARY) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000215 frame_->Enter();
216 // tos: code slot
217
218 // Allocate space for locals and initialize them. This also checks
219 // for stack overflow.
220 frame_->AllocateStackSlots();
221
fschneider@chromium.org40b9da32010-06-28 11:29:21 +0000222 frame_->AssertIsSpilled();
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +0000223 int heap_slots = scope()->num_heap_slots() - Context::MIN_CONTEXT_SLOTS;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000224 if (heap_slots > 0) {
225 // Allocate local context.
226 // Get outer context and create a new context based on it.
227 __ ldr(r0, frame_->Function());
228 frame_->EmitPush(r0);
229 if (heap_slots <= FastNewContextStub::kMaximumSlots) {
230 FastNewContextStub stub(heap_slots);
231 frame_->CallStub(&stub, 1);
232 } else {
233 frame_->CallRuntime(Runtime::kNewContext, 1);
234 }
235
236#ifdef DEBUG
237 JumpTarget verified_true;
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000238 __ cmp(r0, cp);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000239 verified_true.Branch(eq);
240 __ stop("NewContext: r0 is expected to be the same as cp");
241 verified_true.Bind();
242#endif
243 // Update context local.
244 __ str(cp, frame_->Context());
245 }
246
247 // TODO(1241774): Improve this code:
248 // 1) only needed if we have a context
249 // 2) no need to recompute context ptr every single time
250 // 3) don't copy parameter operand code from SlotOperand!
251 {
252 Comment cmnt2(masm_, "[ copy context parameters into .context");
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000253 // Note that iteration order is relevant here! If we have the same
254 // parameter twice (e.g., function (x, y, x)), and that parameter
255 // needs to be copied into the context, it must be the last argument
256 // passed to the parameter that needs to be copied. This is a rare
257 // case so we don't check for it, instead we rely on the copying
258 // order: such a parameter is copied repeatedly into the same
259 // context location and thus the last value is what is seen inside
260 // the function.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +0000261 frame_->AssertIsSpilled();
ager@chromium.org5c838252010-02-19 08:53:10 +0000262 for (int i = 0; i < scope()->num_parameters(); i++) {
263 Variable* par = scope()->parameter(i);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000264 Slot* slot = par->slot();
265 if (slot != NULL && slot->type() == Slot::CONTEXT) {
ager@chromium.org5c838252010-02-19 08:53:10 +0000266 ASSERT(!scope()->is_global_scope()); // No params in global scope.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000267 __ ldr(r1, frame_->ParameterAt(i));
268 // Loads r2 with context; used below in RecordWrite.
269 __ str(r1, SlotOperand(slot, r2));
270 // Load the offset into r3.
271 int slot_offset =
272 FixedArray::kHeaderSize + slot->index() * kPointerSize;
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +0000273 __ RecordWrite(r2, Operand(slot_offset), r3, r1);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000274 }
275 }
276 }
277
278 // Store the arguments object. This must happen after context
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000279 // initialization because the arguments object may be stored in
280 // the context.
281 if (ArgumentsMode() != NO_ARGUMENTS_ALLOCATION) {
282 StoreArgumentsObject(true);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000283 }
284
285 // Initialize ThisFunction reference if present.
ager@chromium.org5c838252010-02-19 08:53:10 +0000286 if (scope()->is_function_scope() && scope()->function() != NULL) {
fschneider@chromium.org40b9da32010-06-28 11:29:21 +0000287 frame_->EmitPushRoot(Heap::kTheHoleValueRootIndex);
ager@chromium.org5c838252010-02-19 08:53:10 +0000288 StoreToSlot(scope()->function()->slot(), NOT_CONST_INIT);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000289 }
290 } else {
291 // When used as the secondary compiler for splitting, r1, cp,
292 // fp, and lr have been pushed on the stack. Adjust the virtual
293 // frame to match this state.
294 frame_->Adjust(4);
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000295
296 // Bind all the bailout labels to the beginning of the function.
297 List<CompilationInfo::Bailout*>* bailouts = info->bailouts();
298 for (int i = 0; i < bailouts->length(); i++) {
299 __ bind(bailouts->at(i)->label());
300 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000301 }
302
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000303 // Initialize the function return target after the locals are set
304 // up, because it needs the expected frame height from the frame.
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000305 function_return_.SetExpectedHeight();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000306 function_return_is_shadowed_ = false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000307
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000308 // Generate code to 'execute' declarations and initialize functions
309 // (source elements). In case of an illegal redeclaration we need to
310 // handle that instead of processing the declarations.
ager@chromium.org5c838252010-02-19 08:53:10 +0000311 if (scope()->HasIllegalRedeclaration()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000312 Comment cmnt(masm_, "[ illegal redeclarations");
ager@chromium.org5c838252010-02-19 08:53:10 +0000313 scope()->VisitIllegalRedeclaration(this);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000314 } else {
315 Comment cmnt(masm_, "[ declarations");
ager@chromium.org5c838252010-02-19 08:53:10 +0000316 ProcessDeclarations(scope()->declarations());
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000317 // Bail out if a stack-overflow exception occurred when processing
318 // declarations.
kasper.lund212ac232008-07-16 07:07:30 +0000319 if (HasStackOverflow()) return;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000320 }
321
mads.s.ager31e71382008-08-13 09:32:07 +0000322 if (FLAG_trace) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000323 frame_->CallRuntime(Runtime::kTraceEnter, 0);
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000324 // Ignore the return value.
mads.s.ager31e71382008-08-13 09:32:07 +0000325 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000326
327 // Compile the body of the function in a vanilla state. Don't
328 // bother compiling all the code if the scope has an illegal
329 // redeclaration.
ager@chromium.org5c838252010-02-19 08:53:10 +0000330 if (!scope()->HasIllegalRedeclaration()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000331 Comment cmnt(masm_, "[ function body");
332#ifdef DEBUG
333 bool is_builtin = Bootstrapper::IsActive();
334 bool should_trace =
335 is_builtin ? FLAG_trace_builtin_calls : FLAG_trace_calls;
mads.s.ager31e71382008-08-13 09:32:07 +0000336 if (should_trace) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000337 frame_->CallRuntime(Runtime::kDebugTrace, 0);
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000338 // Ignore the return value.
mads.s.ager31e71382008-08-13 09:32:07 +0000339 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000340#endif
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000341 VisitStatements(info->function()->body());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000342 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000343 }
344
whesse@chromium.org2c186ca2010-06-16 11:32:39 +0000345 // Handle the return from the function.
346 if (has_valid_frame()) {
347 // If there is a valid frame, control flow can fall off the end of
348 // the body. In that case there is an implicit return statement.
349 ASSERT(!function_return_is_shadowed_);
350 frame_->PrepareForReturn();
ager@chromium.orgab99eea2009-08-25 07:05:41 +0000351 __ LoadRoot(r0, Heap::kUndefinedValueRootIndex);
whesse@chromium.org2c186ca2010-06-16 11:32:39 +0000352 if (function_return_.is_bound()) {
353 function_return_.Jump();
354 } else {
355 function_return_.Bind();
356 GenerateReturnSequence();
357 }
358 } else if (function_return_.is_linked()) {
359 // If the return target has dangling jumps to it, then we have not
360 // yet generated the return sequence. This can happen when (a)
361 // control does not flow off the end of the body so we did not
362 // compile an artificial return statement just above, and (b) there
363 // are return statements in the body but (c) they are all shadowed.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000364 function_return_.Bind();
whesse@chromium.org2c186ca2010-06-16 11:32:39 +0000365 GenerateReturnSequence();
mads.s.ager31e71382008-08-13 09:32:07 +0000366 }
367
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000368 // Adjust for function-level loop nesting.
369 ASSERT(loop_nesting_ == info->loop_nesting());
370 loop_nesting_ = 0;
371
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000372 // Code generation state must be reset.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000373 ASSERT(!has_cc());
374 ASSERT(state_ == NULL);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000375 ASSERT(loop_nesting() == 0);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000376 ASSERT(!function_return_is_shadowed_);
377 function_return_.Unuse();
378 DeleteFrame();
379
380 // Process any deferred code using the register allocator.
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +0000381 if (!HasStackOverflow()) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000382 ProcessDeferred();
383 }
384
385 allocator_ = NULL;
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +0000386 type_info_ = NULL;
387}
388
389
390int CodeGenerator::NumberOfSlot(Slot* slot) {
391 if (slot == NULL) return kInvalidSlotNumber;
392 switch (slot->type()) {
393 case Slot::PARAMETER:
394 return slot->index();
395 case Slot::LOCAL:
396 return slot->index() + scope()->num_parameters();
397 default:
398 break;
399 }
400 return kInvalidSlotNumber;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000401}
402
403
ager@chromium.org7c537e22008-10-16 08:43:32 +0000404MemOperand CodeGenerator::SlotOperand(Slot* slot, Register tmp) {
405 // Currently, this assertion will fail if we try to assign to
406 // a constant variable that is constant because it is read-only
407 // (such as the variable referring to a named function expression).
408 // We need to implement assignments to read-only variables.
409 // Ideally, we should do this during AST generation (by converting
410 // such assignments into expression statements); however, in general
411 // we may not be able to make the decision until past AST generation,
412 // that is when the entire program is known.
413 ASSERT(slot != NULL);
414 int index = slot->index();
415 switch (slot->type()) {
416 case Slot::PARAMETER:
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000417 return frame_->ParameterAt(index);
ager@chromium.org7c537e22008-10-16 08:43:32 +0000418
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000419 case Slot::LOCAL:
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000420 return frame_->LocalAt(index);
ager@chromium.org7c537e22008-10-16 08:43:32 +0000421
422 case Slot::CONTEXT: {
423 // Follow the context chain if necessary.
424 ASSERT(!tmp.is(cp)); // do not overwrite context register
425 Register context = cp;
426 int chain_length = scope()->ContextChainLength(slot->var()->scope());
ager@chromium.org381abbb2009-02-25 13:23:22 +0000427 for (int i = 0; i < chain_length; i++) {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000428 // Load the closure.
429 // (All contexts, even 'with' contexts, have a closure,
430 // and it is the same for all contexts inside a function.
431 // There is no need to go to the function context first.)
432 __ ldr(tmp, ContextOperand(context, Context::CLOSURE_INDEX));
433 // Load the function context (which is the incoming, outer context).
434 __ ldr(tmp, FieldMemOperand(tmp, JSFunction::kContextOffset));
435 context = tmp;
436 }
437 // We may have a 'with' context now. Get the function context.
438 // (In fact this mov may never be the needed, since the scope analysis
439 // may not permit a direct context access in this case and thus we are
440 // always at a function context. However it is safe to dereference be-
441 // cause the function context of a function context is itself. Before
442 // deleting this mov we should try to create a counter-example first,
443 // though...)
444 __ ldr(tmp, ContextOperand(context, Context::FCONTEXT_INDEX));
445 return ContextOperand(tmp, index);
446 }
447
448 default:
449 UNREACHABLE();
450 return MemOperand(r0, 0);
451 }
452}
453
454
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000455MemOperand CodeGenerator::ContextSlotOperandCheckExtensions(
456 Slot* slot,
457 Register tmp,
458 Register tmp2,
459 JumpTarget* slow) {
ager@chromium.org381abbb2009-02-25 13:23:22 +0000460 ASSERT(slot->type() == Slot::CONTEXT);
ager@chromium.org381abbb2009-02-25 13:23:22 +0000461 Register context = cp;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000462
ager@chromium.org381abbb2009-02-25 13:23:22 +0000463 for (Scope* s = scope(); s != slot->var()->scope(); s = s->outer_scope()) {
464 if (s->num_heap_slots() > 0) {
465 if (s->calls_eval()) {
466 // Check that extension is NULL.
467 __ ldr(tmp2, ContextOperand(context, Context::EXTENSION_INDEX));
468 __ tst(tmp2, tmp2);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000469 slow->Branch(ne);
ager@chromium.org381abbb2009-02-25 13:23:22 +0000470 }
471 __ ldr(tmp, ContextOperand(context, Context::CLOSURE_INDEX));
472 __ ldr(tmp, FieldMemOperand(tmp, JSFunction::kContextOffset));
473 context = tmp;
474 }
475 }
476 // Check that last extension is NULL.
477 __ ldr(tmp2, ContextOperand(context, Context::EXTENSION_INDEX));
478 __ tst(tmp2, tmp2);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000479 slow->Branch(ne);
ager@chromium.org381abbb2009-02-25 13:23:22 +0000480 __ ldr(tmp, ContextOperand(context, Context::FCONTEXT_INDEX));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000481 return ContextOperand(tmp, slot->index());
ager@chromium.org381abbb2009-02-25 13:23:22 +0000482}
483
484
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000485// Loads a value on TOS. If it is a boolean value, the result may have been
486// (partially) translated into branches, or it may have set the condition
487// code register. If force_cc is set, the value is forced to set the
488// condition code register and no value is pushed. If the condition code
489// register was set, has_cc() is true and cc_reg_ contains the condition to
490// test for 'true'.
ager@chromium.org7c537e22008-10-16 08:43:32 +0000491void CodeGenerator::LoadCondition(Expression* x,
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000492 JumpTarget* true_target,
493 JumpTarget* false_target,
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000494 bool force_cc) {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000495 ASSERT(!has_cc());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000496 int original_height = frame_->height();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000497
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +0000498 { ConditionCodeGenState new_state(this, true_target, false_target);
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000499 Visit(x);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000500
501 // If we hit a stack overflow, we may not have actually visited
502 // the expression. In that case, we ensure that we have a
503 // valid-looking frame state because we will continue to generate
504 // code as we unwind the C++ stack.
505 //
506 // It's possible to have both a stack overflow and a valid frame
507 // state (eg, a subexpression overflowed, visiting it returned
508 // with a dummied frame state, and visiting this expression
509 // returned with a normal-looking state).
510 if (HasStackOverflow() &&
511 has_valid_frame() &&
512 !has_cc() &&
513 frame_->height() == original_height) {
514 true_target->Jump();
515 }
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000516 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000517 if (force_cc && frame_ != NULL && !has_cc()) {
mads.s.ager31e71382008-08-13 09:32:07 +0000518 // Convert the TOS value to a boolean in the condition code register.
519 ToBoolean(true_target, false_target);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000520 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000521 ASSERT(!force_cc || !has_valid_frame() || has_cc());
522 ASSERT(!has_valid_frame() ||
523 (has_cc() && frame_->height() == original_height) ||
524 (!has_cc() && frame_->height() == original_height + 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000525}
526
527
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000528void CodeGenerator::Load(Expression* expr) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000529#ifdef DEBUG
530 int original_height = frame_->height();
531#endif
kasperl@chromium.org71affb52009-05-26 05:44:31 +0000532 JumpTarget true_target;
533 JumpTarget false_target;
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000534 LoadCondition(expr, &true_target, &false_target, false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000535
536 if (has_cc()) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000537 // Convert cc_reg_ into a boolean value.
kasperl@chromium.org71affb52009-05-26 05:44:31 +0000538 JumpTarget loaded;
539 JumpTarget materialize_true;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000540 materialize_true.Branch(cc_reg_);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +0000541 frame_->EmitPushRoot(Heap::kFalseValueRootIndex);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000542 loaded.Jump();
543 materialize_true.Bind();
fschneider@chromium.org40b9da32010-06-28 11:29:21 +0000544 frame_->EmitPushRoot(Heap::kTrueValueRootIndex);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000545 loaded.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000546 cc_reg_ = al;
547 }
548
549 if (true_target.is_linked() || false_target.is_linked()) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000550 // We have at least one condition value that has been "translated"
551 // into a branch, thus it needs to be loaded explicitly.
kasperl@chromium.org71affb52009-05-26 05:44:31 +0000552 JumpTarget loaded;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000553 if (frame_ != NULL) {
554 loaded.Jump(); // Don't lose the current TOS.
555 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000556 bool both = true_target.is_linked() && false_target.is_linked();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000557 // Load "true" if necessary.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000558 if (true_target.is_linked()) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000559 true_target.Bind();
fschneider@chromium.org40b9da32010-06-28 11:29:21 +0000560 frame_->EmitPushRoot(Heap::kTrueValueRootIndex);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000561 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000562 // If both "true" and "false" need to be loaded jump across the code for
563 // "false".
564 if (both) {
565 loaded.Jump();
566 }
567 // Load "false" if necessary.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000568 if (false_target.is_linked()) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000569 false_target.Bind();
fschneider@chromium.org40b9da32010-06-28 11:29:21 +0000570 frame_->EmitPushRoot(Heap::kFalseValueRootIndex);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000571 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000572 // A value is loaded on all paths reaching this point.
573 loaded.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000574 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000575 ASSERT(has_valid_frame());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000576 ASSERT(!has_cc());
ager@chromium.orgac091b72010-05-05 07:34:42 +0000577 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000578}
579
580
ager@chromium.org7c537e22008-10-16 08:43:32 +0000581void CodeGenerator::LoadGlobal() {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +0000582 Register reg = frame_->GetTOSRegister();
583 __ ldr(reg, GlobalObject());
584 frame_->EmitPush(reg);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000585}
586
587
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000588void CodeGenerator::LoadGlobalReceiver(Register scratch) {
fschneider@chromium.org40b9da32010-06-28 11:29:21 +0000589 Register reg = frame_->GetTOSRegister();
590 __ ldr(reg, ContextOperand(cp, Context::GLOBAL_INDEX));
591 __ ldr(reg,
592 FieldMemOperand(reg, GlobalObject::kGlobalReceiverOffset));
593 frame_->EmitPush(reg);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000594}
595
596
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000597ArgumentsAllocationMode CodeGenerator::ArgumentsMode() {
598 if (scope()->arguments() == NULL) return NO_ARGUMENTS_ALLOCATION;
599 ASSERT(scope()->arguments_shadow() != NULL);
600 // We don't want to do lazy arguments allocation for functions that
601 // have heap-allocated contexts, because it interfers with the
602 // uninitialized const tracking in the context objects.
603 return (scope()->num_heap_slots() > 0)
604 ? EAGER_ARGUMENTS_ALLOCATION
605 : LAZY_ARGUMENTS_ALLOCATION;
606}
607
608
609void CodeGenerator::StoreArgumentsObject(bool initial) {
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000610 ArgumentsAllocationMode mode = ArgumentsMode();
611 ASSERT(mode != NO_ARGUMENTS_ALLOCATION);
612
613 Comment cmnt(masm_, "[ store arguments object");
614 if (mode == LAZY_ARGUMENTS_ALLOCATION && initial) {
615 // When using lazy arguments allocation, we store the hole value
616 // as a sentinel indicating that the arguments object hasn't been
617 // allocated yet.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +0000618 frame_->EmitPushRoot(Heap::kTheHoleValueRootIndex);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000619 } else {
fschneider@chromium.org40b9da32010-06-28 11:29:21 +0000620 frame_->SpillAll();
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000621 ArgumentsAccessStub stub(ArgumentsAccessStub::NEW_OBJECT);
622 __ ldr(r2, frame_->Function());
623 // The receiver is below the arguments, the return address, and the
624 // frame pointer on the stack.
625 const int kReceiverDisplacement = 2 + scope()->num_parameters();
626 __ add(r1, fp, Operand(kReceiverDisplacement * kPointerSize));
627 __ mov(r0, Operand(Smi::FromInt(scope()->num_parameters())));
628 frame_->Adjust(3);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +0000629 __ Push(r2, r1, r0);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000630 frame_->CallStub(&stub, 3);
631 frame_->EmitPush(r0);
632 }
633
634 Variable* arguments = scope()->arguments()->var();
635 Variable* shadow = scope()->arguments_shadow()->var();
636 ASSERT(arguments != NULL && arguments->slot() != NULL);
637 ASSERT(shadow != NULL && shadow->slot() != NULL);
638 JumpTarget done;
639 if (mode == LAZY_ARGUMENTS_ALLOCATION && !initial) {
640 // We have to skip storing into the arguments slot if it has
641 // already been written to. This can happen if the a function
642 // has a local variable named 'arguments'.
643 LoadFromSlot(scope()->arguments()->var()->slot(), NOT_INSIDE_TYPEOF);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +0000644 Register arguments = frame_->PopToRegister();
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000645 __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +0000646 __ cmp(arguments, ip);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000647 done.Branch(ne);
648 }
649 StoreToSlot(arguments->slot(), NOT_CONST_INIT);
650 if (mode == LAZY_ARGUMENTS_ALLOCATION) done.Bind();
651 StoreToSlot(shadow->slot(), NOT_CONST_INIT);
652}
653
654
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000655void CodeGenerator::LoadTypeofExpression(Expression* expr) {
656 // Special handling of identifiers as subexpressions of typeof.
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000657 Variable* variable = expr->AsVariableProxy()->AsVariable();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000658 if (variable != NULL && !variable->is_this() && variable->is_global()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000659 // For a global variable we build the property reference
660 // <global>.<variable> and perform a (regular non-contextual) property
661 // load to make sure we do not get reference errors.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000662 Slot global(variable, Slot::CONTEXT, Context::GLOBAL_INDEX);
663 Literal key(variable->name());
ager@chromium.org236ad962008-09-25 09:45:57 +0000664 Property property(&global, &key, RelocInfo::kNoPosition);
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000665 Reference ref(this, &property);
ager@chromium.org357bf652010-04-12 11:30:10 +0000666 ref.GetValue();
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000667 } else if (variable != NULL && variable->slot() != NULL) {
668 // For a variable that rewrites to a slot, we signal it is the immediate
669 // subexpression of a typeof.
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000670 LoadFromSlotCheckForArguments(variable->slot(), INSIDE_TYPEOF);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000671 } else {
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000672 // Anything else can be handled normally.
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000673 Load(expr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000674 }
675}
676
677
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000678Reference::Reference(CodeGenerator* cgen,
679 Expression* expression,
680 bool persist_after_get)
681 : cgen_(cgen),
682 expression_(expression),
683 type_(ILLEGAL),
684 persist_after_get_(persist_after_get) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000685 cgen->LoadReference(this);
686}
687
688
689Reference::~Reference() {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000690 ASSERT(is_unloaded() || is_illegal());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000691}
692
693
ager@chromium.org7c537e22008-10-16 08:43:32 +0000694void CodeGenerator::LoadReference(Reference* ref) {
695 Comment cmnt(masm_, "[ LoadReference");
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000696 Expression* e = ref->expression();
697 Property* property = e->AsProperty();
698 Variable* var = e->AsVariableProxy()->AsVariable();
699
700 if (property != NULL) {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000701 // The expression is either a property or a variable proxy that rewrites
702 // to a property.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +0000703 Load(property->obj());
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000704 if (property->key()->IsPropertyName()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000705 ref->set_type(Reference::NAMED);
706 } else {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +0000707 Load(property->key());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000708 ref->set_type(Reference::KEYED);
709 }
710 } else if (var != NULL) {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000711 // The expression is a variable proxy that does not rewrite to a
712 // property. Global variables are treated as named property references.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000713 if (var->is_global()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000714 LoadGlobal();
715 ref->set_type(Reference::NAMED);
716 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000717 ASSERT(var->slot() != NULL);
718 ref->set_type(Reference::SLOT);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000719 }
720 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000721 // Anything else is a runtime error.
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000722 Load(e);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000723 frame_->CallRuntime(Runtime::kThrowReferenceError, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000724 }
725}
726
727
ager@chromium.org7c537e22008-10-16 08:43:32 +0000728void CodeGenerator::UnloadReference(Reference* ref) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000729 int size = ref->size();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000730 ref->set_unloaded();
ager@chromium.org357bf652010-04-12 11:30:10 +0000731 if (size == 0) return;
732
733 // Pop a reference from the stack while preserving TOS.
734 VirtualFrame::RegisterAllocationScope scope(this);
735 Comment cmnt(masm_, "[ UnloadReference");
736 if (size > 0) {
737 Register tos = frame_->PopToRegister();
738 frame_->Drop(size);
739 frame_->EmitPush(tos);
740 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000741}
742
743
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000744// ECMA-262, section 9.2, page 30: ToBoolean(). Convert the given
745// register to a boolean in the condition code register. The code
746// may jump to 'false_target' in case the register converts to 'false'.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000747void CodeGenerator::ToBoolean(JumpTarget* true_target,
748 JumpTarget* false_target) {
mads.s.ager31e71382008-08-13 09:32:07 +0000749 // Note: The generated code snippet does not change stack variables.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000750 // Only the condition code should be set.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +0000751 Register tos = frame_->PopToRegister();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000752
753 // Fast case checks
754
mads.s.ager31e71382008-08-13 09:32:07 +0000755 // Check if the value is 'false'.
ager@chromium.orgab99eea2009-08-25 07:05:41 +0000756 __ LoadRoot(ip, Heap::kFalseValueRootIndex);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +0000757 __ cmp(tos, ip);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000758 false_target->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000759
mads.s.ager31e71382008-08-13 09:32:07 +0000760 // Check if the value is 'true'.
ager@chromium.orgab99eea2009-08-25 07:05:41 +0000761 __ LoadRoot(ip, Heap::kTrueValueRootIndex);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +0000762 __ cmp(tos, ip);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000763 true_target->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000764
mads.s.ager31e71382008-08-13 09:32:07 +0000765 // Check if the value is 'undefined'.
ager@chromium.orgab99eea2009-08-25 07:05:41 +0000766 __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +0000767 __ cmp(tos, ip);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000768 false_target->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000769
mads.s.ager31e71382008-08-13 09:32:07 +0000770 // Check if the value is a smi.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +0000771 __ cmp(tos, Operand(Smi::FromInt(0)));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000772 false_target->Branch(eq);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +0000773 __ tst(tos, Operand(kSmiTagMask));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000774 true_target->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000775
776 // Slow case: call the runtime.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +0000777 frame_->EmitPush(tos);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000778 frame_->CallRuntime(Runtime::kToBool, 1);
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000779 // Convert the result (r0) to a condition code.
ager@chromium.orgab99eea2009-08-25 07:05:41 +0000780 __ LoadRoot(ip, Heap::kFalseValueRootIndex);
781 __ cmp(r0, ip);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000782
783 cc_reg_ = ne;
784}
785
786
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000787void CodeGenerator::GenericBinaryOperation(Token::Value op,
kasperl@chromium.org86f77b72009-07-06 08:21:57 +0000788 OverwriteMode overwrite_mode,
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +0000789 GenerateInlineSmi inline_smi,
kasperl@chromium.org86f77b72009-07-06 08:21:57 +0000790 int constant_rhs) {
ager@chromium.org357bf652010-04-12 11:30:10 +0000791 // top of virtual frame: y
792 // 2nd elt. on virtual frame : x
793 // result : top of virtual frame
794
795 // Stub is entered with a call: 'return address' is in lr.
796 switch (op) {
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +0000797 case Token::ADD:
798 case Token::SUB:
799 if (inline_smi) {
800 JumpTarget done;
801 Register rhs = frame_->PopToRegister();
802 Register lhs = frame_->PopToRegister(rhs);
803 Register scratch = VirtualFrame::scratch0();
804 __ orr(scratch, rhs, Operand(lhs));
805 // Check they are both small and positive.
806 __ tst(scratch, Operand(kSmiTagMask | 0xc0000000));
807 ASSERT(rhs.is(r0) || lhs.is(r0)); // r0 is free now.
808 ASSERT_EQ(0, kSmiTag);
809 if (op == Token::ADD) {
810 __ add(r0, lhs, Operand(rhs), LeaveCC, eq);
811 } else {
812 __ sub(r0, lhs, Operand(rhs), LeaveCC, eq);
813 }
814 done.Branch(eq);
815 GenericBinaryOpStub stub(op, overwrite_mode, lhs, rhs, constant_rhs);
816 frame_->SpillAll();
817 frame_->CallStub(&stub, 0);
818 done.Bind();
819 frame_->EmitPush(r0);
820 break;
821 } else {
822 // Fall through!
823 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000824 case Token::BIT_OR:
825 case Token::BIT_AND:
826 case Token::BIT_XOR:
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +0000827 if (inline_smi) {
828 bool rhs_is_smi = frame_->KnownSmiAt(0);
829 bool lhs_is_smi = frame_->KnownSmiAt(1);
830 Register rhs = frame_->PopToRegister();
831 Register lhs = frame_->PopToRegister(rhs);
832 Register smi_test_reg;
833 Condition cond;
834 if (!rhs_is_smi || !lhs_is_smi) {
835 if (rhs_is_smi) {
836 smi_test_reg = lhs;
837 } else if (lhs_is_smi) {
838 smi_test_reg = rhs;
839 } else {
840 smi_test_reg = VirtualFrame::scratch0();
841 __ orr(smi_test_reg, rhs, Operand(lhs));
842 }
843 // Check they are both Smis.
844 __ tst(smi_test_reg, Operand(kSmiTagMask));
845 cond = eq;
846 } else {
847 cond = al;
848 }
849 ASSERT(rhs.is(r0) || lhs.is(r0)); // r0 is free now.
850 if (op == Token::BIT_OR) {
851 __ orr(r0, lhs, Operand(rhs), LeaveCC, cond);
852 } else if (op == Token::BIT_AND) {
853 __ and_(r0, lhs, Operand(rhs), LeaveCC, cond);
854 } else {
855 ASSERT(op == Token::BIT_XOR);
856 ASSERT_EQ(0, kSmiTag);
857 __ eor(r0, lhs, Operand(rhs), LeaveCC, cond);
858 }
859 if (cond != al) {
860 JumpTarget done;
861 done.Branch(cond);
862 GenericBinaryOpStub stub(op, overwrite_mode, lhs, rhs, constant_rhs);
863 frame_->SpillAll();
864 frame_->CallStub(&stub, 0);
865 done.Bind();
866 }
867 frame_->EmitPush(r0);
868 break;
869 } else {
870 // Fall through!
871 }
872 case Token::MUL:
873 case Token::DIV:
874 case Token::MOD:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000875 case Token::SHL:
876 case Token::SHR:
877 case Token::SAR: {
ager@chromium.org357bf652010-04-12 11:30:10 +0000878 Register rhs = frame_->PopToRegister();
879 Register lhs = frame_->PopToRegister(rhs); // Don't pop to rhs register.
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000880 GenericBinaryOpStub stub(op, overwrite_mode, lhs, rhs, constant_rhs);
881 frame_->SpillAll();
882 frame_->CallStub(&stub, 0);
ager@chromium.org357bf652010-04-12 11:30:10 +0000883 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000884 break;
885 }
886
ager@chromium.org357bf652010-04-12 11:30:10 +0000887 case Token::COMMA: {
888 Register scratch = frame_->PopToRegister();
889 // Simply discard left value.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000890 frame_->Drop();
ager@chromium.org357bf652010-04-12 11:30:10 +0000891 frame_->EmitPush(scratch);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000892 break;
ager@chromium.org357bf652010-04-12 11:30:10 +0000893 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000894
895 default:
896 // Other cases should have been handled before this point.
897 UNREACHABLE();
898 break;
899 }
900}
901
902
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000903class DeferredInlineSmiOperation: public DeferredCode {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000904 public:
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +0000905 DeferredInlineSmiOperation(Token::Value op,
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000906 int value,
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000907 bool reversed,
ager@chromium.org357bf652010-04-12 11:30:10 +0000908 OverwriteMode overwrite_mode,
909 Register tos)
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +0000910 : op_(op),
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000911 value_(value),
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000912 reversed_(reversed),
ager@chromium.org357bf652010-04-12 11:30:10 +0000913 overwrite_mode_(overwrite_mode),
914 tos_register_(tos) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000915 set_comment("[ DeferredInlinedSmiOperation");
916 }
917
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000918 virtual void Generate();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000919
920 private:
921 Token::Value op_;
922 int value_;
923 bool reversed_;
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000924 OverwriteMode overwrite_mode_;
ager@chromium.org357bf652010-04-12 11:30:10 +0000925 Register tos_register_;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000926};
927
928
fschneider@chromium.org40b9da32010-06-28 11:29:21 +0000929
930// On entry the non-constant side of the binary operation is in tos_register_
931// and the constant smi side is nowhere. The tos_register_ is not used by the
932// virtual frame. On exit the answer is in the tos_register_ and the virtual
933// frame is unchanged.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000934void DeferredInlineSmiOperation::Generate() {
fschneider@chromium.org40b9da32010-06-28 11:29:21 +0000935 VirtualFrame copied_frame(*frame_state()->frame());
936 copied_frame.SpillAll();
937
ager@chromium.org357bf652010-04-12 11:30:10 +0000938 Register lhs = r1;
939 Register rhs = r0;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000940 switch (op_) {
941 case Token::ADD: {
ager@chromium.orge2902be2009-06-08 12:21:35 +0000942 // Revert optimistic add.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000943 if (reversed_) {
ager@chromium.org357bf652010-04-12 11:30:10 +0000944 __ sub(r0, tos_register_, Operand(Smi::FromInt(value_)));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000945 __ mov(r1, Operand(Smi::FromInt(value_)));
946 } else {
ager@chromium.org357bf652010-04-12 11:30:10 +0000947 __ sub(r1, tos_register_, Operand(Smi::FromInt(value_)));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000948 __ mov(r0, Operand(Smi::FromInt(value_)));
949 }
950 break;
951 }
952
953 case Token::SUB: {
ager@chromium.orge2902be2009-06-08 12:21:35 +0000954 // Revert optimistic sub.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000955 if (reversed_) {
ager@chromium.org357bf652010-04-12 11:30:10 +0000956 __ rsb(r0, tos_register_, Operand(Smi::FromInt(value_)));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000957 __ mov(r1, Operand(Smi::FromInt(value_)));
958 } else {
ager@chromium.org357bf652010-04-12 11:30:10 +0000959 __ add(r1, tos_register_, Operand(Smi::FromInt(value_)));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000960 __ mov(r0, Operand(Smi::FromInt(value_)));
961 }
962 break;
963 }
964
kasperl@chromium.org86f77b72009-07-06 08:21:57 +0000965 // For these operations there is no optimistic operation that needs to be
966 // reverted.
967 case Token::MUL:
968 case Token::MOD:
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000969 case Token::BIT_OR:
970 case Token::BIT_XOR:
fschneider@chromium.org40b9da32010-06-28 11:29:21 +0000971 case Token::BIT_AND:
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000972 case Token::SHL:
973 case Token::SHR:
974 case Token::SAR: {
fschneider@chromium.org40b9da32010-06-28 11:29:21 +0000975 if (tos_register_.is(r1)) {
976 __ mov(r0, Operand(Smi::FromInt(value_)));
977 } else {
978 ASSERT(tos_register_.is(r0));
979 __ mov(r1, Operand(Smi::FromInt(value_)));
980 }
981 if (reversed_ == tos_register_.is(r1)) {
ager@chromium.org357bf652010-04-12 11:30:10 +0000982 lhs = r0;
983 rhs = r1;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000984 }
985 break;
986 }
987
988 default:
ager@chromium.orge2902be2009-06-08 12:21:35 +0000989 // Other cases should have been handled before this point.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000990 UNREACHABLE();
991 break;
992 }
993
ager@chromium.org357bf652010-04-12 11:30:10 +0000994 GenericBinaryOpStub stub(op_, overwrite_mode_, lhs, rhs, value_);
ager@chromium.orge2902be2009-06-08 12:21:35 +0000995 __ CallStub(&stub);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +0000996
ager@chromium.org357bf652010-04-12 11:30:10 +0000997 // The generic stub returns its value in r0, but that's not
998 // necessarily what we want. We want whatever the inlined code
999 // expected, which is that the answer is in the same register as
1000 // the operand was.
1001 __ Move(tos_register_, r0);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00001002
1003 // The tos register was not in use for the virtual frame that we
1004 // came into this function with, so we can merge back to that frame
1005 // without trashing it.
1006 copied_frame.MergeTo(frame_state()->frame());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001007}
1008
1009
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00001010static bool PopCountLessThanEqual2(unsigned int x) {
1011 x &= x - 1;
1012 return (x & (x - 1)) == 0;
1013}
1014
1015
1016// Returns the index of the lowest bit set.
1017static int BitPosition(unsigned x) {
1018 int bit_posn = 0;
1019 while ((x & 0xf) == 0) {
1020 bit_posn += 4;
1021 x >>= 4;
1022 }
1023 while ((x & 1) == 0) {
1024 bit_posn++;
1025 x >>= 1;
1026 }
1027 return bit_posn;
1028}
1029
1030
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00001031void CodeGenerator::SmiOperation(Token::Value op,
1032 Handle<Object> value,
1033 bool reversed,
1034 OverwriteMode mode) {
ager@chromium.org357bf652010-04-12 11:30:10 +00001035 int int_value = Smi::cast(*value)->value();
1036
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001037 bool both_sides_are_smi = frame_->KnownSmiAt(0);
1038
ager@chromium.org357bf652010-04-12 11:30:10 +00001039 bool something_to_inline;
1040 switch (op) {
1041 case Token::ADD:
1042 case Token::SUB:
1043 case Token::BIT_AND:
1044 case Token::BIT_OR:
1045 case Token::BIT_XOR: {
1046 something_to_inline = true;
1047 break;
1048 }
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001049 case Token::SHL: {
1050 something_to_inline = (both_sides_are_smi || !reversed);
1051 break;
1052 }
ager@chromium.org357bf652010-04-12 11:30:10 +00001053 case Token::SHR:
1054 case Token::SAR: {
1055 if (reversed) {
1056 something_to_inline = false;
1057 } else {
1058 something_to_inline = true;
1059 }
1060 break;
1061 }
1062 case Token::MOD: {
1063 if (reversed || int_value < 2 || !IsPowerOf2(int_value)) {
1064 something_to_inline = false;
1065 } else {
1066 something_to_inline = true;
1067 }
1068 break;
1069 }
1070 case Token::MUL: {
1071 if (!IsEasyToMultiplyBy(int_value)) {
1072 something_to_inline = false;
1073 } else {
1074 something_to_inline = true;
1075 }
1076 break;
1077 }
1078 default: {
1079 something_to_inline = false;
1080 break;
1081 }
1082 }
1083
1084 if (!something_to_inline) {
1085 if (!reversed) {
1086 // Push the rhs onto the virtual frame by putting it in a TOS register.
1087 Register rhs = frame_->GetTOSRegister();
1088 __ mov(rhs, Operand(value));
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001089 frame_->EmitPush(rhs, TypeInfo::Smi());
1090 GenericBinaryOperation(op, mode, GENERATE_INLINE_SMI, int_value);
ager@chromium.org357bf652010-04-12 11:30:10 +00001091 } else {
1092 // Pop the rhs, then push lhs and rhs in the right order. Only performs
1093 // at most one pop, the rest takes place in TOS registers.
1094 Register lhs = frame_->GetTOSRegister(); // Get reg for pushing.
1095 Register rhs = frame_->PopToRegister(lhs); // Don't use lhs for this.
1096 __ mov(lhs, Operand(value));
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001097 frame_->EmitPush(lhs, TypeInfo::Smi());
1098 TypeInfo t = both_sides_are_smi ? TypeInfo::Smi() : TypeInfo::Unknown();
1099 frame_->EmitPush(rhs, t);
1100 GenericBinaryOperation(op, mode, GENERATE_INLINE_SMI, kUnknownIntValue);
ager@chromium.org357bf652010-04-12 11:30:10 +00001101 }
1102 return;
1103 }
1104
1105 // We move the top of stack to a register (normally no move is invoved).
1106 Register tos = frame_->PopToRegister();
ager@chromium.org357bf652010-04-12 11:30:10 +00001107 switch (op) {
1108 case Token::ADD: {
1109 DeferredCode* deferred =
1110 new DeferredInlineSmiOperation(op, int_value, reversed, mode, tos);
1111
1112 __ add(tos, tos, Operand(value), SetCC);
1113 deferred->Branch(vs);
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001114 if (!both_sides_are_smi) {
1115 __ tst(tos, Operand(kSmiTagMask));
1116 deferred->Branch(ne);
1117 }
ager@chromium.org357bf652010-04-12 11:30:10 +00001118 deferred->BindExit();
1119 frame_->EmitPush(tos);
1120 break;
1121 }
1122
1123 case Token::SUB: {
1124 DeferredCode* deferred =
1125 new DeferredInlineSmiOperation(op, int_value, reversed, mode, tos);
1126
1127 if (reversed) {
1128 __ rsb(tos, tos, Operand(value), SetCC);
1129 } else {
1130 __ sub(tos, tos, Operand(value), SetCC);
1131 }
1132 deferred->Branch(vs);
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001133 if (!both_sides_are_smi) {
1134 __ tst(tos, Operand(kSmiTagMask));
1135 deferred->Branch(ne);
1136 }
ager@chromium.org357bf652010-04-12 11:30:10 +00001137 deferred->BindExit();
1138 frame_->EmitPush(tos);
1139 break;
1140 }
1141
1142
1143 case Token::BIT_OR:
1144 case Token::BIT_XOR:
1145 case Token::BIT_AND: {
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001146 if (both_sides_are_smi) {
1147 switch (op) {
1148 case Token::BIT_OR: __ orr(tos, tos, Operand(value)); break;
1149 case Token::BIT_XOR: __ eor(tos, tos, Operand(value)); break;
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00001150 case Token::BIT_AND: __ And(tos, tos, Operand(value)); break;
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001151 default: UNREACHABLE();
1152 }
1153 frame_->EmitPush(tos, TypeInfo::Smi());
1154 } else {
1155 DeferredCode* deferred =
1156 new DeferredInlineSmiOperation(op, int_value, reversed, mode, tos);
1157 __ tst(tos, Operand(kSmiTagMask));
1158 deferred->Branch(ne);
1159 switch (op) {
1160 case Token::BIT_OR: __ orr(tos, tos, Operand(value)); break;
1161 case Token::BIT_XOR: __ eor(tos, tos, Operand(value)); break;
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00001162 case Token::BIT_AND: __ And(tos, tos, Operand(value)); break;
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001163 default: UNREACHABLE();
1164 }
1165 deferred->BindExit();
1166 TypeInfo result_type =
1167 (op == Token::BIT_AND) ? TypeInfo::Smi() : TypeInfo::Integer32();
1168 frame_->EmitPush(tos, result_type);
ager@chromium.org357bf652010-04-12 11:30:10 +00001169 }
ager@chromium.org357bf652010-04-12 11:30:10 +00001170 break;
1171 }
1172
1173 case Token::SHL:
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001174 if (reversed) {
1175 ASSERT(both_sides_are_smi);
1176 int max_shift = 0;
1177 int max_result = int_value == 0 ? 1 : int_value;
1178 while (Smi::IsValid(max_result << 1)) {
1179 max_shift++;
1180 max_result <<= 1;
1181 }
1182 DeferredCode* deferred =
1183 new DeferredInlineSmiOperation(op, int_value, true, mode, tos);
1184 // Mask off the last 5 bits of the shift operand (rhs). This is part
1185 // of the definition of shift in JS and we know we have a Smi so we
1186 // can safely do this. The masked version gets passed to the
1187 // deferred code, but that makes no difference.
1188 __ and_(tos, tos, Operand(Smi::FromInt(0x1f)));
1189 __ cmp(tos, Operand(Smi::FromInt(max_shift)));
1190 deferred->Branch(ge);
1191 Register scratch = VirtualFrame::scratch0();
1192 __ mov(scratch, Operand(tos, ASR, kSmiTagSize)); // Untag.
1193 __ mov(tos, Operand(Smi::FromInt(int_value))); // Load constant.
1194 __ mov(tos, Operand(tos, LSL, scratch)); // Shift constant.
1195 deferred->BindExit();
1196 TypeInfo result = TypeInfo::Integer32();
1197 frame_->EmitPush(tos, result);
1198 break;
1199 }
1200 // Fall through!
ager@chromium.org357bf652010-04-12 11:30:10 +00001201 case Token::SHR:
1202 case Token::SAR: {
1203 ASSERT(!reversed);
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001204 TypeInfo result = TypeInfo::Integer32();
ager@chromium.org357bf652010-04-12 11:30:10 +00001205 Register scratch = VirtualFrame::scratch0();
1206 Register scratch2 = VirtualFrame::scratch1();
1207 int shift_value = int_value & 0x1f; // least significant 5 bits
1208 DeferredCode* deferred =
1209 new DeferredInlineSmiOperation(op, shift_value, false, mode, tos);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00001210 uint32_t problematic_mask = kSmiTagMask;
1211 // For unsigned shift by zero all negative smis are problematic.
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001212 bool skip_smi_test = both_sides_are_smi;
1213 if (shift_value == 0 && op == Token::SHR) {
1214 problematic_mask |= 0x80000000;
1215 skip_smi_test = false;
1216 }
1217 if (!skip_smi_test) {
1218 __ tst(tos, Operand(problematic_mask));
1219 deferred->Branch(ne); // Go slow for problematic input.
1220 }
ager@chromium.org357bf652010-04-12 11:30:10 +00001221 switch (op) {
1222 case Token::SHL: {
1223 if (shift_value != 0) {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00001224 int adjusted_shift = shift_value - kSmiTagSize;
1225 ASSERT(adjusted_shift >= 0);
1226 if (adjusted_shift != 0) {
1227 __ mov(scratch, Operand(tos, LSL, adjusted_shift));
1228 // Check that the *signed* result fits in a smi.
1229 __ add(scratch2, scratch, Operand(0x40000000), SetCC);
1230 deferred->Branch(mi);
1231 __ mov(tos, Operand(scratch, LSL, kSmiTagSize));
1232 } else {
1233 // Check that the *signed* result fits in a smi.
1234 __ add(scratch2, tos, Operand(0x40000000), SetCC);
1235 deferred->Branch(mi);
1236 __ mov(tos, Operand(tos, LSL, kSmiTagSize));
1237 }
ager@chromium.org357bf652010-04-12 11:30:10 +00001238 }
ager@chromium.org357bf652010-04-12 11:30:10 +00001239 break;
1240 }
1241 case Token::SHR: {
ager@chromium.org357bf652010-04-12 11:30:10 +00001242 if (shift_value != 0) {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00001243 __ mov(scratch, Operand(tos, ASR, kSmiTagSize)); // Remove tag.
1244 // LSR by immediate 0 means shifting 32 bits.
ager@chromium.org357bf652010-04-12 11:30:10 +00001245 __ mov(scratch, Operand(scratch, LSR, shift_value));
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00001246 if (shift_value == 1) {
1247 // check that the *unsigned* result fits in a smi
1248 // neither of the two high-order bits can be set:
1249 // - 0x80000000: high bit would be lost when smi tagging
1250 // - 0x40000000: this number would convert to negative when
1251 // smi tagging these two cases can only happen with shifts
1252 // by 0 or 1 when handed a valid smi
1253 __ tst(scratch, Operand(0xc0000000));
1254 deferred->Branch(ne);
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001255 } else {
1256 ASSERT(shift_value >= 2);
1257 result = TypeInfo::Smi(); // SHR by at least 2 gives a Smi.
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00001258 }
1259 __ mov(tos, Operand(scratch, LSL, kSmiTagSize));
ager@chromium.org357bf652010-04-12 11:30:10 +00001260 }
ager@chromium.org357bf652010-04-12 11:30:10 +00001261 break;
1262 }
1263 case Token::SAR: {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00001264 // In the ARM instructions set, ASR by immediate 0 means shifting 32
1265 // bits.
ager@chromium.org357bf652010-04-12 11:30:10 +00001266 if (shift_value != 0) {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00001267 // Do the shift and the tag removal in one operation. If the shift
1268 // is 31 bits (the highest possible value) then we emit the
1269 // instruction as a shift by 0 which means shift arithmetically by
1270 // 32.
1271 __ mov(tos, Operand(tos, ASR, (kSmiTagSize + shift_value) & 0x1f));
1272 // Put tag back.
1273 __ mov(tos, Operand(tos, LSL, kSmiTagSize));
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001274 // SAR by at least 1 gives a Smi.
1275 result = TypeInfo::Smi();
ager@chromium.org357bf652010-04-12 11:30:10 +00001276 }
1277 break;
1278 }
1279 default: UNREACHABLE();
1280 }
ager@chromium.org357bf652010-04-12 11:30:10 +00001281 deferred->BindExit();
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001282 frame_->EmitPush(tos, result);
ager@chromium.org357bf652010-04-12 11:30:10 +00001283 break;
1284 }
1285
1286 case Token::MOD: {
1287 ASSERT(!reversed);
1288 ASSERT(int_value >= 2);
1289 ASSERT(IsPowerOf2(int_value));
1290 DeferredCode* deferred =
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001291 new DeferredInlineSmiOperation(op, int_value, reversed, mode, tos);
ager@chromium.org357bf652010-04-12 11:30:10 +00001292 unsigned mask = (0x80000000u | kSmiTagMask);
1293 __ tst(tos, Operand(mask));
1294 deferred->Branch(ne); // Go to deferred code on non-Smis and negative.
1295 mask = (int_value << kSmiTagSize) - 1;
1296 __ and_(tos, tos, Operand(mask));
1297 deferred->BindExit();
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001298 // Mod of positive power of 2 Smi gives a Smi if the lhs is an integer.
1299 frame_->EmitPush(
1300 tos,
1301 both_sides_are_smi ? TypeInfo::Smi() : TypeInfo::Number());
ager@chromium.org357bf652010-04-12 11:30:10 +00001302 break;
1303 }
1304
1305 case Token::MUL: {
1306 ASSERT(IsEasyToMultiplyBy(int_value));
1307 DeferredCode* deferred =
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001308 new DeferredInlineSmiOperation(op, int_value, reversed, mode, tos);
ager@chromium.org357bf652010-04-12 11:30:10 +00001309 unsigned max_smi_that_wont_overflow = Smi::kMaxValue / int_value;
1310 max_smi_that_wont_overflow <<= kSmiTagSize;
1311 unsigned mask = 0x80000000u;
1312 while ((mask & max_smi_that_wont_overflow) == 0) {
1313 mask |= mask >> 1;
1314 }
1315 mask |= kSmiTagMask;
1316 // This does a single mask that checks for a too high value in a
1317 // conservative way and for a non-Smi. It also filters out negative
1318 // numbers, unfortunately, but since this code is inline we prefer
1319 // brevity to comprehensiveness.
1320 __ tst(tos, Operand(mask));
1321 deferred->Branch(ne);
1322 MultiplyByKnownInt(masm_, tos, tos, int_value);
1323 deferred->BindExit();
1324 frame_->EmitPush(tos);
1325 break;
1326 }
1327
1328 default:
1329 UNREACHABLE();
1330 break;
1331 }
1332}
1333
1334
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001335void CodeGenerator::Comparison(Condition cc,
1336 Expression* left,
1337 Expression* right,
1338 bool strict) {
ager@chromium.org357bf652010-04-12 11:30:10 +00001339 VirtualFrame::RegisterAllocationScope scope(this);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001340
ager@chromium.org357bf652010-04-12 11:30:10 +00001341 if (left != NULL) Load(left);
1342 if (right != NULL) Load(right);
1343
mads.s.ager31e71382008-08-13 09:32:07 +00001344 // sp[0] : y
1345 // sp[1] : x
1346 // result : cc register
1347
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001348 // Strict only makes sense for equality comparisons.
1349 ASSERT(!strict || cc == eq);
1350
ager@chromium.org357bf652010-04-12 11:30:10 +00001351 Register lhs;
1352 Register rhs;
1353
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001354 bool lhs_is_smi;
1355 bool rhs_is_smi;
1356
ager@chromium.org357bf652010-04-12 11:30:10 +00001357 // We load the top two stack positions into registers chosen by the virtual
1358 // frame. This should keep the register shuffling to a minimum.
mads.s.ager@gmail.com769cc962008-08-06 10:02:49 +00001359 // Implement '>' and '<=' by reversal to obtain ECMA-262 conversion order.
1360 if (cc == gt || cc == le) {
1361 cc = ReverseCondition(cc);
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001362 lhs_is_smi = frame_->KnownSmiAt(0);
1363 rhs_is_smi = frame_->KnownSmiAt(1);
ager@chromium.org357bf652010-04-12 11:30:10 +00001364 lhs = frame_->PopToRegister();
1365 rhs = frame_->PopToRegister(lhs); // Don't pop to the same register again!
mads.s.ager@gmail.com769cc962008-08-06 10:02:49 +00001366 } else {
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001367 rhs_is_smi = frame_->KnownSmiAt(0);
1368 lhs_is_smi = frame_->KnownSmiAt(1);
ager@chromium.org357bf652010-04-12 11:30:10 +00001369 rhs = frame_->PopToRegister();
1370 lhs = frame_->PopToRegister(rhs); // Don't pop to the same register again!
mads.s.ager@gmail.com769cc962008-08-06 10:02:49 +00001371 }
ager@chromium.org357bf652010-04-12 11:30:10 +00001372
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001373 bool both_sides_are_smi = (lhs_is_smi && rhs_is_smi);
1374
ager@chromium.org357bf652010-04-12 11:30:10 +00001375 ASSERT(rhs.is(r0) || rhs.is(r1));
1376 ASSERT(lhs.is(r0) || lhs.is(r1));
1377
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001378 JumpTarget exit;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001379
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001380 if (!both_sides_are_smi) {
1381 // Now we have the two sides in r0 and r1. We flush any other registers
1382 // because the stub doesn't know about register allocation.
1383 frame_->SpillAll();
1384 Register scratch = VirtualFrame::scratch0();
1385 Register smi_test_reg;
1386 if (lhs_is_smi) {
1387 smi_test_reg = rhs;
1388 } else if (rhs_is_smi) {
1389 smi_test_reg = lhs;
1390 } else {
1391 __ orr(scratch, lhs, Operand(rhs));
1392 smi_test_reg = scratch;
1393 }
1394 __ tst(smi_test_reg, Operand(kSmiTagMask));
1395 JumpTarget smi;
1396 smi.Branch(eq);
1397
1398 // Perform non-smi comparison by stub.
1399 // CompareStub takes arguments in r0 and r1, returns <0, >0 or 0 in r0.
1400 // We call with 0 args because there are 0 on the stack.
1401 if (!rhs.is(r0)) {
1402 __ Swap(rhs, lhs, ip);
1403 }
1404
1405 CompareStub stub(cc, strict);
1406 frame_->CallStub(&stub, 0);
1407 __ cmp(r0, Operand(0));
1408 exit.Jump();
1409
1410 smi.Bind();
ager@chromium.org357bf652010-04-12 11:30:10 +00001411 }
1412
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001413 // Do smi comparisons by pointer comparison.
ager@chromium.org357bf652010-04-12 11:30:10 +00001414 __ cmp(lhs, Operand(rhs));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001415
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001416 exit.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001417 cc_reg_ = cc;
1418}
1419
1420
mads.s.ager31e71382008-08-13 09:32:07 +00001421// Call the function on the stack with the given arguments.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001422void CodeGenerator::CallWithArguments(ZoneList<Expression*>* args,
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001423 CallFunctionFlags flags,
1424 int position) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001425 // Push the arguments ("left-to-right") on the stack.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001426 int arg_count = args->length();
1427 for (int i = 0; i < arg_count; i++) {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001428 Load(args->at(i));
mads.s.ager31e71382008-08-13 09:32:07 +00001429 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001430
kasper.lund7276f142008-07-30 08:49:36 +00001431 // Record the position for debugging purposes.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001432 CodeForSourcePosition(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001433
kasper.lund7276f142008-07-30 08:49:36 +00001434 // Use the shared code stub to call the function.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001435 InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001436 CallFunctionStub call_function(arg_count, in_loop, flags);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001437 frame_->CallStub(&call_function, arg_count + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001438
1439 // Restore context and pop function from the stack.
ager@chromium.org3bf7b912008-11-17 09:09:45 +00001440 __ ldr(cp, frame_->Context());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001441 frame_->Drop(); // discard the TOS
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001442}
1443
1444
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00001445void CodeGenerator::CallApplyLazy(Expression* applicand,
1446 Expression* receiver,
1447 VariableProxy* arguments,
1448 int position) {
1449 // An optimized implementation of expressions of the form
1450 // x.apply(y, arguments).
1451 // If the arguments object of the scope has not been allocated,
1452 // and x.apply is Function.prototype.apply, this optimization
1453 // just copies y and the arguments of the current function on the
1454 // stack, as receiver and arguments, and calls x.
1455 // In the implementation comments, we call x the applicand
1456 // and y the receiver.
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00001457
1458 ASSERT(ArgumentsMode() == LAZY_ARGUMENTS_ALLOCATION);
1459 ASSERT(arguments->IsArguments());
1460
1461 // Load applicand.apply onto the stack. This will usually
1462 // give us a megamorphic load site. Not super, but it works.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001463 Load(applicand);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00001464 Handle<String> name = Factory::LookupAsciiSymbol("apply");
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001465 frame_->Dup();
ager@chromium.orgac091b72010-05-05 07:34:42 +00001466 frame_->CallLoadIC(name, RelocInfo::CODE_TARGET);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00001467 frame_->EmitPush(r0);
1468
1469 // Load the receiver and the existing arguments object onto the
1470 // expression stack. Avoid allocating the arguments object here.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001471 Load(receiver);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00001472 LoadFromSlot(scope()->arguments()->var()->slot(), NOT_INSIDE_TYPEOF);
1473
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00001474 // At this point the top two stack elements are probably in registers
1475 // since they were just loaded. Ensure they are in regs and get the
1476 // regs.
1477 Register receiver_reg = frame_->Peek2();
1478 Register arguments_reg = frame_->Peek();
1479
1480 // From now on the frame is spilled.
1481 frame_->SpillAll();
1482
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00001483 // Emit the source position information after having loaded the
1484 // receiver and the arguments.
1485 CodeForSourcePosition(position);
1486 // Contents of the stack at this point:
1487 // sp[0]: arguments object of the current function or the hole.
1488 // sp[1]: receiver
1489 // sp[2]: applicand.apply
1490 // sp[3]: applicand.
1491
1492 // Check if the arguments object has been lazily allocated
1493 // already. If so, just use that instead of copying the arguments
1494 // from the stack. This also deals with cases where a local variable
1495 // named 'arguments' has been introduced.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00001496 JumpTarget slow;
1497 Label done;
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00001498 __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00001499 __ cmp(ip, arguments_reg);
1500 slow.Branch(ne);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00001501
1502 Label build_args;
1503 // Get rid of the arguments object probe.
1504 frame_->Drop();
1505 // Stack now has 3 elements on it.
1506 // Contents of stack at this point:
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00001507 // sp[0]: receiver - in the receiver_reg register.
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00001508 // sp[1]: applicand.apply
1509 // sp[2]: applicand.
1510
1511 // Check that the receiver really is a JavaScript object.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00001512 __ BranchOnSmi(receiver_reg, &build_args);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00001513 // We allow all JSObjects including JSFunctions. As long as
1514 // JS_FUNCTION_TYPE is the last instance type and it is right
1515 // after LAST_JS_OBJECT_TYPE, we do not have to check the upper
1516 // bound.
1517 ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
1518 ASSERT(JS_FUNCTION_TYPE == LAST_JS_OBJECT_TYPE + 1);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00001519 __ CompareObjectType(receiver_reg, r2, r3, FIRST_JS_OBJECT_TYPE);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00001520 __ b(lt, &build_args);
1521
1522 // Check that applicand.apply is Function.prototype.apply.
1523 __ ldr(r0, MemOperand(sp, kPointerSize));
1524 __ BranchOnSmi(r0, &build_args);
1525 __ CompareObjectType(r0, r1, r2, JS_FUNCTION_TYPE);
1526 __ b(ne, &build_args);
1527 __ ldr(r0, FieldMemOperand(r0, JSFunction::kSharedFunctionInfoOffset));
1528 Handle<Code> apply_code(Builtins::builtin(Builtins::FunctionApply));
1529 __ ldr(r1, FieldMemOperand(r0, SharedFunctionInfo::kCodeOffset));
1530 __ cmp(r1, Operand(apply_code));
1531 __ b(ne, &build_args);
1532
1533 // Check that applicand is a function.
1534 __ ldr(r1, MemOperand(sp, 2 * kPointerSize));
1535 __ BranchOnSmi(r1, &build_args);
1536 __ CompareObjectType(r1, r2, r3, JS_FUNCTION_TYPE);
1537 __ b(ne, &build_args);
1538
1539 // Copy the arguments to this function possibly from the
1540 // adaptor frame below it.
1541 Label invoke, adapted;
1542 __ ldr(r2, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
1543 __ ldr(r3, MemOperand(r2, StandardFrameConstants::kContextOffset));
1544 __ cmp(r3, Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
1545 __ b(eq, &adapted);
1546
1547 // No arguments adaptor frame. Copy fixed number of arguments.
1548 __ mov(r0, Operand(scope()->num_parameters()));
1549 for (int i = 0; i < scope()->num_parameters(); i++) {
1550 __ ldr(r2, frame_->ParameterAt(i));
1551 __ push(r2);
1552 }
1553 __ jmp(&invoke);
1554
1555 // Arguments adaptor frame present. Copy arguments from there, but
1556 // avoid copying too many arguments to avoid stack overflows.
1557 __ bind(&adapted);
1558 static const uint32_t kArgumentsLimit = 1 * KB;
1559 __ ldr(r0, MemOperand(r2, ArgumentsAdaptorFrameConstants::kLengthOffset));
1560 __ mov(r0, Operand(r0, LSR, kSmiTagSize));
1561 __ mov(r3, r0);
1562 __ cmp(r0, Operand(kArgumentsLimit));
1563 __ b(gt, &build_args);
1564
1565 // Loop through the arguments pushing them onto the execution
1566 // stack. We don't inform the virtual frame of the push, so we don't
1567 // have to worry about getting rid of the elements from the virtual
1568 // frame.
1569 Label loop;
1570 // r3 is a small non-negative integer, due to the test above.
1571 __ cmp(r3, Operand(0));
1572 __ b(eq, &invoke);
1573 // Compute the address of the first argument.
1574 __ add(r2, r2, Operand(r3, LSL, kPointerSizeLog2));
1575 __ add(r2, r2, Operand(kPointerSize));
1576 __ bind(&loop);
1577 // Post-decrement argument address by kPointerSize on each iteration.
1578 __ ldr(r4, MemOperand(r2, kPointerSize, NegPostIndex));
1579 __ push(r4);
1580 __ sub(r3, r3, Operand(1), SetCC);
1581 __ b(gt, &loop);
1582
1583 // Invoke the function.
1584 __ bind(&invoke);
1585 ParameterCount actual(r0);
1586 __ InvokeFunction(r1, actual, CALL_FUNCTION);
1587 // Drop applicand.apply and applicand from the stack, and push
1588 // the result of the function call, but leave the spilled frame
1589 // unchanged, with 3 elements, so it is correct when we compile the
1590 // slow-case code.
1591 __ add(sp, sp, Operand(2 * kPointerSize));
1592 __ push(r0);
1593 // Stack now has 1 element:
1594 // sp[0]: result
1595 __ jmp(&done);
1596
1597 // Slow-case: Allocate the arguments object since we know it isn't
1598 // there, and fall-through to the slow-case where we call
1599 // applicand.apply.
1600 __ bind(&build_args);
1601 // Stack now has 3 elements, because we have jumped from where:
1602 // sp[0]: receiver
1603 // sp[1]: applicand.apply
1604 // sp[2]: applicand.
1605 StoreArgumentsObject(false);
1606
1607 // Stack and frame now have 4 elements.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00001608 slow.Bind();
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00001609
1610 // Generic computation of x.apply(y, args) with no special optimization.
1611 // Flip applicand.apply and applicand on the stack, so
1612 // applicand looks like the receiver of the applicand.apply call.
1613 // Then process it as a normal function call.
1614 __ ldr(r0, MemOperand(sp, 3 * kPointerSize));
1615 __ ldr(r1, MemOperand(sp, 2 * kPointerSize));
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00001616 __ Strd(r0, r1, MemOperand(sp, 2 * kPointerSize));
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00001617
1618 CallFunctionStub call_function(2, NOT_IN_LOOP, NO_CALL_FUNCTION_FLAGS);
1619 frame_->CallStub(&call_function, 3);
1620 // The function and its two arguments have been dropped.
1621 frame_->Drop(); // Drop the receiver as well.
1622 frame_->EmitPush(r0);
1623 // Stack now has 1 element:
1624 // sp[0]: result
1625 __ bind(&done);
1626
1627 // Restore the context register after a call.
1628 __ ldr(cp, frame_->Context());
1629}
1630
1631
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001632void CodeGenerator::Branch(bool if_true, JumpTarget* target) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001633 ASSERT(has_cc());
1634 Condition cc = if_true ? cc_reg_ : NegateCondition(cc_reg_);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001635 target->Branch(cc);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001636 cc_reg_ = al;
1637}
1638
1639
ager@chromium.org7c537e22008-10-16 08:43:32 +00001640void CodeGenerator::CheckStack() {
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00001641 frame_->SpillAll();
ager@chromium.org3811b432009-10-28 14:53:37 +00001642 Comment cmnt(masm_, "[ check stack");
1643 __ LoadRoot(ip, Heap::kStackLimitRootIndex);
1644 // Put the lr setup instruction in the delay slot. kInstrSize is added to
1645 // the implicit 8 byte offset that always applies to operations with pc and
1646 // gives a return address 12 bytes down.
1647 masm_->add(lr, pc, Operand(Assembler::kInstrSize));
1648 masm_->cmp(sp, Operand(ip));
1649 StackCheckStub stub;
1650 // Call the stub if lower.
1651 masm_->mov(pc,
1652 Operand(reinterpret_cast<intptr_t>(stub.GetCode().location()),
1653 RelocInfo::CODE_TARGET),
1654 LeaveCC,
1655 lo);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001656}
1657
1658
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001659void CodeGenerator::VisitStatements(ZoneList<Statement*>* statements) {
1660#ifdef DEBUG
1661 int original_height = frame_->height();
1662#endif
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001663 for (int i = 0; frame_ != NULL && i < statements->length(); i++) {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001664 Visit(statements->at(i));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001665 }
1666 ASSERT(!has_valid_frame() || frame_->height() == original_height);
1667}
1668
1669
ager@chromium.org7c537e22008-10-16 08:43:32 +00001670void CodeGenerator::VisitBlock(Block* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001671#ifdef DEBUG
1672 int original_height = frame_->height();
1673#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001674 Comment cmnt(masm_, "[ Block");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001675 CodeForStatementPosition(node);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00001676 node->break_target()->SetExpectedHeight();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001677 VisitStatements(node->statements());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001678 if (node->break_target()->is_linked()) {
1679 node->break_target()->Bind();
1680 }
1681 node->break_target()->Unuse();
1682 ASSERT(!has_valid_frame() || frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001683}
1684
1685
ager@chromium.org7c537e22008-10-16 08:43:32 +00001686void CodeGenerator::DeclareGlobals(Handle<FixedArray> pairs) {
ager@chromium.org3811b432009-10-28 14:53:37 +00001687 frame_->EmitPush(cp);
ager@chromium.orgac091b72010-05-05 07:34:42 +00001688 frame_->EmitPush(Operand(pairs));
1689 frame_->EmitPush(Operand(Smi::FromInt(is_eval() ? 1 : 0)));
1690
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001691 frame_->CallRuntime(Runtime::kDeclareGlobals, 3);
mads.s.ager31e71382008-08-13 09:32:07 +00001692 // The result is discarded.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001693}
1694
1695
ager@chromium.org7c537e22008-10-16 08:43:32 +00001696void CodeGenerator::VisitDeclaration(Declaration* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001697#ifdef DEBUG
1698 int original_height = frame_->height();
1699#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001700 Comment cmnt(masm_, "[ Declaration");
1701 Variable* var = node->proxy()->var();
1702 ASSERT(var != NULL); // must have been resolved
1703 Slot* slot = var->slot();
1704
1705 // If it was not possible to allocate the variable at compile time,
1706 // we need to "declare" it at runtime to make sure it actually
1707 // exists in the local context.
1708 if (slot != NULL && slot->type() == Slot::LOOKUP) {
1709 // Variables with a "LOOKUP" slot were introduced as non-locals
1710 // during variable resolution and must have mode DYNAMIC.
ager@chromium.org381abbb2009-02-25 13:23:22 +00001711 ASSERT(var->is_dynamic());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001712 // For now, just do a runtime call.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001713 frame_->EmitPush(cp);
ager@chromium.orgac091b72010-05-05 07:34:42 +00001714 frame_->EmitPush(Operand(var->name()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001715 // Declaration nodes are always declared in only two modes.
1716 ASSERT(node->mode() == Variable::VAR || node->mode() == Variable::CONST);
1717 PropertyAttributes attr = node->mode() == Variable::VAR ? NONE : READ_ONLY;
ager@chromium.orgac091b72010-05-05 07:34:42 +00001718 frame_->EmitPush(Operand(Smi::FromInt(attr)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001719 // Push initial value, if any.
1720 // Note: For variables we must not push an initial value (such as
1721 // 'undefined') because we may have a (legal) redeclaration and we
1722 // must not destroy the current value.
1723 if (node->mode() == Variable::CONST) {
ager@chromium.orgac091b72010-05-05 07:34:42 +00001724 frame_->EmitPushRoot(Heap::kTheHoleValueRootIndex);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001725 } else if (node->fun() != NULL) {
ager@chromium.orgac091b72010-05-05 07:34:42 +00001726 Load(node->fun());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001727 } else {
ager@chromium.orgac091b72010-05-05 07:34:42 +00001728 frame_->EmitPush(Operand(0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001729 }
ager@chromium.orgac091b72010-05-05 07:34:42 +00001730
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001731 frame_->CallRuntime(Runtime::kDeclareContextSlot, 4);
ager@chromium.org7c537e22008-10-16 08:43:32 +00001732 // Ignore the return value (declarations are statements).
ager@chromium.orgac091b72010-05-05 07:34:42 +00001733
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001734 ASSERT(frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001735 return;
1736 }
1737
1738 ASSERT(!var->is_global());
1739
1740 // If we have a function or a constant, we need to initialize the variable.
1741 Expression* val = NULL;
1742 if (node->mode() == Variable::CONST) {
1743 val = new Literal(Factory::the_hole_value());
1744 } else {
1745 val = node->fun(); // NULL if we don't have a function
1746 }
1747
1748 if (val != NULL) {
ager@chromium.orgac091b72010-05-05 07:34:42 +00001749 // Set initial value.
1750 Reference target(this, node->proxy());
1751 Load(val);
1752 target.SetValue(NOT_CONST_INIT);
1753
iposva@chromium.org245aa852009-02-10 00:49:54 +00001754 // Get rid of the assigned value (declarations are statements).
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001755 frame_->Drop();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001756 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001757 ASSERT(frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001758}
1759
1760
ager@chromium.org7c537e22008-10-16 08:43:32 +00001761void CodeGenerator::VisitExpressionStatement(ExpressionStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001762#ifdef DEBUG
1763 int original_height = frame_->height();
1764#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001765 Comment cmnt(masm_, "[ ExpressionStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001766 CodeForStatementPosition(node);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001767 Expression* expression = node->expression();
1768 expression->MarkAsStatement();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001769 Load(expression);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001770 frame_->Drop();
1771 ASSERT(frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001772}
1773
1774
ager@chromium.org7c537e22008-10-16 08:43:32 +00001775void CodeGenerator::VisitEmptyStatement(EmptyStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001776#ifdef DEBUG
1777 int original_height = frame_->height();
1778#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001779 Comment cmnt(masm_, "// EmptyStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001780 CodeForStatementPosition(node);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001781 // nothing to do
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001782 ASSERT(frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001783}
1784
1785
ager@chromium.org7c537e22008-10-16 08:43:32 +00001786void CodeGenerator::VisitIfStatement(IfStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001787#ifdef DEBUG
1788 int original_height = frame_->height();
1789#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001790 Comment cmnt(masm_, "[ IfStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001791 // Generate different code depending on which parts of the if statement
1792 // are present or not.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001793 bool has_then_stm = node->HasThenStatement();
1794 bool has_else_stm = node->HasElseStatement();
1795
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001796 CodeForStatementPosition(node);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001797
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001798 JumpTarget exit;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001799 if (has_then_stm && has_else_stm) {
mads.s.ager31e71382008-08-13 09:32:07 +00001800 Comment cmnt(masm_, "[ IfThenElse");
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001801 JumpTarget then;
1802 JumpTarget else_;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001803 // if (cond)
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001804 LoadCondition(node->condition(), &then, &else_, true);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001805 if (frame_ != NULL) {
1806 Branch(false, &else_);
1807 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001808 // then
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001809 if (frame_ != NULL || then.is_linked()) {
1810 then.Bind();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001811 Visit(node->then_statement());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001812 }
1813 if (frame_ != NULL) {
1814 exit.Jump();
1815 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001816 // else
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001817 if (else_.is_linked()) {
1818 else_.Bind();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001819 Visit(node->else_statement());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001820 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001821
1822 } else if (has_then_stm) {
mads.s.ager31e71382008-08-13 09:32:07 +00001823 Comment cmnt(masm_, "[ IfThen");
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001824 ASSERT(!has_else_stm);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001825 JumpTarget then;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001826 // if (cond)
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001827 LoadCondition(node->condition(), &then, &exit, true);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001828 if (frame_ != NULL) {
1829 Branch(false, &exit);
1830 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001831 // then
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001832 if (frame_ != NULL || then.is_linked()) {
1833 then.Bind();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001834 Visit(node->then_statement());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001835 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001836
1837 } else if (has_else_stm) {
mads.s.ager31e71382008-08-13 09:32:07 +00001838 Comment cmnt(masm_, "[ IfElse");
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001839 ASSERT(!has_then_stm);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001840 JumpTarget else_;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001841 // if (!cond)
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001842 LoadCondition(node->condition(), &exit, &else_, true);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001843 if (frame_ != NULL) {
1844 Branch(true, &exit);
1845 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001846 // else
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001847 if (frame_ != NULL || else_.is_linked()) {
1848 else_.Bind();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001849 Visit(node->else_statement());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001850 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001851
1852 } else {
mads.s.ager31e71382008-08-13 09:32:07 +00001853 Comment cmnt(masm_, "[ If");
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001854 ASSERT(!has_then_stm && !has_else_stm);
1855 // if (cond)
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001856 LoadCondition(node->condition(), &exit, &exit, false);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001857 if (frame_ != NULL) {
1858 if (has_cc()) {
1859 cc_reg_ = al;
1860 } else {
1861 frame_->Drop();
1862 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001863 }
1864 }
1865
1866 // end
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001867 if (exit.is_linked()) {
1868 exit.Bind();
1869 }
1870 ASSERT(!has_valid_frame() || frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001871}
1872
1873
ager@chromium.org7c537e22008-10-16 08:43:32 +00001874void CodeGenerator::VisitContinueStatement(ContinueStatement* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001875 Comment cmnt(masm_, "[ ContinueStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001876 CodeForStatementPosition(node);
1877 node->target()->continue_target()->Jump();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001878}
1879
1880
ager@chromium.org7c537e22008-10-16 08:43:32 +00001881void CodeGenerator::VisitBreakStatement(BreakStatement* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001882 Comment cmnt(masm_, "[ BreakStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001883 CodeForStatementPosition(node);
1884 node->target()->break_target()->Jump();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001885}
1886
1887
ager@chromium.org7c537e22008-10-16 08:43:32 +00001888void CodeGenerator::VisitReturnStatement(ReturnStatement* node) {
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00001889 frame_->SpillAll();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001890 Comment cmnt(masm_, "[ ReturnStatement");
mads.s.ager31e71382008-08-13 09:32:07 +00001891
ager@chromium.org4af710e2009-09-15 12:20:11 +00001892 CodeForStatementPosition(node);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001893 Load(node->expression());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001894 if (function_return_is_shadowed_) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001895 frame_->EmitPop(r0);
1896 function_return_.Jump();
1897 } else {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001898 // Pop the result from the frame and prepare the frame for
1899 // returning thus making it easier to merge.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00001900 frame_->PopToR0();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001901 frame_->PrepareForReturn();
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00001902 if (function_return_.is_bound()) {
1903 // If the function return label is already bound we reuse the
1904 // code by jumping to the return site.
1905 function_return_.Jump();
1906 } else {
1907 function_return_.Bind();
1908 GenerateReturnSequence();
1909 }
1910 }
1911}
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001912
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00001913
1914void CodeGenerator::GenerateReturnSequence() {
1915 if (FLAG_trace) {
1916 // Push the return value on the stack as the parameter.
1917 // Runtime::TraceExit returns the parameter as it is.
1918 frame_->EmitPush(r0);
1919 frame_->CallRuntime(Runtime::kTraceExit, 1);
1920 }
1921
1922#ifdef DEBUG
1923 // Add a label for checking the size of the code used for returning.
1924 Label check_exit_codesize;
1925 masm_->bind(&check_exit_codesize);
1926#endif
1927 // Make sure that the constant pool is not emitted inside of the return
1928 // sequence.
1929 { Assembler::BlockConstPoolScope block_const_pool(masm_);
1930 // Tear down the frame which will restore the caller's frame pointer and
1931 // the link register.
1932 frame_->Exit();
1933
1934 // Here we use masm_-> instead of the __ macro to avoid the code coverage
1935 // tool from instrumenting as we rely on the code size here.
1936 int32_t sp_delta = (scope()->num_parameters() + 1) * kPointerSize;
1937 masm_->add(sp, sp, Operand(sp_delta));
1938 masm_->Jump(lr);
1939 DeleteFrame();
1940
1941#ifdef DEBUG
1942 // Check that the size of the code used for returning matches what is
1943 // expected by the debugger. If the sp_delts above cannot be encoded in
1944 // the add instruction the add will generate two instructions.
1945 int return_sequence_length =
1946 masm_->InstructionsGeneratedSince(&check_exit_codesize);
1947 CHECK(return_sequence_length ==
1948 Assembler::kJSReturnSequenceInstructions ||
1949 return_sequence_length ==
1950 Assembler::kJSReturnSequenceInstructions + 1);
1951#endif
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001952 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001953}
1954
1955
ager@chromium.org7c537e22008-10-16 08:43:32 +00001956void CodeGenerator::VisitWithEnterStatement(WithEnterStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001957#ifdef DEBUG
1958 int original_height = frame_->height();
1959#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001960 Comment cmnt(masm_, "[ WithEnterStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001961 CodeForStatementPosition(node);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001962 Load(node->expression());
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001963 if (node->is_catch_block()) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001964 frame_->CallRuntime(Runtime::kPushCatchContext, 1);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001965 } else {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001966 frame_->CallRuntime(Runtime::kPushContext, 1);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001967 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00001968#ifdef DEBUG
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001969 JumpTarget verified_true;
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00001970 __ cmp(r0, cp);
ager@chromium.org65dad4b2009-04-23 08:48:43 +00001971 verified_true.Branch(eq);
1972 __ stop("PushContext: r0 is expected to be the same as cp");
1973 verified_true.Bind();
1974#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001975 // Update context local.
ager@chromium.org3bf7b912008-11-17 09:09:45 +00001976 __ str(cp, frame_->Context());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001977 ASSERT(frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001978}
1979
1980
ager@chromium.org7c537e22008-10-16 08:43:32 +00001981void CodeGenerator::VisitWithExitStatement(WithExitStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001982#ifdef DEBUG
1983 int original_height = frame_->height();
1984#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001985 Comment cmnt(masm_, "[ WithExitStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001986 CodeForStatementPosition(node);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001987 // Pop context.
1988 __ ldr(cp, ContextOperand(cp, Context::PREVIOUS_INDEX));
1989 // Update context local.
ager@chromium.org3bf7b912008-11-17 09:09:45 +00001990 __ str(cp, frame_->Context());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001991 ASSERT(frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001992}
1993
1994
ager@chromium.org7c537e22008-10-16 08:43:32 +00001995void CodeGenerator::VisitSwitchStatement(SwitchStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001996#ifdef DEBUG
1997 int original_height = frame_->height();
1998#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001999 Comment cmnt(masm_, "[ SwitchStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002000 CodeForStatementPosition(node);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00002001 node->break_target()->SetExpectedHeight();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002002
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002003 Load(node->tag());
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002004
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002005 JumpTarget next_test;
2006 JumpTarget fall_through;
2007 JumpTarget default_entry;
2008 JumpTarget default_exit(JumpTarget::BIDIRECTIONAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002009 ZoneList<CaseClause*>* cases = node->cases();
2010 int length = cases->length();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002011 CaseClause* default_clause = NULL;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002012
2013 for (int i = 0; i < length; i++) {
2014 CaseClause* clause = cases->at(i);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002015 if (clause->is_default()) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002016 // Remember the default clause and compile it at the end.
2017 default_clause = clause;
2018 continue;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002019 }
2020
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002021 Comment cmnt(masm_, "[ Case clause");
2022 // Compile the test.
2023 next_test.Bind();
2024 next_test.Unuse();
2025 // Duplicate TOS.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00002026 frame_->Dup();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00002027 Comparison(eq, NULL, clause->label(), true);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002028 Branch(false, &next_test);
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00002029
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002030 // Before entering the body from the test, remove the switch value from
2031 // the stack.
2032 frame_->Drop();
2033
2034 // Label the body so that fall through is enabled.
2035 if (i > 0 && cases->at(i - 1)->is_default()) {
2036 default_exit.Bind();
2037 } else {
2038 fall_through.Bind();
2039 fall_through.Unuse();
2040 }
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002041 VisitStatements(clause->statements());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002042
2043 // If control flow can fall through from the body, jump to the next body
2044 // or the end of the statement.
2045 if (frame_ != NULL) {
2046 if (i < length - 1 && cases->at(i + 1)->is_default()) {
2047 default_entry.Jump();
2048 } else {
2049 fall_through.Jump();
2050 }
2051 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002052 }
2053
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002054 // The final "test" removes the switch value.
2055 next_test.Bind();
2056 frame_->Drop();
2057
2058 // If there is a default clause, compile it.
2059 if (default_clause != NULL) {
2060 Comment cmnt(masm_, "[ Default clause");
2061 default_entry.Bind();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002062 VisitStatements(default_clause->statements());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002063 // If control flow can fall out of the default and there is a case after
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00002064 // it, jump to that case's body.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002065 if (frame_ != NULL && default_exit.is_bound()) {
2066 default_exit.Jump();
2067 }
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00002068 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002069
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002070 if (fall_through.is_linked()) {
2071 fall_through.Bind();
2072 }
2073
2074 if (node->break_target()->is_linked()) {
2075 node->break_target()->Bind();
2076 }
2077 node->break_target()->Unuse();
2078 ASSERT(!has_valid_frame() || frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002079}
2080
2081
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002082void CodeGenerator::VisitDoWhileStatement(DoWhileStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002083#ifdef DEBUG
2084 int original_height = frame_->height();
2085#endif
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002086 Comment cmnt(masm_, "[ DoWhileStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002087 CodeForStatementPosition(node);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00002088 node->break_target()->SetExpectedHeight();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002089 JumpTarget body(JumpTarget::BIDIRECTIONAL);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00002090 IncrementLoopNesting();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002091
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002092 // Label the top of the loop for the backward CFG edge. If the test
2093 // is always true we can use the continue target, and if the test is
2094 // always false there is no need.
2095 ConditionAnalysis info = AnalyzeCondition(node->cond());
2096 switch (info) {
2097 case ALWAYS_TRUE:
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00002098 node->continue_target()->SetExpectedHeight();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002099 node->continue_target()->Bind();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002100 break;
2101 case ALWAYS_FALSE:
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00002102 node->continue_target()->SetExpectedHeight();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002103 break;
2104 case DONT_KNOW:
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00002105 node->continue_target()->SetExpectedHeight();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002106 body.Bind();
2107 break;
2108 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002109
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002110 CheckStack(); // TODO(1222600): ignore if body contains calls.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002111 Visit(node->body());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002112
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00002113 // Compile the test.
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002114 switch (info) {
2115 case ALWAYS_TRUE:
2116 // If control can fall off the end of the body, jump back to the
2117 // top.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002118 if (has_valid_frame()) {
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002119 node->continue_target()->Jump();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002120 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002121 break;
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002122 case ALWAYS_FALSE:
2123 // If we have a continue in the body, we only have to bind its
2124 // jump target.
2125 if (node->continue_target()->is_linked()) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002126 node->continue_target()->Bind();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002127 }
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002128 break;
2129 case DONT_KNOW:
2130 // We have to compile the test expression if it can be reached by
2131 // control flow falling out of the body or via continue.
2132 if (node->continue_target()->is_linked()) {
2133 node->continue_target()->Bind();
2134 }
2135 if (has_valid_frame()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002136 Comment cmnt(masm_, "[ DoWhileCondition");
2137 CodeForDoWhileConditionPosition(node);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002138 LoadCondition(node->cond(), &body, node->break_target(), true);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002139 if (has_valid_frame()) {
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002140 // A invalid frame here indicates that control did not
2141 // fall out of the test expression.
2142 Branch(true, &body);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002143 }
2144 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002145 break;
2146 }
2147
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002148 if (node->break_target()->is_linked()) {
2149 node->break_target()->Bind();
2150 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00002151 DecrementLoopNesting();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002152 ASSERT(!has_valid_frame() || frame_->height() == original_height);
2153}
2154
2155
2156void CodeGenerator::VisitWhileStatement(WhileStatement* node) {
2157#ifdef DEBUG
2158 int original_height = frame_->height();
2159#endif
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002160 Comment cmnt(masm_, "[ WhileStatement");
2161 CodeForStatementPosition(node);
2162
2163 // If the test is never true and has no side effects there is no need
2164 // to compile the test or body.
2165 ConditionAnalysis info = AnalyzeCondition(node->cond());
2166 if (info == ALWAYS_FALSE) return;
2167
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00002168 node->break_target()->SetExpectedHeight();
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00002169 IncrementLoopNesting();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002170
2171 // Label the top of the loop with the continue target for the backward
2172 // CFG edge.
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00002173 node->continue_target()->SetExpectedHeight();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002174 node->continue_target()->Bind();
2175
2176 if (info == DONT_KNOW) {
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00002177 JumpTarget body(JumpTarget::BIDIRECTIONAL);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002178 LoadCondition(node->cond(), &body, node->break_target(), true);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002179 if (has_valid_frame()) {
2180 // A NULL frame indicates that control did not fall out of the
2181 // test expression.
2182 Branch(false, node->break_target());
2183 }
2184 if (has_valid_frame() || body.is_linked()) {
2185 body.Bind();
2186 }
2187 }
2188
2189 if (has_valid_frame()) {
2190 CheckStack(); // TODO(1222600): ignore if body contains calls.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002191 Visit(node->body());
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002192
2193 // If control flow can fall out of the body, jump back to the top.
2194 if (has_valid_frame()) {
2195 node->continue_target()->Jump();
2196 }
2197 }
2198 if (node->break_target()->is_linked()) {
2199 node->break_target()->Bind();
2200 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00002201 DecrementLoopNesting();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002202 ASSERT(!has_valid_frame() || frame_->height() == original_height);
2203}
2204
2205
2206void CodeGenerator::VisitForStatement(ForStatement* node) {
2207#ifdef DEBUG
2208 int original_height = frame_->height();
2209#endif
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002210 Comment cmnt(masm_, "[ ForStatement");
2211 CodeForStatementPosition(node);
2212 if (node->init() != NULL) {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002213 Visit(node->init());
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002214 }
2215
2216 // If the test is never true there is no need to compile the test or
2217 // body.
2218 ConditionAnalysis info = AnalyzeCondition(node->cond());
2219 if (info == ALWAYS_FALSE) return;
2220
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00002221 node->break_target()->SetExpectedHeight();
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00002222 IncrementLoopNesting();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002223
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00002224 // We know that the loop index is a smi if it is not modified in the
2225 // loop body and it is checked against a constant limit in the loop
2226 // condition. In this case, we reset the static type information of the
2227 // loop index to smi before compiling the body, the update expression, and
2228 // the bottom check of the loop condition.
2229 TypeInfoCodeGenState type_info_scope(this,
2230 node->is_fast_smi_loop() ?
2231 node->loop_variable()->slot() :
2232 NULL,
2233 TypeInfo::Smi());
2234
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002235 // If there is no update statement, label the top of the loop with the
2236 // continue target, otherwise with the loop target.
2237 JumpTarget loop(JumpTarget::BIDIRECTIONAL);
2238 if (node->next() == NULL) {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00002239 node->continue_target()->SetExpectedHeight();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002240 node->continue_target()->Bind();
2241 } else {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00002242 node->continue_target()->SetExpectedHeight();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002243 loop.Bind();
2244 }
2245
2246 // If the test is always true, there is no need to compile it.
2247 if (info == DONT_KNOW) {
2248 JumpTarget body;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002249 LoadCondition(node->cond(), &body, node->break_target(), true);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002250 if (has_valid_frame()) {
2251 Branch(false, node->break_target());
2252 }
2253 if (has_valid_frame() || body.is_linked()) {
2254 body.Bind();
2255 }
2256 }
2257
2258 if (has_valid_frame()) {
2259 CheckStack(); // TODO(1222600): ignore if body contains calls.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002260 Visit(node->body());
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002261
2262 if (node->next() == NULL) {
2263 // If there is no update statement and control flow can fall out
2264 // of the loop, jump directly to the continue label.
2265 if (has_valid_frame()) {
2266 node->continue_target()->Jump();
2267 }
2268 } else {
2269 // If there is an update statement and control flow can reach it
2270 // via falling out of the body of the loop or continuing, we
2271 // compile the update statement.
2272 if (node->continue_target()->is_linked()) {
2273 node->continue_target()->Bind();
2274 }
2275 if (has_valid_frame()) {
2276 // Record source position of the statement as this code which is
2277 // after the code for the body actually belongs to the loop
2278 // statement and not the body.
2279 CodeForStatementPosition(node);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002280 Visit(node->next());
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002281 loop.Jump();
2282 }
2283 }
2284 }
2285 if (node->break_target()->is_linked()) {
2286 node->break_target()->Bind();
2287 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00002288 DecrementLoopNesting();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002289 ASSERT(!has_valid_frame() || frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002290}
2291
2292
ager@chromium.org7c537e22008-10-16 08:43:32 +00002293void CodeGenerator::VisitForInStatement(ForInStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002294#ifdef DEBUG
2295 int original_height = frame_->height();
2296#endif
ager@chromium.org357bf652010-04-12 11:30:10 +00002297 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002298 Comment cmnt(masm_, "[ ForInStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002299 CodeForStatementPosition(node);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002300
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002301 JumpTarget primitive;
2302 JumpTarget jsobject;
2303 JumpTarget fixed_array;
2304 JumpTarget entry(JumpTarget::BIDIRECTIONAL);
2305 JumpTarget end_del_check;
2306 JumpTarget exit;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002307
2308 // Get the object to enumerate over (converted to JSObject).
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002309 Load(node->enumerable());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002310
2311 // Both SpiderMonkey and kjs ignore null and undefined in contrast
2312 // to the specification. 12.6.4 mandates a call to ToObject.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002313 frame_->EmitPop(r0);
ager@chromium.orgab99eea2009-08-25 07:05:41 +00002314 __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
2315 __ cmp(r0, ip);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002316 exit.Branch(eq);
ager@chromium.orgab99eea2009-08-25 07:05:41 +00002317 __ LoadRoot(ip, Heap::kNullValueRootIndex);
2318 __ cmp(r0, ip);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002319 exit.Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002320
2321 // Stack layout in body:
2322 // [iteration counter (Smi)]
2323 // [length of array]
2324 // [FixedArray]
2325 // [Map or 0]
2326 // [Object]
2327
2328 // Check if enumerable is already a JSObject
2329 __ tst(r0, Operand(kSmiTagMask));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002330 primitive.Branch(eq);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002331 __ CompareObjectType(r0, r1, r1, FIRST_JS_OBJECT_TYPE);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002332 jsobject.Branch(hs);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002333
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002334 primitive.Bind();
2335 frame_->EmitPush(r0);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00002336 frame_->InvokeBuiltin(Builtins::TO_OBJECT, CALL_JS, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002337
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002338 jsobject.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002339 // Get the set of properties (as a FixedArray or Map).
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00002340 // r0: value to be iterated over
2341 frame_->EmitPush(r0); // Push the object being iterated over.
2342
2343 // Check cache validity in generated code. This is a fast case for
2344 // the JSObject::IsSimpleEnum cache validity checks. If we cannot
2345 // guarantee cache validity, call the runtime system to check cache
2346 // validity or get the property names in a fixed array.
2347 JumpTarget call_runtime;
2348 JumpTarget loop(JumpTarget::BIDIRECTIONAL);
2349 JumpTarget check_prototype;
2350 JumpTarget use_cache;
2351 __ mov(r1, Operand(r0));
2352 loop.Bind();
2353 // Check that there are no elements.
2354 __ ldr(r2, FieldMemOperand(r1, JSObject::kElementsOffset));
2355 __ LoadRoot(r4, Heap::kEmptyFixedArrayRootIndex);
2356 __ cmp(r2, r4);
2357 call_runtime.Branch(ne);
2358 // Check that instance descriptors are not empty so that we can
2359 // check for an enum cache. Leave the map in r3 for the subsequent
2360 // prototype load.
2361 __ ldr(r3, FieldMemOperand(r1, HeapObject::kMapOffset));
2362 __ ldr(r2, FieldMemOperand(r3, Map::kInstanceDescriptorsOffset));
2363 __ LoadRoot(ip, Heap::kEmptyDescriptorArrayRootIndex);
2364 __ cmp(r2, ip);
2365 call_runtime.Branch(eq);
2366 // Check that there in an enum cache in the non-empty instance
2367 // descriptors. This is the case if the next enumeration index
2368 // field does not contain a smi.
2369 __ ldr(r2, FieldMemOperand(r2, DescriptorArray::kEnumerationIndexOffset));
2370 __ tst(r2, Operand(kSmiTagMask));
2371 call_runtime.Branch(eq);
2372 // For all objects but the receiver, check that the cache is empty.
2373 // r4: empty fixed array root.
2374 __ cmp(r1, r0);
2375 check_prototype.Branch(eq);
2376 __ ldr(r2, FieldMemOperand(r2, DescriptorArray::kEnumCacheBridgeCacheOffset));
2377 __ cmp(r2, r4);
2378 call_runtime.Branch(ne);
2379 check_prototype.Bind();
2380 // Load the prototype from the map and loop if non-null.
2381 __ ldr(r1, FieldMemOperand(r3, Map::kPrototypeOffset));
2382 __ LoadRoot(ip, Heap::kNullValueRootIndex);
2383 __ cmp(r1, ip);
2384 loop.Branch(ne);
2385 // The enum cache is valid. Load the map of the object being
2386 // iterated over and use the cache for the iteration.
2387 __ ldr(r0, FieldMemOperand(r0, HeapObject::kMapOffset));
2388 use_cache.Jump();
2389
2390 call_runtime.Bind();
2391 // Call the runtime to get the property names for the object.
2392 frame_->EmitPush(r0); // push the object (slot 4) for the runtime call
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002393 frame_->CallRuntime(Runtime::kGetPropertyNamesFast, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002394
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00002395 // If we got a map from the runtime call, we can do a fast
2396 // modification check. Otherwise, we got a fixed array, and we have
2397 // to do a slow check.
2398 // r0: map or fixed array (result from call to
2399 // Runtime::kGetPropertyNamesFast)
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002400 __ mov(r2, Operand(r0));
2401 __ ldr(r1, FieldMemOperand(r2, HeapObject::kMapOffset));
ager@chromium.orgab99eea2009-08-25 07:05:41 +00002402 __ LoadRoot(ip, Heap::kMetaMapRootIndex);
2403 __ cmp(r1, ip);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002404 fixed_array.Branch(ne);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002405
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00002406 use_cache.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002407 // Get enum cache
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00002408 // r0: map (either the result from a call to
2409 // Runtime::kGetPropertyNamesFast or has been fetched directly from
2410 // the object)
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002411 __ mov(r1, Operand(r0));
2412 __ ldr(r1, FieldMemOperand(r1, Map::kInstanceDescriptorsOffset));
2413 __ ldr(r1, FieldMemOperand(r1, DescriptorArray::kEnumerationIndexOffset));
2414 __ ldr(r2,
2415 FieldMemOperand(r1, DescriptorArray::kEnumCacheBridgeCacheOffset));
2416
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002417 frame_->EmitPush(r0); // map
2418 frame_->EmitPush(r2); // enum cache bridge cache
mads.s.ager31e71382008-08-13 09:32:07 +00002419 __ ldr(r0, FieldMemOperand(r2, FixedArray::kLengthOffset));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002420 frame_->EmitPush(r0);
mads.s.ager31e71382008-08-13 09:32:07 +00002421 __ mov(r0, Operand(Smi::FromInt(0)));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002422 frame_->EmitPush(r0);
2423 entry.Jump();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002424
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002425 fixed_array.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002426 __ mov(r1, Operand(Smi::FromInt(0)));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002427 frame_->EmitPush(r1); // insert 0 in place of Map
2428 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002429
2430 // Push the length of the array and the initial index onto the stack.
mads.s.ager31e71382008-08-13 09:32:07 +00002431 __ ldr(r0, FieldMemOperand(r0, FixedArray::kLengthOffset));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002432 frame_->EmitPush(r0);
mads.s.ager31e71382008-08-13 09:32:07 +00002433 __ mov(r0, Operand(Smi::FromInt(0))); // init index
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002434 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002435
2436 // Condition.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002437 entry.Bind();
mads.s.ager31e71382008-08-13 09:32:07 +00002438 // sp[0] : index
2439 // sp[1] : array/enum cache length
2440 // sp[2] : array or enum cache
2441 // sp[3] : 0 or map
2442 // sp[4] : enumerable
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002443 // Grab the current frame's height for the break and continue
2444 // targets only after all the state is pushed on the frame.
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00002445 node->break_target()->SetExpectedHeight();
2446 node->continue_target()->SetExpectedHeight();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002447
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00002448 // Load the current count to r0, load the length to r1.
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00002449 __ Ldrd(r0, r1, frame_->ElementAt(0));
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00002450 __ cmp(r0, r1); // compare to the array length
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002451 node->break_target()->Branch(hs);
2452
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002453 // Get the i'th entry of the array.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002454 __ ldr(r2, frame_->ElementAt(2));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002455 __ add(r2, r2, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
2456 __ ldr(r3, MemOperand(r2, r0, LSL, kPointerSizeLog2 - kSmiTagSize));
2457
2458 // Get Map or 0.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002459 __ ldr(r2, frame_->ElementAt(3));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002460 // Check if this (still) matches the map of the enumerable.
2461 // If not, we have to filter the key.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002462 __ ldr(r1, frame_->ElementAt(4));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002463 __ ldr(r1, FieldMemOperand(r1, HeapObject::kMapOffset));
2464 __ cmp(r1, Operand(r2));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002465 end_del_check.Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002466
2467 // Convert the entry to a string (or null if it isn't a property anymore).
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002468 __ ldr(r0, frame_->ElementAt(4)); // push enumerable
2469 frame_->EmitPush(r0);
2470 frame_->EmitPush(r3); // push entry
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00002471 frame_->InvokeBuiltin(Builtins::FILTER_KEY, CALL_JS, 2);
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00002472 __ mov(r3, Operand(r0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002473
2474 // If the property has been removed while iterating, we just skip it.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00002475 __ LoadRoot(ip, Heap::kNullValueRootIndex);
2476 __ cmp(r3, ip);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002477 node->continue_target()->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002478
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002479 end_del_check.Bind();
2480 // Store the entry in the 'each' expression and take another spin in the
2481 // loop. r3: i'th entry of the enum cache (or string there of)
2482 frame_->EmitPush(r3); // push entry
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002483 { Reference each(this, node->each());
2484 if (!each.is_illegal()) {
mads.s.ager31e71382008-08-13 09:32:07 +00002485 if (each.size() > 0) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002486 __ ldr(r0, frame_->ElementAt(each.size()));
2487 frame_->EmitPush(r0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002488 each.SetValue(NOT_CONST_INIT);
2489 frame_->Drop(2);
2490 } else {
2491 // If the reference was to a slot we rely on the convenient property
2492 // that it doesn't matter whether a value (eg, r3 pushed above) is
2493 // right on top of or right underneath a zero-sized reference.
2494 each.SetValue(NOT_CONST_INIT);
2495 frame_->Drop();
mads.s.ager31e71382008-08-13 09:32:07 +00002496 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002497 }
2498 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002499 // Body.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002500 CheckStack(); // TODO(1222600): ignore if body contains calls.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002501 Visit(node->body());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002502
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002503 // Next. Reestablish a spilled frame in case we are coming here via
2504 // a continue in the body.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002505 node->continue_target()->Bind();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002506 frame_->SpillAll();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002507 frame_->EmitPop(r0);
2508 __ add(r0, r0, Operand(Smi::FromInt(1)));
2509 frame_->EmitPush(r0);
2510 entry.Jump();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002511
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002512 // Cleanup. No need to spill because VirtualFrame::Drop is safe for
2513 // any frame.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002514 node->break_target()->Bind();
ager@chromium.org3bf7b912008-11-17 09:09:45 +00002515 frame_->Drop(5);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002516
2517 // Exit.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002518 exit.Bind();
2519 node->continue_target()->Unuse();
2520 node->break_target()->Unuse();
2521 ASSERT(frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002522}
2523
2524
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002525void CodeGenerator::VisitTryCatchStatement(TryCatchStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002526#ifdef DEBUG
2527 int original_height = frame_->height();
2528#endif
ager@chromium.org357bf652010-04-12 11:30:10 +00002529 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002530 Comment cmnt(masm_, "[ TryCatchStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002531 CodeForStatementPosition(node);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002532
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002533 JumpTarget try_block;
2534 JumpTarget exit;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002535
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002536 try_block.Call();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002537 // --- Catch block ---
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002538 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002539
2540 // Store the caught exception in the catch variable.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002541 Variable* catch_var = node->catch_var()->var();
2542 ASSERT(catch_var != NULL && catch_var->slot() != NULL);
2543 StoreToSlot(catch_var->slot(), NOT_CONST_INIT);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002544
2545 // Remove the exception from the stack.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002546 frame_->Drop();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002547
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002548 VisitStatements(node->catch_block()->statements());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002549 if (frame_ != NULL) {
2550 exit.Jump();
2551 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002552
2553
2554 // --- Try block ---
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002555 try_block.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002556
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002557 frame_->PushTryHandler(TRY_CATCH_HANDLER);
2558 int handler_height = frame_->height();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002559
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002560 // Shadow the labels for all escapes from the try block, including
2561 // returns. During shadowing, the original label is hidden as the
2562 // LabelShadow and operations on the original actually affect the
2563 // shadowing label.
2564 //
2565 // We should probably try to unify the escaping labels and the return
2566 // label.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002567 int nof_escapes = node->escaping_targets()->length();
2568 List<ShadowTarget*> shadows(1 + nof_escapes);
2569
2570 // Add the shadow target for the function return.
2571 static const int kReturnShadowIndex = 0;
2572 shadows.Add(new ShadowTarget(&function_return_));
2573 bool function_return_was_shadowed = function_return_is_shadowed_;
2574 function_return_is_shadowed_ = true;
2575 ASSERT(shadows[kReturnShadowIndex]->other_target() == &function_return_);
2576
2577 // Add the remaining shadow targets.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002578 for (int i = 0; i < nof_escapes; i++) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002579 shadows.Add(new ShadowTarget(node->escaping_targets()->at(i)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002580 }
2581
2582 // Generate code for the statements in the try block.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002583 VisitStatements(node->try_block()->statements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002584
2585 // Stop the introduced shadowing and count the number of required unlinks.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002586 // After shadowing stops, the original labels are unshadowed and the
2587 // LabelShadows represent the formerly shadowing labels.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002588 bool has_unlinks = false;
2589 for (int i = 0; i < shadows.length(); i++) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002590 shadows[i]->StopShadowing();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002591 has_unlinks = has_unlinks || shadows[i]->is_linked();
2592 }
2593 function_return_is_shadowed_ = function_return_was_shadowed;
2594
2595 // Get an external reference to the handler address.
2596 ExternalReference handler_address(Top::k_handler_address);
2597
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002598 // If we can fall off the end of the try block, unlink from try chain.
2599 if (has_valid_frame()) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002600 // The next handler address is on top of the frame. Unlink from
2601 // the handler list and drop the rest of this handler from the
2602 // frame.
2603 ASSERT(StackHandlerConstants::kNextOffset == 0);
2604 frame_->EmitPop(r1);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002605 __ mov(r3, Operand(handler_address));
2606 __ str(r1, MemOperand(r3));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002607 frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002608 if (has_unlinks) {
2609 exit.Jump();
2610 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002611 }
2612
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002613 // Generate unlink code for the (formerly) shadowing labels that have been
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002614 // jumped to. Deallocate each shadow target.
2615 for (int i = 0; i < shadows.length(); i++) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002616 if (shadows[i]->is_linked()) {
mads.s.ager31e71382008-08-13 09:32:07 +00002617 // Unlink from try chain;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002618 shadows[i]->Bind();
2619 // Because we can be jumping here (to spilled code) from unspilled
2620 // code, we need to reestablish a spilled frame at this block.
2621 frame_->SpillAll();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002622
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002623 // Reload sp from the top handler, because some statements that we
2624 // break from (eg, for...in) may have left stuff on the stack.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002625 __ mov(r3, Operand(handler_address));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002626 __ ldr(sp, MemOperand(r3));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002627 frame_->Forget(frame_->height() - handler_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002628
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002629 ASSERT(StackHandlerConstants::kNextOffset == 0);
2630 frame_->EmitPop(r1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002631 __ str(r1, MemOperand(r3));
ager@chromium.org3bf7b912008-11-17 09:09:45 +00002632 frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002633
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002634 if (!function_return_is_shadowed_ && i == kReturnShadowIndex) {
2635 frame_->PrepareForReturn();
2636 }
2637 shadows[i]->other_target()->Jump();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002638 }
2639 }
2640
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002641 exit.Bind();
2642 ASSERT(!has_valid_frame() || frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002643}
2644
2645
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002646void CodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002647#ifdef DEBUG
2648 int original_height = frame_->height();
2649#endif
ager@chromium.org357bf652010-04-12 11:30:10 +00002650 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002651 Comment cmnt(masm_, "[ TryFinallyStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002652 CodeForStatementPosition(node);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002653
2654 // State: Used to keep track of reason for entering the finally
2655 // block. Should probably be extended to hold information for
2656 // break/continue from within the try block.
2657 enum { FALLING, THROWING, JUMPING };
2658
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002659 JumpTarget try_block;
2660 JumpTarget finally_block;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002661
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002662 try_block.Call();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002663
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002664 frame_->EmitPush(r0); // save exception object on the stack
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002665 // In case of thrown exceptions, this is where we continue.
2666 __ mov(r2, Operand(Smi::FromInt(THROWING)));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002667 finally_block.Jump();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002668
2669 // --- Try block ---
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002670 try_block.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002671
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002672 frame_->PushTryHandler(TRY_FINALLY_HANDLER);
2673 int handler_height = frame_->height();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002674
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002675 // Shadow the labels for all escapes from the try block, including
2676 // returns. Shadowing hides the original label as the LabelShadow and
2677 // operations on the original actually affect the shadowing label.
2678 //
2679 // We should probably try to unify the escaping labels and the return
2680 // label.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002681 int nof_escapes = node->escaping_targets()->length();
2682 List<ShadowTarget*> shadows(1 + nof_escapes);
2683
2684 // Add the shadow target for the function return.
2685 static const int kReturnShadowIndex = 0;
2686 shadows.Add(new ShadowTarget(&function_return_));
2687 bool function_return_was_shadowed = function_return_is_shadowed_;
2688 function_return_is_shadowed_ = true;
2689 ASSERT(shadows[kReturnShadowIndex]->other_target() == &function_return_);
2690
2691 // Add the remaining shadow targets.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002692 for (int i = 0; i < nof_escapes; i++) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002693 shadows.Add(new ShadowTarget(node->escaping_targets()->at(i)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002694 }
2695
2696 // Generate code for the statements in the try block.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002697 VisitStatements(node->try_block()->statements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002698
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002699 // Stop the introduced shadowing and count the number of required unlinks.
2700 // After shadowing stops, the original labels are unshadowed and the
2701 // LabelShadows represent the formerly shadowing labels.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002702 int nof_unlinks = 0;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002703 for (int i = 0; i < shadows.length(); i++) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002704 shadows[i]->StopShadowing();
2705 if (shadows[i]->is_linked()) nof_unlinks++;
2706 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002707 function_return_is_shadowed_ = function_return_was_shadowed;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002708
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002709 // Get an external reference to the handler address.
2710 ExternalReference handler_address(Top::k_handler_address);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002711
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002712 // If we can fall off the end of the try block, unlink from the try
2713 // chain and set the state on the frame to FALLING.
2714 if (has_valid_frame()) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002715 // The next handler address is on top of the frame.
2716 ASSERT(StackHandlerConstants::kNextOffset == 0);
2717 frame_->EmitPop(r1);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002718 __ mov(r3, Operand(handler_address));
2719 __ str(r1, MemOperand(r3));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002720 frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002721
2722 // Fake a top of stack value (unneeded when FALLING) and set the
2723 // state in r2, then jump around the unlink blocks if any.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00002724 __ LoadRoot(r0, Heap::kUndefinedValueRootIndex);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002725 frame_->EmitPush(r0);
2726 __ mov(r2, Operand(Smi::FromInt(FALLING)));
2727 if (nof_unlinks > 0) {
2728 finally_block.Jump();
2729 }
2730 }
2731
2732 // Generate code to unlink and set the state for the (formerly)
2733 // shadowing targets that have been jumped to.
2734 for (int i = 0; i < shadows.length(); i++) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002735 if (shadows[i]->is_linked()) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002736 // If we have come from the shadowed return, the return value is
2737 // in (a non-refcounted reference to) r0. We must preserve it
2738 // until it is pushed.
2739 //
2740 // Because we can be jumping here (to spilled code) from
2741 // unspilled code, we need to reestablish a spilled frame at
2742 // this block.
2743 shadows[i]->Bind();
2744 frame_->SpillAll();
2745
2746 // Reload sp from the top handler, because some statements that
2747 // we break from (eg, for...in) may have left stuff on the
2748 // stack.
2749 __ mov(r3, Operand(handler_address));
2750 __ ldr(sp, MemOperand(r3));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002751 frame_->Forget(frame_->height() - handler_height);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002752
2753 // Unlink this handler and drop it from the frame. The next
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002754 // handler address is currently on top of the frame.
2755 ASSERT(StackHandlerConstants::kNextOffset == 0);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002756 frame_->EmitPop(r1);
2757 __ str(r1, MemOperand(r3));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002758 frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002759
2760 if (i == kReturnShadowIndex) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002761 // If this label shadowed the function return, materialize the
2762 // return value on the stack.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002763 frame_->EmitPush(r0);
mads.s.ager31e71382008-08-13 09:32:07 +00002764 } else {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002765 // Fake TOS for targets that shadowed breaks and continues.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00002766 __ LoadRoot(r0, Heap::kUndefinedValueRootIndex);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002767 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002768 }
2769 __ mov(r2, Operand(Smi::FromInt(JUMPING + i)));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002770 if (--nof_unlinks > 0) {
2771 // If this is not the last unlink block, jump around the next.
2772 finally_block.Jump();
2773 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002774 }
2775 }
2776
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002777 // --- Finally block ---
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002778 finally_block.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002779
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002780 // Push the state on the stack.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002781 frame_->EmitPush(r2);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002782
2783 // We keep two elements on the stack - the (possibly faked) result
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002784 // and the state - while evaluating the finally block.
2785 //
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002786 // Generate code for the statements in the finally block.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002787 VisitStatements(node->finally_block()->statements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002788
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002789 if (has_valid_frame()) {
2790 // Restore state and return value or faked TOS.
2791 frame_->EmitPop(r2);
2792 frame_->EmitPop(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002793 }
2794
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002795 // Generate code to jump to the right destination for all used
2796 // formerly shadowing targets. Deallocate each shadow target.
2797 for (int i = 0; i < shadows.length(); i++) {
2798 if (has_valid_frame() && shadows[i]->is_bound()) {
2799 JumpTarget* original = shadows[i]->other_target();
2800 __ cmp(r2, Operand(Smi::FromInt(JUMPING + i)));
2801 if (!function_return_is_shadowed_ && i == kReturnShadowIndex) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002802 JumpTarget skip;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002803 skip.Branch(ne);
2804 frame_->PrepareForReturn();
2805 original->Jump();
2806 skip.Bind();
2807 } else {
2808 original->Branch(eq);
2809 }
2810 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002811 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002812
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002813 if (has_valid_frame()) {
2814 // Check if we need to rethrow the exception.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002815 JumpTarget exit;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002816 __ cmp(r2, Operand(Smi::FromInt(THROWING)));
2817 exit.Branch(ne);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002818
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002819 // Rethrow exception.
2820 frame_->EmitPush(r0);
2821 frame_->CallRuntime(Runtime::kReThrow, 1);
2822
2823 // Done.
2824 exit.Bind();
2825 }
2826 ASSERT(!has_valid_frame() || frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002827}
2828
2829
ager@chromium.org7c537e22008-10-16 08:43:32 +00002830void CodeGenerator::VisitDebuggerStatement(DebuggerStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002831#ifdef DEBUG
2832 int original_height = frame_->height();
2833#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002834 Comment cmnt(masm_, "[ DebuggerStatament");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002835 CodeForStatementPosition(node);
ager@chromium.org65dad4b2009-04-23 08:48:43 +00002836#ifdef ENABLE_DEBUGGER_SUPPORT
ager@chromium.org5c838252010-02-19 08:53:10 +00002837 frame_->DebugBreak();
ager@chromium.org65dad4b2009-04-23 08:48:43 +00002838#endif
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00002839 // Ignore the return value.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002840 ASSERT(frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002841}
2842
2843
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00002844void CodeGenerator::InstantiateFunction(
2845 Handle<SharedFunctionInfo> function_info) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002846 // Use the fast case closure allocation code that allocates in new
2847 // space for nested functions that don't need literals cloning.
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00002848 if (scope()->is_function_scope() && function_info->num_literals() == 0) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002849 FastNewClosureStub stub;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002850 frame_->EmitPush(Operand(function_info));
2851 frame_->SpillAll();
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002852 frame_->CallStub(&stub, 1);
2853 frame_->EmitPush(r0);
2854 } else {
2855 // Create a new closure.
2856 frame_->EmitPush(cp);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002857 frame_->EmitPush(Operand(function_info));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002858 frame_->CallRuntime(Runtime::kNewClosure, 2);
2859 frame_->EmitPush(r0);
2860 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002861}
2862
2863
ager@chromium.org7c537e22008-10-16 08:43:32 +00002864void CodeGenerator::VisitFunctionLiteral(FunctionLiteral* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002865#ifdef DEBUG
2866 int original_height = frame_->height();
2867#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002868 Comment cmnt(masm_, "[ FunctionLiteral");
2869
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00002870 // Build the function info and instantiate it.
2871 Handle<SharedFunctionInfo> function_info =
2872 Compiler::BuildFunctionInfo(node, script(), this);
kasper.lund212ac232008-07-16 07:07:30 +00002873 // Check for stack-overflow exception.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002874 if (HasStackOverflow()) {
2875 ASSERT(frame_->height() == original_height);
2876 return;
2877 }
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00002878 InstantiateFunction(function_info);
ager@chromium.orgac091b72010-05-05 07:34:42 +00002879 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002880}
2881
2882
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00002883void CodeGenerator::VisitSharedFunctionInfoLiteral(
2884 SharedFunctionInfoLiteral* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002885#ifdef DEBUG
2886 int original_height = frame_->height();
2887#endif
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00002888 Comment cmnt(masm_, "[ SharedFunctionInfoLiteral");
2889 InstantiateFunction(node->shared_function_info());
ager@chromium.orgac091b72010-05-05 07:34:42 +00002890 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002891}
2892
2893
ager@chromium.org7c537e22008-10-16 08:43:32 +00002894void CodeGenerator::VisitConditional(Conditional* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002895#ifdef DEBUG
2896 int original_height = frame_->height();
2897#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002898 Comment cmnt(masm_, "[ Conditional");
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002899 JumpTarget then;
2900 JumpTarget else_;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002901 LoadCondition(node->condition(), &then, &else_, true);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00002902 if (has_valid_frame()) {
2903 Branch(false, &else_);
2904 }
2905 if (has_valid_frame() || then.is_linked()) {
2906 then.Bind();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002907 Load(node->then_expression());
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00002908 }
2909 if (else_.is_linked()) {
2910 JumpTarget exit;
2911 if (has_valid_frame()) exit.Jump();
2912 else_.Bind();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002913 Load(node->else_expression());
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00002914 if (exit.is_linked()) exit.Bind();
2915 }
ager@chromium.orgac091b72010-05-05 07:34:42 +00002916 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002917}
2918
2919
ager@chromium.org7c537e22008-10-16 08:43:32 +00002920void CodeGenerator::LoadFromSlot(Slot* slot, TypeofState typeof_state) {
2921 if (slot->type() == Slot::LOOKUP) {
ager@chromium.org381abbb2009-02-25 13:23:22 +00002922 ASSERT(slot->var()->is_dynamic());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002923
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00002924 // JumpTargets do not yet support merging frames so the frame must be
2925 // spilled when jumping to these targets.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002926 JumpTarget slow;
2927 JumpTarget done;
ager@chromium.org381abbb2009-02-25 13:23:22 +00002928
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00002929 // Generate fast case for loading from slots that correspond to
2930 // local/global variables or arguments unless they are shadowed by
2931 // eval-introduced bindings.
2932 EmitDynamicLoadFromSlotFastCase(slot,
2933 typeof_state,
2934 &slow,
2935 &done);
ager@chromium.org381abbb2009-02-25 13:23:22 +00002936
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002937 slow.Bind();
2938 frame_->EmitPush(cp);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00002939 frame_->EmitPush(Operand(slot->var()->name()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002940
ager@chromium.org7c537e22008-10-16 08:43:32 +00002941 if (typeof_state == INSIDE_TYPEOF) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002942 frame_->CallRuntime(Runtime::kLoadContextSlotNoReferenceError, 2);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002943 } else {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002944 frame_->CallRuntime(Runtime::kLoadContextSlot, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002945 }
ager@chromium.org381abbb2009-02-25 13:23:22 +00002946
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002947 done.Bind();
2948 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002949
2950 } else {
ager@chromium.org357bf652010-04-12 11:30:10 +00002951 Register scratch = VirtualFrame::scratch0();
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00002952 TypeInfo info = type_info(slot);
2953 frame_->EmitPush(SlotOperand(slot, scratch), info);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00002954
ager@chromium.org7c537e22008-10-16 08:43:32 +00002955 if (slot->var()->mode() == Variable::CONST) {
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00002956 // Const slots may contain 'the hole' value (the constant hasn't been
2957 // initialized yet) which needs to be converted into the 'undefined'
2958 // value.
2959 Comment cmnt(masm_, "[ Unhole const");
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00002960 Register tos = frame_->PopToRegister();
ager@chromium.orgab99eea2009-08-25 07:05:41 +00002961 __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00002962 __ cmp(tos, ip);
2963 __ LoadRoot(tos, Heap::kUndefinedValueRootIndex, eq);
2964 frame_->EmitPush(tos);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002965 }
2966 }
2967}
2968
2969
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00002970void CodeGenerator::LoadFromSlotCheckForArguments(Slot* slot,
2971 TypeofState state) {
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00002972 VirtualFrame::RegisterAllocationScope scope(this);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00002973 LoadFromSlot(slot, state);
2974
2975 // Bail out quickly if we're not using lazy arguments allocation.
2976 if (ArgumentsMode() != LAZY_ARGUMENTS_ALLOCATION) return;
2977
2978 // ... or if the slot isn't a non-parameter arguments slot.
2979 if (slot->type() == Slot::PARAMETER || !slot->is_arguments()) return;
2980
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00002981 // Load the loaded value from the stack into a register but leave it on the
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00002982 // stack.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00002983 Register tos = frame_->Peek();
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00002984
2985 // If the loaded value is the sentinel that indicates that we
2986 // haven't loaded the arguments object yet, we need to do it now.
2987 JumpTarget exit;
2988 __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00002989 __ cmp(tos, ip);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00002990 exit.Branch(ne);
2991 frame_->Drop();
2992 StoreArgumentsObject(false);
2993 exit.Bind();
2994}
2995
2996
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002997void CodeGenerator::StoreToSlot(Slot* slot, InitState init_state) {
2998 ASSERT(slot != NULL);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00002999 VirtualFrame::RegisterAllocationScope scope(this);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003000 if (slot->type() == Slot::LOOKUP) {
3001 ASSERT(slot->var()->is_dynamic());
3002
3003 // For now, just do a runtime call.
3004 frame_->EmitPush(cp);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003005 frame_->EmitPush(Operand(slot->var()->name()));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003006
3007 if (init_state == CONST_INIT) {
3008 // Same as the case for a normal store, but ignores attribute
3009 // (e.g. READ_ONLY) of context slot so that we can initialize
3010 // const properties (introduced via eval("const foo = (some
3011 // expr);")). Also, uses the current function context instead of
3012 // the top context.
3013 //
3014 // Note that we must declare the foo upon entry of eval(), via a
3015 // context slot declaration, but we cannot initialize it at the
3016 // same time, because the const declaration may be at the end of
3017 // the eval code (sigh...) and the const variable may have been
3018 // used before (where its value is 'undefined'). Thus, we can only
3019 // do the initialization when we actually encounter the expression
3020 // and when the expression operands are defined and valid, and
3021 // thus we need the split into 2 operations: declaration of the
3022 // context slot followed by initialization.
3023 frame_->CallRuntime(Runtime::kInitializeConstContextSlot, 3);
3024 } else {
3025 frame_->CallRuntime(Runtime::kStoreContextSlot, 3);
3026 }
3027 // Storing a variable must keep the (new) value on the expression
3028 // stack. This is necessary for compiling assignment expressions.
3029 frame_->EmitPush(r0);
3030
3031 } else {
3032 ASSERT(!slot->var()->is_dynamic());
ager@chromium.org357bf652010-04-12 11:30:10 +00003033 Register scratch = VirtualFrame::scratch0();
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003034 Register scratch2 = VirtualFrame::scratch1();
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003035
ager@chromium.org357bf652010-04-12 11:30:10 +00003036 // The frame must be spilled when branching to this target.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003037 JumpTarget exit;
ager@chromium.org357bf652010-04-12 11:30:10 +00003038
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003039 if (init_state == CONST_INIT) {
3040 ASSERT(slot->var()->mode() == Variable::CONST);
3041 // Only the first const initialization must be executed (the slot
3042 // still contains 'the hole' value). When the assignment is
3043 // executed, the code is identical to a normal store (see below).
3044 Comment cmnt(masm_, "[ Init const");
ager@chromium.org357bf652010-04-12 11:30:10 +00003045 __ ldr(scratch, SlotOperand(slot, scratch));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003046 __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
ager@chromium.org357bf652010-04-12 11:30:10 +00003047 __ cmp(scratch, ip);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003048 exit.Branch(ne);
3049 }
3050
3051 // We must execute the store. Storing a variable must keep the
3052 // (new) value on the stack. This is necessary for compiling
3053 // assignment expressions.
3054 //
3055 // Note: We will reach here even with slot->var()->mode() ==
3056 // Variable::CONST because of const declarations which will
3057 // initialize consts to 'the hole' value and by doing so, end up
3058 // calling this code. r2 may be loaded with context; used below in
3059 // RecordWrite.
ager@chromium.org357bf652010-04-12 11:30:10 +00003060 Register tos = frame_->Peek();
3061 __ str(tos, SlotOperand(slot, scratch));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003062 if (slot->type() == Slot::CONTEXT) {
3063 // Skip write barrier if the written value is a smi.
ager@chromium.org357bf652010-04-12 11:30:10 +00003064 __ tst(tos, Operand(kSmiTagMask));
3065 // We don't use tos any more after here.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003066 exit.Branch(eq);
ager@chromium.org357bf652010-04-12 11:30:10 +00003067 // scratch is loaded with context when calling SlotOperand above.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003068 int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize;
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003069 // We need an extra register. Until we have a way to do that in the
3070 // virtual frame we will cheat and ask for a free TOS register.
3071 Register scratch3 = frame_->GetTOSRegister();
3072 __ RecordWrite(scratch, Operand(offset), scratch2, scratch3);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003073 }
3074 // If we definitely did not jump over the assignment, we do not need
3075 // to bind the exit label. Doing so can defeat peephole
3076 // optimization.
3077 if (init_state == CONST_INIT || slot->type() == Slot::CONTEXT) {
3078 exit.Bind();
3079 }
3080 }
3081}
3082
3083
ager@chromium.org381abbb2009-02-25 13:23:22 +00003084void CodeGenerator::LoadFromGlobalSlotCheckExtensions(Slot* slot,
3085 TypeofState typeof_state,
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003086 JumpTarget* slow) {
ager@chromium.org381abbb2009-02-25 13:23:22 +00003087 // Check that no extension objects have been created by calls to
3088 // eval from the current scope to the global scope.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003089 Register tmp = frame_->scratch0();
3090 Register tmp2 = frame_->scratch1();
ager@chromium.org381abbb2009-02-25 13:23:22 +00003091 Register context = cp;
3092 Scope* s = scope();
3093 while (s != NULL) {
3094 if (s->num_heap_slots() > 0) {
3095 if (s->calls_eval()) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003096 frame_->SpillAll();
ager@chromium.org381abbb2009-02-25 13:23:22 +00003097 // Check that extension is NULL.
3098 __ ldr(tmp2, ContextOperand(context, Context::EXTENSION_INDEX));
3099 __ tst(tmp2, tmp2);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003100 slow->Branch(ne);
ager@chromium.org381abbb2009-02-25 13:23:22 +00003101 }
3102 // Load next context in chain.
3103 __ ldr(tmp, ContextOperand(context, Context::CLOSURE_INDEX));
3104 __ ldr(tmp, FieldMemOperand(tmp, JSFunction::kContextOffset));
3105 context = tmp;
3106 }
3107 // If no outer scope calls eval, we do not need to check more
3108 // context extensions.
3109 if (!s->outer_scope_calls_eval() || s->is_eval_scope()) break;
3110 s = s->outer_scope();
3111 }
3112
3113 if (s->is_eval_scope()) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003114 frame_->SpillAll();
ager@chromium.org381abbb2009-02-25 13:23:22 +00003115 Label next, fast;
ager@chromium.org357bf652010-04-12 11:30:10 +00003116 __ Move(tmp, context);
ager@chromium.org381abbb2009-02-25 13:23:22 +00003117 __ bind(&next);
3118 // Terminate at global context.
3119 __ ldr(tmp2, FieldMemOperand(tmp, HeapObject::kMapOffset));
ager@chromium.orgab99eea2009-08-25 07:05:41 +00003120 __ LoadRoot(ip, Heap::kGlobalContextMapRootIndex);
3121 __ cmp(tmp2, ip);
ager@chromium.org381abbb2009-02-25 13:23:22 +00003122 __ b(eq, &fast);
3123 // Check that extension is NULL.
3124 __ ldr(tmp2, ContextOperand(tmp, Context::EXTENSION_INDEX));
3125 __ tst(tmp2, tmp2);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003126 slow->Branch(ne);
ager@chromium.org381abbb2009-02-25 13:23:22 +00003127 // Load next context in chain.
3128 __ ldr(tmp, ContextOperand(tmp, Context::CLOSURE_INDEX));
3129 __ ldr(tmp, FieldMemOperand(tmp, JSFunction::kContextOffset));
3130 __ b(&next);
3131 __ bind(&fast);
3132 }
3133
ager@chromium.org381abbb2009-02-25 13:23:22 +00003134 // Load the global object.
3135 LoadGlobal();
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00003136 // Setup the name register and call load IC.
ager@chromium.orgac091b72010-05-05 07:34:42 +00003137 frame_->CallLoadIC(slot->var()->name(),
3138 typeof_state == INSIDE_TYPEOF
3139 ? RelocInfo::CODE_TARGET
3140 : RelocInfo::CODE_TARGET_CONTEXT);
ager@chromium.org381abbb2009-02-25 13:23:22 +00003141}
3142
3143
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00003144void CodeGenerator::EmitDynamicLoadFromSlotFastCase(Slot* slot,
3145 TypeofState typeof_state,
3146 JumpTarget* slow,
3147 JumpTarget* done) {
3148 // Generate fast-case code for variables that might be shadowed by
3149 // eval-introduced variables. Eval is used a lot without
3150 // introducing variables. In those cases, we do not want to
3151 // perform a runtime call for all variables in the scope
3152 // containing the eval.
3153 if (slot->var()->mode() == Variable::DYNAMIC_GLOBAL) {
3154 LoadFromGlobalSlotCheckExtensions(slot, typeof_state, slow);
3155 frame_->SpillAll();
3156 done->Jump();
3157
3158 } else if (slot->var()->mode() == Variable::DYNAMIC_LOCAL) {
3159 frame_->SpillAll();
3160 Slot* potential_slot = slot->var()->local_if_not_shadowed()->slot();
3161 Expression* rewrite = slot->var()->local_if_not_shadowed()->rewrite();
3162 if (potential_slot != NULL) {
3163 // Generate fast case for locals that rewrite to slots.
3164 __ ldr(r0,
3165 ContextSlotOperandCheckExtensions(potential_slot,
3166 r1,
3167 r2,
3168 slow));
3169 if (potential_slot->var()->mode() == Variable::CONST) {
3170 __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
3171 __ cmp(r0, ip);
3172 __ LoadRoot(r0, Heap::kUndefinedValueRootIndex, eq);
3173 }
3174 done->Jump();
3175 } else if (rewrite != NULL) {
3176 // Generate fast case for argument loads.
3177 Property* property = rewrite->AsProperty();
3178 if (property != NULL) {
3179 VariableProxy* obj_proxy = property->obj()->AsVariableProxy();
3180 Literal* key_literal = property->key()->AsLiteral();
3181 if (obj_proxy != NULL &&
3182 key_literal != NULL &&
3183 obj_proxy->IsArguments() &&
3184 key_literal->handle()->IsSmi()) {
3185 // Load arguments object if there are no eval-introduced
3186 // variables. Then load the argument from the arguments
3187 // object using keyed load.
3188 __ ldr(r0,
3189 ContextSlotOperandCheckExtensions(obj_proxy->var()->slot(),
3190 r1,
3191 r2,
3192 slow));
3193 frame_->EmitPush(r0);
3194 __ mov(r1, Operand(key_literal->handle()));
3195 frame_->EmitPush(r1);
3196 EmitKeyedLoad();
3197 done->Jump();
3198 }
3199 }
3200 }
3201 }
3202}
3203
3204
ager@chromium.org7c537e22008-10-16 08:43:32 +00003205void CodeGenerator::VisitSlot(Slot* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003206#ifdef DEBUG
3207 int original_height = frame_->height();
3208#endif
ager@chromium.org7c537e22008-10-16 08:43:32 +00003209 Comment cmnt(masm_, "[ Slot");
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00003210 LoadFromSlotCheckForArguments(node, NOT_INSIDE_TYPEOF);
ager@chromium.orgac091b72010-05-05 07:34:42 +00003211 ASSERT_EQ(original_height + 1, frame_->height());
ager@chromium.org7c537e22008-10-16 08:43:32 +00003212}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003213
ager@chromium.org7c537e22008-10-16 08:43:32 +00003214
3215void CodeGenerator::VisitVariableProxy(VariableProxy* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003216#ifdef DEBUG
3217 int original_height = frame_->height();
3218#endif
ager@chromium.org7c537e22008-10-16 08:43:32 +00003219 Comment cmnt(masm_, "[ VariableProxy");
3220
3221 Variable* var = node->var();
3222 Expression* expr = var->rewrite();
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00003223 if (expr != NULL) {
3224 Visit(expr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003225 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +00003226 ASSERT(var->is_global());
3227 Reference ref(this, node);
ager@chromium.org357bf652010-04-12 11:30:10 +00003228 ref.GetValue();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003229 }
ager@chromium.orgac091b72010-05-05 07:34:42 +00003230 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003231}
3232
3233
ager@chromium.org7c537e22008-10-16 08:43:32 +00003234void CodeGenerator::VisitLiteral(Literal* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003235#ifdef DEBUG
3236 int original_height = frame_->height();
3237#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003238 Comment cmnt(masm_, "[ Literal");
ager@chromium.org357bf652010-04-12 11:30:10 +00003239 Register reg = frame_->GetTOSRegister();
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00003240 bool is_smi = node->handle()->IsSmi();
ager@chromium.org357bf652010-04-12 11:30:10 +00003241 __ mov(reg, Operand(node->handle()));
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00003242 frame_->EmitPush(reg, is_smi ? TypeInfo::Smi() : TypeInfo::Unknown());
ager@chromium.orgac091b72010-05-05 07:34:42 +00003243 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003244}
3245
3246
ager@chromium.org7c537e22008-10-16 08:43:32 +00003247void CodeGenerator::VisitRegExpLiteral(RegExpLiteral* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003248#ifdef DEBUG
3249 int original_height = frame_->height();
3250#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003251 Comment cmnt(masm_, "[ RexExp Literal");
3252
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003253 Register tmp = VirtualFrame::scratch0();
3254 // Free up a TOS register that can be used to push the literal.
3255 Register literal = frame_->GetTOSRegister();
3256
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003257 // Retrieve the literal array and check the allocated entry.
3258
3259 // Load the function of this activation.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003260 __ ldr(tmp, frame_->Function());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003261
3262 // Load the literals array of the function.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003263 __ ldr(tmp, FieldMemOperand(tmp, JSFunction::kLiteralsOffset));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003264
3265 // Load the literal at the ast saved index.
3266 int literal_offset =
3267 FixedArray::kHeaderSize + node->literal_index() * kPointerSize;
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003268 __ ldr(literal, FieldMemOperand(tmp, literal_offset));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003269
kasperl@chromium.org71affb52009-05-26 05:44:31 +00003270 JumpTarget done;
ager@chromium.orgab99eea2009-08-25 07:05:41 +00003271 __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003272 __ cmp(literal, ip);
3273 // This branch locks the virtual frame at the done label to match the
3274 // one we have here, where the literal register is not on the stack and
3275 // nothing is spilled.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003276 done.Branch(ne);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003277
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003278 // If the entry is undefined we call the runtime system to compute
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003279 // the literal.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003280 // literal array (0)
3281 frame_->EmitPush(tmp);
3282 // literal index (1)
3283 frame_->EmitPush(Operand(Smi::FromInt(node->literal_index())));
3284 // RegExp pattern (2)
3285 frame_->EmitPush(Operand(node->pattern()));
3286 // RegExp flags (3)
3287 frame_->EmitPush(Operand(node->flags()));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003288 frame_->CallRuntime(Runtime::kMaterializeRegExpLiteral, 4);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003289 __ Move(literal, r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003290
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003291 // This call to bind will get us back to the virtual frame we had before
3292 // where things are not spilled and the literal register is not on the stack.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003293 done.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003294 // Push the literal.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003295 frame_->EmitPush(literal);
ager@chromium.orgac091b72010-05-05 07:34:42 +00003296 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003297}
3298
3299
ager@chromium.org7c537e22008-10-16 08:43:32 +00003300void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003301#ifdef DEBUG
3302 int original_height = frame_->height();
3303#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003304 Comment cmnt(masm_, "[ ObjectLiteral");
3305
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003306 Register literal = frame_->GetTOSRegister();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003307 // Load the function of this activation.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003308 __ ldr(literal, frame_->Function());
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003309 // Literal array.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003310 __ ldr(literal, FieldMemOperand(literal, JSFunction::kLiteralsOffset));
3311 frame_->EmitPush(literal);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003312 // Literal index.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003313 frame_->EmitPush(Operand(Smi::FromInt(node->literal_index())));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003314 // Constant properties.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003315 frame_->EmitPush(Operand(node->constant_properties()));
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00003316 // Should the object literal have fast elements?
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003317 frame_->EmitPush(Operand(Smi::FromInt(node->fast_elements() ? 1 : 0)));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003318 if (node->depth() > 1) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00003319 frame_->CallRuntime(Runtime::kCreateObjectLiteral, 4);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003320 } else {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00003321 frame_->CallRuntime(Runtime::kCreateObjectLiteralShallow, 4);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003322 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003323 frame_->EmitPush(r0); // save the result
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003324 for (int i = 0; i < node->properties()->length(); i++) {
ager@chromium.org5c838252010-02-19 08:53:10 +00003325 // At the start of each iteration, the top of stack contains
3326 // the newly created object literal.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003327 ObjectLiteral::Property* property = node->properties()->at(i);
3328 Literal* key = property->key();
3329 Expression* value = property->value();
3330 switch (property->kind()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003331 case ObjectLiteral::Property::CONSTANT:
3332 break;
3333 case ObjectLiteral::Property::MATERIALIZED_LITERAL:
3334 if (CompileTimeValue::IsCompileTimeValue(property->value())) break;
3335 // else fall through
ager@chromium.org5c838252010-02-19 08:53:10 +00003336 case ObjectLiteral::Property::COMPUTED:
3337 if (key->handle()->IsSymbol()) {
3338 Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003339 Load(value);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003340 frame_->PopToR0();
3341 // Fetch the object literal.
3342 frame_->SpillAllButCopyTOSToR1();
ager@chromium.org5c838252010-02-19 08:53:10 +00003343 __ mov(r2, Operand(key->handle()));
ager@chromium.org5c838252010-02-19 08:53:10 +00003344 frame_->CallCodeObject(ic, RelocInfo::CODE_TARGET, 0);
3345 break;
3346 }
3347 // else fall through
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003348 case ObjectLiteral::Property::PROTOTYPE: {
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003349 frame_->Dup();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003350 Load(key);
3351 Load(value);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003352 frame_->CallRuntime(Runtime::kSetProperty, 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003353 break;
3354 }
3355 case ObjectLiteral::Property::SETTER: {
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003356 frame_->Dup();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003357 Load(key);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003358 frame_->EmitPush(Operand(Smi::FromInt(1)));
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003359 Load(value);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003360 frame_->CallRuntime(Runtime::kDefineAccessor, 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003361 break;
3362 }
3363 case ObjectLiteral::Property::GETTER: {
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003364 frame_->Dup();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003365 Load(key);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003366 frame_->EmitPush(Operand(Smi::FromInt(0)));
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003367 Load(value);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003368 frame_->CallRuntime(Runtime::kDefineAccessor, 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003369 break;
3370 }
3371 }
3372 }
ager@chromium.orgac091b72010-05-05 07:34:42 +00003373 ASSERT_EQ(original_height + 1, frame_->height());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003374}
3375
3376
ager@chromium.org7c537e22008-10-16 08:43:32 +00003377void CodeGenerator::VisitArrayLiteral(ArrayLiteral* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003378#ifdef DEBUG
3379 int original_height = frame_->height();
3380#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003381 Comment cmnt(masm_, "[ ArrayLiteral");
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003382
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003383 Register tos = frame_->GetTOSRegister();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003384 // Load the function of this activation.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003385 __ ldr(tos, frame_->Function());
ager@chromium.org5c838252010-02-19 08:53:10 +00003386 // Load the literals array of the function.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003387 __ ldr(tos, FieldMemOperand(tos, JSFunction::kLiteralsOffset));
3388 frame_->EmitPush(tos);
3389 frame_->EmitPush(Operand(Smi::FromInt(node->literal_index())));
3390 frame_->EmitPush(Operand(node->constant_elements()));
ager@chromium.org5c838252010-02-19 08:53:10 +00003391 int length = node->values()->length();
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003392 if (node->depth() > 1) {
3393 frame_->CallRuntime(Runtime::kCreateArrayLiteral, 3);
ager@chromium.org5c838252010-02-19 08:53:10 +00003394 } else if (length > FastCloneShallowArrayStub::kMaximumLength) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003395 frame_->CallRuntime(Runtime::kCreateArrayLiteralShallow, 3);
ager@chromium.org5c838252010-02-19 08:53:10 +00003396 } else {
3397 FastCloneShallowArrayStub stub(length);
3398 frame_->CallStub(&stub, 3);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003399 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003400 frame_->EmitPush(r0); // save the result
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003401 // r0: created object literal
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003402
3403 // Generate code to set the elements in the array that are not
3404 // literals.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003405 for (int i = 0; i < node->values()->length(); i++) {
3406 Expression* value = node->values()->at(i);
3407
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003408 // If value is a literal the property value is already set in the
3409 // boilerplate object.
3410 if (value->AsLiteral() != NULL) continue;
3411 // If value is a materialized literal the property value is already set
3412 // in the boilerplate object if it is simple.
3413 if (CompileTimeValue::IsCompileTimeValue(value)) continue;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003414
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003415 // The property must be set by generated code.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003416 Load(value);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003417 frame_->PopToR0();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003418 // Fetch the object literal.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003419 frame_->SpillAllButCopyTOSToR1();
3420
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003421 // Get the elements array.
3422 __ ldr(r1, FieldMemOperand(r1, JSObject::kElementsOffset));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003423
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003424 // Write to the indexed properties array.
kasperl@chromium.orge959c182009-07-27 08:59:04 +00003425 int offset = i * kPointerSize + FixedArray::kHeaderSize;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003426 __ str(r0, FieldMemOperand(r1, offset));
3427
3428 // Update the write barrier for the array address.
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00003429 __ RecordWrite(r1, Operand(offset), r3, r2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003430 }
ager@chromium.orgac091b72010-05-05 07:34:42 +00003431 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003432}
3433
3434
ager@chromium.org32912102009-01-16 10:38:43 +00003435void CodeGenerator::VisitCatchExtensionObject(CatchExtensionObject* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003436#ifdef DEBUG
3437 int original_height = frame_->height();
3438#endif
ager@chromium.org32912102009-01-16 10:38:43 +00003439 // Call runtime routine to allocate the catch extension object and
3440 // assign the exception value to the catch variable.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003441 Comment cmnt(masm_, "[ CatchExtensionObject");
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003442 Load(node->key());
3443 Load(node->value());
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00003444 frame_->CallRuntime(Runtime::kCreateCatchExtensionObject, 2);
3445 frame_->EmitPush(r0);
ager@chromium.orgac091b72010-05-05 07:34:42 +00003446 ASSERT_EQ(original_height + 1, frame_->height());
3447}
3448
3449
3450void CodeGenerator::EmitSlotAssignment(Assignment* node) {
3451#ifdef DEBUG
3452 int original_height = frame_->height();
3453#endif
3454 Comment cmnt(masm(), "[ Variable Assignment");
3455 Variable* var = node->target()->AsVariableProxy()->AsVariable();
3456 ASSERT(var != NULL);
3457 Slot* slot = var->slot();
3458 ASSERT(slot != NULL);
3459
3460 // Evaluate the right-hand side.
3461 if (node->is_compound()) {
3462 // For a compound assignment the right-hand side is a binary operation
3463 // between the current property value and the actual right-hand side.
3464 LoadFromSlotCheckForArguments(slot, NOT_INSIDE_TYPEOF);
3465
3466 // Perform the binary operation.
3467 Literal* literal = node->value()->AsLiteral();
3468 bool overwrite_value =
3469 (node->value()->AsBinaryOperation() != NULL &&
3470 node->value()->AsBinaryOperation()->ResultOverwriteAllowed());
3471 if (literal != NULL && literal->handle()->IsSmi()) {
3472 SmiOperation(node->binary_op(),
3473 literal->handle(),
3474 false,
3475 overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE);
3476 } else {
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00003477 GenerateInlineSmi inline_smi =
3478 loop_nesting() > 0 ? GENERATE_INLINE_SMI : DONT_GENERATE_INLINE_SMI;
3479 if (literal != NULL) {
3480 ASSERT(!literal->handle()->IsSmi());
3481 inline_smi = DONT_GENERATE_INLINE_SMI;
3482 }
ager@chromium.orgac091b72010-05-05 07:34:42 +00003483 Load(node->value());
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00003484 GenericBinaryOperation(node->binary_op(),
3485 overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE,
3486 inline_smi);
ager@chromium.orgac091b72010-05-05 07:34:42 +00003487 }
3488 } else {
3489 Load(node->value());
3490 }
3491
3492 // Perform the assignment.
3493 if (var->mode() != Variable::CONST || node->op() == Token::INIT_CONST) {
3494 CodeForSourcePosition(node->position());
3495 StoreToSlot(slot,
3496 node->op() == Token::INIT_CONST ? CONST_INIT : NOT_CONST_INIT);
3497 }
3498 ASSERT_EQ(original_height + 1, frame_->height());
3499}
3500
3501
3502void CodeGenerator::EmitNamedPropertyAssignment(Assignment* node) {
3503#ifdef DEBUG
3504 int original_height = frame_->height();
3505#endif
3506 Comment cmnt(masm(), "[ Named Property Assignment");
3507 Variable* var = node->target()->AsVariableProxy()->AsVariable();
3508 Property* prop = node->target()->AsProperty();
3509 ASSERT(var == NULL || (prop == NULL && var->is_global()));
3510
3511 // Initialize name and evaluate the receiver sub-expression if necessary. If
3512 // the receiver is trivial it is not placed on the stack at this point, but
3513 // loaded whenever actually needed.
3514 Handle<String> name;
3515 bool is_trivial_receiver = false;
3516 if (var != NULL) {
3517 name = var->name();
3518 } else {
3519 Literal* lit = prop->key()->AsLiteral();
3520 ASSERT_NOT_NULL(lit);
3521 name = Handle<String>::cast(lit->handle());
3522 // Do not materialize the receiver on the frame if it is trivial.
3523 is_trivial_receiver = prop->obj()->IsTrivial();
3524 if (!is_trivial_receiver) Load(prop->obj());
3525 }
3526
3527 // Change to slow case in the beginning of an initialization block to
3528 // avoid the quadratic behavior of repeatedly adding fast properties.
3529 if (node->starts_initialization_block()) {
3530 // Initialization block consists of assignments of the form expr.x = ..., so
3531 // this will never be an assignment to a variable, so there must be a
3532 // receiver object.
3533 ASSERT_EQ(NULL, var);
3534 if (is_trivial_receiver) {
3535 Load(prop->obj());
3536 } else {
3537 frame_->Dup();
3538 }
3539 frame_->CallRuntime(Runtime::kToSlowProperties, 1);
3540 }
3541
3542 // Change to fast case at the end of an initialization block. To prepare for
3543 // that add an extra copy of the receiver to the frame, so that it can be
3544 // converted back to fast case after the assignment.
3545 if (node->ends_initialization_block() && !is_trivial_receiver) {
3546 frame_->Dup();
3547 }
3548
3549 // Stack layout:
3550 // [tos] : receiver (only materialized if non-trivial)
3551 // [tos+1] : receiver if at the end of an initialization block
3552
3553 // Evaluate the right-hand side.
3554 if (node->is_compound()) {
3555 // For a compound assignment the right-hand side is a binary operation
3556 // between the current property value and the actual right-hand side.
3557 if (is_trivial_receiver) {
3558 Load(prop->obj());
3559 } else if (var != NULL) {
3560 LoadGlobal();
3561 } else {
3562 frame_->Dup();
3563 }
3564 EmitNamedLoad(name, var != NULL);
ager@chromium.orgac091b72010-05-05 07:34:42 +00003565
3566 // Perform the binary operation.
3567 Literal* literal = node->value()->AsLiteral();
3568 bool overwrite_value =
3569 (node->value()->AsBinaryOperation() != NULL &&
3570 node->value()->AsBinaryOperation()->ResultOverwriteAllowed());
3571 if (literal != NULL && literal->handle()->IsSmi()) {
3572 SmiOperation(node->binary_op(),
3573 literal->handle(),
3574 false,
3575 overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE);
3576 } else {
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00003577 GenerateInlineSmi inline_smi =
3578 loop_nesting() > 0 ? GENERATE_INLINE_SMI : DONT_GENERATE_INLINE_SMI;
3579 if (literal != NULL) {
3580 ASSERT(!literal->handle()->IsSmi());
3581 inline_smi = DONT_GENERATE_INLINE_SMI;
3582 }
ager@chromium.orgac091b72010-05-05 07:34:42 +00003583 Load(node->value());
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00003584 GenericBinaryOperation(node->binary_op(),
3585 overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE,
3586 inline_smi);
ager@chromium.orgac091b72010-05-05 07:34:42 +00003587 }
3588 } else {
3589 // For non-compound assignment just load the right-hand side.
3590 Load(node->value());
3591 }
3592
3593 // Stack layout:
3594 // [tos] : value
3595 // [tos+1] : receiver (only materialized if non-trivial)
3596 // [tos+2] : receiver if at the end of an initialization block
3597
3598 // Perform the assignment. It is safe to ignore constants here.
3599 ASSERT(var == NULL || var->mode() != Variable::CONST);
3600 ASSERT_NE(Token::INIT_CONST, node->op());
3601 if (is_trivial_receiver) {
3602 // Load the receiver and swap with the value.
3603 Load(prop->obj());
3604 Register t0 = frame_->PopToRegister();
3605 Register t1 = frame_->PopToRegister(t0);
3606 frame_->EmitPush(t0);
3607 frame_->EmitPush(t1);
3608 }
3609 CodeForSourcePosition(node->position());
3610 bool is_contextual = (var != NULL);
3611 EmitNamedStore(name, is_contextual);
3612 frame_->EmitPush(r0);
3613
3614 // Change to fast case at the end of an initialization block.
3615 if (node->ends_initialization_block()) {
3616 ASSERT_EQ(NULL, var);
3617 // The argument to the runtime call is the receiver.
3618 if (is_trivial_receiver) {
3619 Load(prop->obj());
3620 } else {
3621 // A copy of the receiver is below the value of the assignment. Swap
3622 // the receiver and the value of the assignment expression.
3623 Register t0 = frame_->PopToRegister();
3624 Register t1 = frame_->PopToRegister(t0);
3625 frame_->EmitPush(t0);
3626 frame_->EmitPush(t1);
3627 }
3628 frame_->CallRuntime(Runtime::kToFastProperties, 1);
3629 }
3630
3631 // Stack layout:
3632 // [tos] : result
3633
3634 ASSERT_EQ(original_height + 1, frame_->height());
3635}
3636
3637
3638void CodeGenerator::EmitKeyedPropertyAssignment(Assignment* node) {
3639#ifdef DEBUG
3640 int original_height = frame_->height();
3641#endif
3642 Comment cmnt(masm_, "[ Keyed Property Assignment");
3643 Property* prop = node->target()->AsProperty();
3644 ASSERT_NOT_NULL(prop);
3645
3646 // Evaluate the receiver subexpression.
3647 Load(prop->obj());
3648
3649 // Change to slow case in the beginning of an initialization block to
3650 // avoid the quadratic behavior of repeatedly adding fast properties.
3651 if (node->starts_initialization_block()) {
3652 frame_->Dup();
3653 frame_->CallRuntime(Runtime::kToSlowProperties, 1);
3654 }
3655
3656 // Change to fast case at the end of an initialization block. To prepare for
3657 // that add an extra copy of the receiver to the frame, so that it can be
3658 // converted back to fast case after the assignment.
3659 if (node->ends_initialization_block()) {
3660 frame_->Dup();
3661 }
3662
3663 // Evaluate the key subexpression.
3664 Load(prop->key());
3665
3666 // Stack layout:
3667 // [tos] : key
3668 // [tos+1] : receiver
3669 // [tos+2] : receiver if at the end of an initialization block
3670
3671 // Evaluate the right-hand side.
3672 if (node->is_compound()) {
3673 // For a compound assignment the right-hand side is a binary operation
3674 // between the current property value and the actual right-hand side.
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00003675 // Duplicate receiver and key for loading the current property value.
3676 frame_->Dup2();
ager@chromium.orgac091b72010-05-05 07:34:42 +00003677 EmitKeyedLoad();
3678 frame_->EmitPush(r0);
3679
3680 // Perform the binary operation.
3681 Literal* literal = node->value()->AsLiteral();
3682 bool overwrite_value =
3683 (node->value()->AsBinaryOperation() != NULL &&
3684 node->value()->AsBinaryOperation()->ResultOverwriteAllowed());
3685 if (literal != NULL && literal->handle()->IsSmi()) {
3686 SmiOperation(node->binary_op(),
3687 literal->handle(),
3688 false,
3689 overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE);
3690 } else {
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00003691 GenerateInlineSmi inline_smi =
3692 loop_nesting() > 0 ? GENERATE_INLINE_SMI : DONT_GENERATE_INLINE_SMI;
3693 if (literal != NULL) {
3694 ASSERT(!literal->handle()->IsSmi());
3695 inline_smi = DONT_GENERATE_INLINE_SMI;
3696 }
ager@chromium.orgac091b72010-05-05 07:34:42 +00003697 Load(node->value());
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00003698 GenericBinaryOperation(node->binary_op(),
3699 overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE,
3700 inline_smi);
ager@chromium.orgac091b72010-05-05 07:34:42 +00003701 }
3702 } else {
3703 // For non-compound assignment just load the right-hand side.
3704 Load(node->value());
3705 }
3706
3707 // Stack layout:
3708 // [tos] : value
3709 // [tos+1] : key
3710 // [tos+2] : receiver
3711 // [tos+3] : receiver if at the end of an initialization block
3712
3713 // Perform the assignment. It is safe to ignore constants here.
3714 ASSERT(node->op() != Token::INIT_CONST);
3715 CodeForSourcePosition(node->position());
ager@chromium.orgac091b72010-05-05 07:34:42 +00003716 EmitKeyedStore(prop->key()->type());
ager@chromium.orgac091b72010-05-05 07:34:42 +00003717 frame_->EmitPush(r0);
3718
3719 // Stack layout:
3720 // [tos] : result
3721 // [tos+1] : receiver if at the end of an initialization block
3722
3723 // Change to fast case at the end of an initialization block.
3724 if (node->ends_initialization_block()) {
3725 // The argument to the runtime call is the extra copy of the receiver,
3726 // which is below the value of the assignment. Swap the receiver and
3727 // the value of the assignment expression.
3728 Register t0 = frame_->PopToRegister();
3729 Register t1 = frame_->PopToRegister(t0);
3730 frame_->EmitPush(t1);
3731 frame_->EmitPush(t0);
3732 frame_->CallRuntime(Runtime::kToFastProperties, 1);
3733 }
3734
3735 // Stack layout:
3736 // [tos] : result
3737
3738 ASSERT_EQ(original_height + 1, frame_->height());
ager@chromium.org32912102009-01-16 10:38:43 +00003739}
3740
3741
ager@chromium.org7c537e22008-10-16 08:43:32 +00003742void CodeGenerator::VisitAssignment(Assignment* node) {
ager@chromium.org357bf652010-04-12 11:30:10 +00003743 VirtualFrame::RegisterAllocationScope scope(this);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003744#ifdef DEBUG
3745 int original_height = frame_->height();
3746#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003747 Comment cmnt(masm_, "[ Assignment");
mads.s.ager31e71382008-08-13 09:32:07 +00003748
ager@chromium.orgac091b72010-05-05 07:34:42 +00003749 Variable* var = node->target()->AsVariableProxy()->AsVariable();
3750 Property* prop = node->target()->AsProperty();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003751
ager@chromium.orgac091b72010-05-05 07:34:42 +00003752 if (var != NULL && !var->is_global()) {
3753 EmitSlotAssignment(node);
mads.s.ager31e71382008-08-13 09:32:07 +00003754
ager@chromium.orgac091b72010-05-05 07:34:42 +00003755 } else if ((prop != NULL && prop->key()->IsPropertyName()) ||
3756 (var != NULL && var->is_global())) {
3757 // Properties whose keys are property names and global variables are
3758 // treated as named property references. We do not need to consider
3759 // global 'this' because it is not a valid left-hand side.
3760 EmitNamedPropertyAssignment(node);
3761
3762 } else if (prop != NULL) {
3763 // Other properties (including rewritten parameters for a function that
3764 // uses arguments) are keyed property assignments.
3765 EmitKeyedPropertyAssignment(node);
3766
3767 } else {
3768 // Invalid left-hand side.
3769 Load(node->target());
3770 frame_->CallRuntime(Runtime::kThrowReferenceError, 1);
3771 // The runtime call doesn't actually return but the code generator will
3772 // still generate code and expects a certain frame height.
3773 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003774 }
ager@chromium.orgac091b72010-05-05 07:34:42 +00003775 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003776}
3777
3778
ager@chromium.org7c537e22008-10-16 08:43:32 +00003779void CodeGenerator::VisitThrow(Throw* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003780#ifdef DEBUG
3781 int original_height = frame_->height();
3782#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003783 Comment cmnt(masm_, "[ Throw");
3784
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003785 Load(node->exception());
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00003786 CodeForSourcePosition(node->position());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003787 frame_->CallRuntime(Runtime::kThrow, 1);
3788 frame_->EmitPush(r0);
ager@chromium.orgac091b72010-05-05 07:34:42 +00003789 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003790}
3791
3792
ager@chromium.org7c537e22008-10-16 08:43:32 +00003793void CodeGenerator::VisitProperty(Property* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003794#ifdef DEBUG
3795 int original_height = frame_->height();
3796#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003797 Comment cmnt(masm_, "[ Property");
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00003798
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003799 { Reference property(this, node);
ager@chromium.org357bf652010-04-12 11:30:10 +00003800 property.GetValue();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003801 }
ager@chromium.orgac091b72010-05-05 07:34:42 +00003802 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003803}
3804
3805
ager@chromium.org7c537e22008-10-16 08:43:32 +00003806void CodeGenerator::VisitCall(Call* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003807#ifdef DEBUG
3808 int original_height = frame_->height();
3809#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003810 Comment cmnt(masm_, "[ Call");
3811
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003812 Expression* function = node->expression();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003813 ZoneList<Expression*>* args = node->arguments();
3814
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003815 // Standard function call.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003816 // Check if the function is a variable or a property.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003817 Variable* var = function->AsVariableProxy()->AsVariable();
3818 Property* property = function->AsProperty();
3819
3820 // ------------------------------------------------------------------------
3821 // Fast-case: Use inline caching.
3822 // ---
3823 // According to ECMA-262, section 11.2.3, page 44, the function to call
3824 // must be resolved after the arguments have been evaluated. The IC code
3825 // automatically handles this by loading the arguments before the function
3826 // is resolved in cache misses (this also holds for megamorphic calls).
3827 // ------------------------------------------------------------------------
3828
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003829 if (var != NULL && var->is_possibly_eval()) {
3830 // ----------------------------------
3831 // JavaScript example: 'eval(arg)' // eval is not known to be shadowed
3832 // ----------------------------------
3833
3834 // In a call to eval, we first call %ResolvePossiblyDirectEval to
3835 // resolve the function we need to call and the receiver of the
3836 // call. Then we call the resolved function using the given
3837 // arguments.
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00003838
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003839 // Prepare stack for call to resolved function.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003840 Load(function);
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00003841
3842 // Allocate a frame slot for the receiver.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003843 frame_->EmitPushRoot(Heap::kUndefinedValueRootIndex);
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00003844
3845 // Load the arguments.
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003846 int arg_count = args->length();
3847 for (int i = 0; i < arg_count; i++) {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003848 Load(args->at(i));
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003849 }
3850
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00003851 VirtualFrame::SpilledScope spilled_scope(frame_);
3852
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00003853 // If we know that eval can only be shadowed by eval-introduced
3854 // variables we attempt to load the global eval function directly
3855 // in generated code. If we succeed, there is no need to perform a
3856 // context lookup in the runtime system.
3857 JumpTarget done;
3858 if (var->slot() != NULL && var->mode() == Variable::DYNAMIC_GLOBAL) {
3859 ASSERT(var->slot()->type() == Slot::LOOKUP);
3860 JumpTarget slow;
3861 // Prepare the stack for the call to
3862 // ResolvePossiblyDirectEvalNoLookup by pushing the loaded
3863 // function, the first argument to the eval call and the
3864 // receiver.
3865 LoadFromGlobalSlotCheckExtensions(var->slot(),
3866 NOT_INSIDE_TYPEOF,
3867 &slow);
3868 frame_->EmitPush(r0);
3869 if (arg_count > 0) {
3870 __ ldr(r1, MemOperand(sp, arg_count * kPointerSize));
3871 frame_->EmitPush(r1);
3872 } else {
3873 frame_->EmitPush(r2);
3874 }
3875 __ ldr(r1, frame_->Receiver());
3876 frame_->EmitPush(r1);
3877
3878 frame_->CallRuntime(Runtime::kResolvePossiblyDirectEvalNoLookup, 3);
3879
3880 done.Jump();
3881 slow.Bind();
3882 }
3883
3884 // Prepare the stack for the call to ResolvePossiblyDirectEval by
3885 // pushing the loaded function, the first argument to the eval
3886 // call and the receiver.
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003887 __ ldr(r1, MemOperand(sp, arg_count * kPointerSize + kPointerSize));
3888 frame_->EmitPush(r1);
3889 if (arg_count > 0) {
3890 __ ldr(r1, MemOperand(sp, arg_count * kPointerSize));
3891 frame_->EmitPush(r1);
3892 } else {
3893 frame_->EmitPush(r2);
3894 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003895 __ ldr(r1, frame_->Receiver());
3896 frame_->EmitPush(r1);
3897
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003898 // Resolve the call.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003899 frame_->CallRuntime(Runtime::kResolvePossiblyDirectEval, 3);
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003900
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00003901 // If we generated fast-case code bind the jump-target where fast
3902 // and slow case merge.
3903 if (done.is_linked()) done.Bind();
3904
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003905 // Touch up stack with the right values for the function and the receiver.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003906 __ str(r0, MemOperand(sp, (arg_count + 1) * kPointerSize));
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003907 __ str(r1, MemOperand(sp, arg_count * kPointerSize));
3908
3909 // Call the function.
3910 CodeForSourcePosition(node->position());
3911
3912 InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003913 CallFunctionStub call_function(arg_count, in_loop, RECEIVER_MIGHT_BE_VALUE);
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003914 frame_->CallStub(&call_function, arg_count + 1);
3915
3916 __ ldr(cp, frame_->Context());
3917 // Remove the function from the stack.
3918 frame_->Drop();
3919 frame_->EmitPush(r0);
3920
3921 } else if (var != NULL && !var->is_this() && var->is_global()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003922 // ----------------------------------
3923 // JavaScript example: 'foo(1, 2, 3)' // foo is global
3924 // ----------------------------------
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003925 // Pass the global object as the receiver and let the IC stub
3926 // patch the stack to use the global proxy as 'this' in the
3927 // invoked function.
3928 LoadGlobal();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003929
3930 // Load the arguments.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003931 int arg_count = args->length();
3932 for (int i = 0; i < arg_count; i++) {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003933 Load(args->at(i));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003934 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003935
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003936 VirtualFrame::SpilledScope spilled_scope(frame_);
ager@chromium.org5c838252010-02-19 08:53:10 +00003937 // Setup the name register and call the IC initialization code.
3938 __ mov(r2, Operand(var->name()));
kasperl@chromium.org71affb52009-05-26 05:44:31 +00003939 InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP;
3940 Handle<Code> stub = ComputeCallInitialize(arg_count, in_loop);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00003941 CodeForSourcePosition(node->position());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003942 frame_->CallCodeObject(stub, RelocInfo::CODE_TARGET_CONTEXT,
3943 arg_count + 1);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00003944 __ ldr(cp, frame_->Context());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003945 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003946
3947 } else if (var != NULL && var->slot() != NULL &&
3948 var->slot()->type() == Slot::LOOKUP) {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003949 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003950 // ----------------------------------
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00003951 // JavaScript examples:
3952 //
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00003953 // with (obj) foo(1, 2, 3) // foo may be in obj.
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00003954 //
3955 // function f() {};
3956 // function g() {
3957 // eval(...);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00003958 // f(); // f could be in extension object.
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00003959 // }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003960 // ----------------------------------
3961
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00003962 // JumpTargets do not yet support merging frames so the frame must be
3963 // spilled when jumping to these targets.
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00003964 JumpTarget slow, done;
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00003965
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00003966 // Generate fast case for loading functions from slots that
3967 // correspond to local/global variables or arguments unless they
3968 // are shadowed by eval-introduced bindings.
3969 EmitDynamicLoadFromSlotFastCase(var->slot(),
3970 NOT_INSIDE_TYPEOF,
3971 &slow,
3972 &done);
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00003973
3974 slow.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003975 // Load the function
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003976 frame_->EmitPush(cp);
mads.s.ager31e71382008-08-13 09:32:07 +00003977 __ mov(r0, Operand(var->name()));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003978 frame_->EmitPush(r0);
3979 frame_->CallRuntime(Runtime::kLoadContextSlot, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003980 // r0: slot value; r1: receiver
3981
3982 // Load the receiver.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003983 frame_->EmitPush(r0); // function
3984 frame_->EmitPush(r1); // receiver
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003985
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00003986 // If fast case code has been generated, emit code to push the
3987 // function and receiver and have the slow path jump around this
3988 // code.
3989 if (done.is_linked()) {
3990 JumpTarget call;
3991 call.Jump();
3992 done.Bind();
3993 frame_->EmitPush(r0); // function
3994 LoadGlobalReceiver(r1); // receiver
3995 call.Bind();
3996 }
3997
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00003998 // Call the function. At this point, everything is spilled but the
3999 // function and receiver are in r0 and r1.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004000 CallWithArguments(args, NO_CALL_FUNCTION_FLAGS, node->position());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004001 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004002
4003 } else if (property != NULL) {
4004 // Check if the key is a literal string.
4005 Literal* literal = property->key()->AsLiteral();
4006
4007 if (literal != NULL && literal->handle()->IsSymbol()) {
4008 // ------------------------------------------------------------------
4009 // JavaScript example: 'object.foo(1, 2, 3)' or 'map["key"](1, 2, 3)'
4010 // ------------------------------------------------------------------
4011
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00004012 Handle<String> name = Handle<String>::cast(literal->handle());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004013
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00004014 if (ArgumentsMode() == LAZY_ARGUMENTS_ALLOCATION &&
4015 name->IsEqualTo(CStrVector("apply")) &&
4016 args->length() == 2 &&
4017 args->at(1)->AsVariableProxy() != NULL &&
4018 args->at(1)->AsVariableProxy()->IsArguments()) {
4019 // Use the optimized Function.prototype.apply that avoids
4020 // allocating lazily allocated arguments objects.
4021 CallApplyLazy(property->obj(),
4022 args->at(0),
4023 args->at(1)->AsVariableProxy(),
4024 node->position());
4025
4026 } else {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004027 Load(property->obj()); // Receiver.
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00004028 // Load the arguments.
4029 int arg_count = args->length();
4030 for (int i = 0; i < arg_count; i++) {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004031 Load(args->at(i));
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00004032 }
4033
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004034 VirtualFrame::SpilledScope spilled_scope(frame_);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00004035 // Set the name register and call the IC initialization code.
4036 __ mov(r2, Operand(name));
4037 InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP;
4038 Handle<Code> stub = ComputeCallInitialize(arg_count, in_loop);
4039 CodeForSourcePosition(node->position());
4040 frame_->CallCodeObject(stub, RelocInfo::CODE_TARGET, arg_count + 1);
4041 __ ldr(cp, frame_->Context());
4042 frame_->EmitPush(r0);
4043 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004044
4045 } else {
4046 // -------------------------------------------
4047 // JavaScript example: 'array[index](1, 2, 3)'
4048 // -------------------------------------------
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004049 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004050
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004051 Load(property->obj());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004052 if (property->is_synthetic()) {
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00004053 Load(property->key());
4054 EmitKeyedLoad();
4055 // Put the function below the receiver.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004056 // Use the global receiver.
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00004057 frame_->EmitPush(r0); // Function.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004058 LoadGlobalReceiver(r0);
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00004059 // Call the function.
4060 CallWithArguments(args, RECEIVER_MIGHT_BE_VALUE, node->position());
4061 frame_->EmitPush(r0);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004062 } else {
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00004063 // Load the arguments.
4064 int arg_count = args->length();
4065 for (int i = 0; i < arg_count; i++) {
4066 Load(args->at(i));
4067 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004068
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00004069 // Set the name register and call the IC initialization code.
4070 Load(property->key());
4071 frame_->EmitPop(r2); // Function name.
4072
4073 InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP;
4074 Handle<Code> stub = ComputeKeyedCallInitialize(arg_count, in_loop);
4075 CodeForSourcePosition(node->position());
4076 frame_->CallCodeObject(stub, RelocInfo::CODE_TARGET, arg_count + 1);
4077 __ ldr(cp, frame_->Context());
4078 frame_->EmitPush(r0);
4079 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004080 }
4081
4082 } else {
4083 // ----------------------------------
4084 // JavaScript example: 'foo(1, 2, 3)' // foo is not global
4085 // ----------------------------------
4086
4087 // Load the function.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004088 Load(function);
4089
4090 VirtualFrame::SpilledScope spilled_scope(frame_);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004091
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004092 // Pass the global proxy as the receiver.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004093 LoadGlobalReceiver(r0);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004094
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004095 // Call the function.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004096 CallWithArguments(args, NO_CALL_FUNCTION_FLAGS, node->position());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004097 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004098 }
ager@chromium.orgac091b72010-05-05 07:34:42 +00004099 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004100}
4101
4102
ager@chromium.org7c537e22008-10-16 08:43:32 +00004103void CodeGenerator::VisitCallNew(CallNew* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004104#ifdef DEBUG
4105 int original_height = frame_->height();
4106#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004107 Comment cmnt(masm_, "[ CallNew");
4108
4109 // According to ECMA-262, section 11.2.2, page 44, the function
4110 // expression in new calls must be evaluated before the
4111 // arguments. This is different from ordinary calls, where the
4112 // actual function to call is resolved after the arguments have been
4113 // evaluated.
4114
4115 // Compute function to call and use the global object as the
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004116 // receiver. There is no need to use the global proxy here because
4117 // it will always be replaced with a newly allocated object.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004118 Load(node->expression());
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004119 LoadGlobal();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004120
4121 // Push the arguments ("left-to-right") on the stack.
4122 ZoneList<Expression*>* args = node->arguments();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004123 int arg_count = args->length();
4124 for (int i = 0; i < arg_count; i++) {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004125 Load(args->at(i));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004126 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004127
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004128 VirtualFrame::SpilledScope spilled_scope(frame_);
4129
mads.s.ager31e71382008-08-13 09:32:07 +00004130 // r0: the number of arguments.
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00004131 __ mov(r0, Operand(arg_count));
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00004132 // Load the function into r1 as per calling convention.
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00004133 __ ldr(r1, frame_->ElementAt(arg_count + 1));
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00004134
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004135 // Call the construct call builtin that handles allocation and
4136 // constructor invocation.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004137 CodeForSourcePosition(node->position());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004138 Handle<Code> ic(Builtins::builtin(Builtins::JSConstructCall));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004139 frame_->CallCodeObject(ic, RelocInfo::CONSTRUCT_CALL, arg_count + 1);
mads.s.ager31e71382008-08-13 09:32:07 +00004140
4141 // Discard old TOS value and push r0 on the stack (same as Pop(), push(r0)).
ager@chromium.org3bf7b912008-11-17 09:09:45 +00004142 __ str(r0, frame_->Top());
ager@chromium.orgac091b72010-05-05 07:34:42 +00004143 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004144}
4145
4146
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004147void CodeGenerator::GenerateClassOf(ZoneList<Expression*>* args) {
ager@chromium.org357bf652010-04-12 11:30:10 +00004148 VirtualFrame::SpilledScope spilled_scope(frame_);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004149 ASSERT(args->length() == 1);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00004150 JumpTarget leave, null, function, non_function_constructor;
4151
4152 // Load the object into r0.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004153 Load(args->at(0));
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00004154 frame_->EmitPop(r0);
4155
4156 // If the object is a smi, we return null.
4157 __ tst(r0, Operand(kSmiTagMask));
4158 null.Branch(eq);
4159
4160 // Check that the object is a JS object but take special care of JS
4161 // functions to make sure they have 'Function' as their class.
4162 __ CompareObjectType(r0, r0, r1, FIRST_JS_OBJECT_TYPE);
4163 null.Branch(lt);
4164
4165 // As long as JS_FUNCTION_TYPE is the last instance type and it is
4166 // right after LAST_JS_OBJECT_TYPE, we can avoid checking for
4167 // LAST_JS_OBJECT_TYPE.
4168 ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
4169 ASSERT(JS_FUNCTION_TYPE == LAST_JS_OBJECT_TYPE + 1);
4170 __ cmp(r1, Operand(JS_FUNCTION_TYPE));
4171 function.Branch(eq);
4172
4173 // Check if the constructor in the map is a function.
4174 __ ldr(r0, FieldMemOperand(r0, Map::kConstructorOffset));
4175 __ CompareObjectType(r0, r1, r1, JS_FUNCTION_TYPE);
4176 non_function_constructor.Branch(ne);
4177
4178 // The r0 register now contains the constructor function. Grab the
4179 // instance class name from there.
4180 __ ldr(r0, FieldMemOperand(r0, JSFunction::kSharedFunctionInfoOffset));
4181 __ ldr(r0, FieldMemOperand(r0, SharedFunctionInfo::kInstanceClassNameOffset));
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004182 frame_->EmitPush(r0);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00004183 leave.Jump();
4184
4185 // Functions have class 'Function'.
4186 function.Bind();
4187 __ mov(r0, Operand(Factory::function_class_symbol()));
4188 frame_->EmitPush(r0);
4189 leave.Jump();
4190
4191 // Objects with a non-function constructor have class 'Object'.
4192 non_function_constructor.Bind();
4193 __ mov(r0, Operand(Factory::Object_symbol()));
4194 frame_->EmitPush(r0);
4195 leave.Jump();
4196
4197 // Non-JS objects have class null.
4198 null.Bind();
ager@chromium.orgab99eea2009-08-25 07:05:41 +00004199 __ LoadRoot(r0, Heap::kNullValueRootIndex);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00004200 frame_->EmitPush(r0);
4201
4202 // All done.
4203 leave.Bind();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004204}
4205
4206
ager@chromium.org7c537e22008-10-16 08:43:32 +00004207void CodeGenerator::GenerateValueOf(ZoneList<Expression*>* args) {
ager@chromium.org357bf652010-04-12 11:30:10 +00004208 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004209 ASSERT(args->length() == 1);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004210 JumpTarget leave;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004211 Load(args->at(0));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004212 frame_->EmitPop(r0); // r0 contains object.
mads.s.ager31e71382008-08-13 09:32:07 +00004213 // if (object->IsSmi()) return the object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004214 __ tst(r0, Operand(kSmiTagMask));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004215 leave.Branch(eq);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00004216 // It is a heap object - get map. If (!object->IsJSValue()) return the object.
4217 __ CompareObjectType(r0, r1, r1, JS_VALUE_TYPE);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004218 leave.Branch(ne);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004219 // Load the value.
4220 __ ldr(r0, FieldMemOperand(r0, JSValue::kValueOffset));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004221 leave.Bind();
4222 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004223}
4224
4225
ager@chromium.org7c537e22008-10-16 08:43:32 +00004226void CodeGenerator::GenerateSetValueOf(ZoneList<Expression*>* args) {
ager@chromium.org357bf652010-04-12 11:30:10 +00004227 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004228 ASSERT(args->length() == 2);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004229 JumpTarget leave;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004230 Load(args->at(0)); // Load the object.
4231 Load(args->at(1)); // Load the value.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004232 frame_->EmitPop(r0); // r0 contains value
4233 frame_->EmitPop(r1); // r1 contains object
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004234 // if (object->IsSmi()) return object.
4235 __ tst(r1, Operand(kSmiTagMask));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004236 leave.Branch(eq);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00004237 // It is a heap object - get map. If (!object->IsJSValue()) return the object.
4238 __ CompareObjectType(r1, r2, r2, JS_VALUE_TYPE);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004239 leave.Branch(ne);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004240 // Store the value.
4241 __ str(r0, FieldMemOperand(r1, JSValue::kValueOffset));
4242 // Update the write barrier.
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00004243 __ RecordWrite(r1, Operand(JSValue::kValueOffset - kHeapObjectTag), r2, r3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004244 // Leave.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004245 leave.Bind();
4246 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004247}
4248
4249
ager@chromium.org7c537e22008-10-16 08:43:32 +00004250void CodeGenerator::GenerateIsSmi(ZoneList<Expression*>* args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004251 ASSERT(args->length() == 1);
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004252 Load(args->at(0));
4253 Register reg = frame_->PopToRegister();
4254 __ tst(reg, Operand(kSmiTagMask));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004255 cc_reg_ = eq;
4256}
4257
4258
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004259void CodeGenerator::GenerateLog(ZoneList<Expression*>* args) {
4260 // See comment in CodeGenerator::GenerateLog in codegen-ia32.cc.
4261 ASSERT_EQ(args->length(), 3);
christian.plesner.hansen@gmail.comaca49682009-01-07 14:29:04 +00004262#ifdef ENABLE_LOGGING_AND_PROFILING
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004263 if (ShouldGenerateLog(args->at(0))) {
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004264 Load(args->at(1));
4265 Load(args->at(2));
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004266 frame_->CallRuntime(Runtime::kLog, 2);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004267 }
christian.plesner.hansen@gmail.comaca49682009-01-07 14:29:04 +00004268#endif
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004269 frame_->EmitPushRoot(Heap::kUndefinedValueRootIndex);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004270}
4271
4272
ager@chromium.org7c537e22008-10-16 08:43:32 +00004273void CodeGenerator::GenerateIsNonNegativeSmi(ZoneList<Expression*>* args) {
ager@chromium.orgc27e4e72008-09-04 13:52:27 +00004274 ASSERT(args->length() == 1);
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004275 Load(args->at(0));
4276 Register reg = frame_->PopToRegister();
4277 __ tst(reg, Operand(kSmiTagMask | 0x80000000u));
ager@chromium.orgc27e4e72008-09-04 13:52:27 +00004278 cc_reg_ = eq;
4279}
4280
4281
lrn@chromium.org32d961d2010-06-30 09:09:34 +00004282// Generates the Math.pow method.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004283void CodeGenerator::GenerateMathPow(ZoneList<Expression*>* args) {
4284 ASSERT(args->length() == 2);
4285 Load(args->at(0));
4286 Load(args->at(1));
lrn@chromium.org32d961d2010-06-30 09:09:34 +00004287
4288 if (!CpuFeatures::IsSupported(VFP3)) {
4289 frame_->CallRuntime(Runtime::kMath_pow, 2);
4290 frame_->EmitPush(r0);
4291 } else {
4292 CpuFeatures::Scope scope(VFP3);
4293 JumpTarget runtime, done;
4294 Label not_minus_half, allocate_return;
4295
4296 Register scratch1 = VirtualFrame::scratch0();
4297 Register scratch2 = VirtualFrame::scratch1();
4298
4299 // Get base and exponent to registers.
4300 Register exponent = frame_->PopToRegister();
4301 Register base = frame_->PopToRegister(exponent);
4302
4303 // Set the frame for the runtime jump target. The code below jumps to the
4304 // jump target label so the frame needs to be established before that.
4305 ASSERT(runtime.entry_frame() == NULL);
4306 runtime.set_entry_frame(frame_);
4307
4308 __ BranchOnSmi(exponent, runtime.entry_label());
4309
4310 // Special handling of raising to the power of -0.5 and 0.5. First check
4311 // that the value is a heap number and that the lower bits (which for both
4312 // values are zero).
4313 Register heap_number_map = r6;
4314 __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
4315 __ ldr(scratch1, FieldMemOperand(exponent, HeapObject::kMapOffset));
4316 __ ldr(scratch2, FieldMemOperand(exponent, HeapNumber::kMantissaOffset));
4317 __ cmp(scratch1, heap_number_map);
4318 runtime.Branch(ne);
4319 __ tst(scratch2, scratch2);
4320 runtime.Branch(ne);
4321
4322 // Load the e
4323 __ ldr(scratch1, FieldMemOperand(exponent, HeapNumber::kExponentOffset));
4324
4325 // Compare exponent with -0.5.
4326 __ cmp(scratch1, Operand(0xbfe00000));
4327 __ b(ne, &not_minus_half);
4328
4329 // Get the double value from the base into vfp register d0.
4330 __ ObjectToDoubleVFPRegister(base, d0,
4331 scratch1, scratch2, heap_number_map, s0,
4332 runtime.entry_label(),
4333 AVOID_NANS_AND_INFINITIES);
4334
4335 // Load 1.0 into d2.
4336 __ mov(scratch2, Operand(0x3ff00000));
4337 __ mov(scratch1, Operand(0));
4338 __ vmov(d2, scratch1, scratch2);
4339
4340 // Calculate the reciprocal of the square root. 1/sqrt(x) = sqrt(1/x).
4341 __ vdiv(d0, d2, d0);
4342 __ vsqrt(d0, d0);
4343
4344 __ b(&allocate_return);
4345
4346 __ bind(&not_minus_half);
4347 // Compare exponent with 0.5.
4348 __ cmp(scratch1, Operand(0x3fe00000));
4349 runtime.Branch(ne);
4350
4351 // Get the double value from the base into vfp register d0.
4352 __ ObjectToDoubleVFPRegister(base, d0,
4353 scratch1, scratch2, heap_number_map, s0,
4354 runtime.entry_label(),
4355 AVOID_NANS_AND_INFINITIES);
4356 __ vsqrt(d0, d0);
4357
4358 __ bind(&allocate_return);
4359 __ AllocateHeapNumberWithValue(
4360 base, d0, scratch1, scratch2, heap_number_map, runtime.entry_label());
4361 done.Jump();
4362
4363 runtime.Bind();
4364
4365 // Push back the arguments again for the runtime call.
4366 frame_->EmitPush(base);
4367 frame_->EmitPush(exponent);
4368 frame_->CallRuntime(Runtime::kMath_pow, 2);
4369 __ Move(base, r0);
4370
4371 done.Bind();
4372 frame_->EmitPush(base);
4373 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004374}
4375
4376
lrn@chromium.org32d961d2010-06-30 09:09:34 +00004377// Generates the Math.sqrt method.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004378void CodeGenerator::GenerateMathSqrt(ZoneList<Expression*>* args) {
4379 ASSERT(args->length() == 1);
4380 Load(args->at(0));
lrn@chromium.org32d961d2010-06-30 09:09:34 +00004381
4382 if (!CpuFeatures::IsSupported(VFP3)) {
4383 frame_->CallRuntime(Runtime::kMath_sqrt, 1);
4384 frame_->EmitPush(r0);
4385 } else {
4386 CpuFeatures::Scope scope(VFP3);
4387 JumpTarget runtime, done;
4388
4389 Register scratch1 = VirtualFrame::scratch0();
4390 Register scratch2 = VirtualFrame::scratch1();
4391
4392 // Get the value from the frame.
4393 Register tos = frame_->PopToRegister();
4394
4395 // Set the frame for the runtime jump target. The code below jumps to the
4396 // jump target label so the frame needs to be established before that.
4397 ASSERT(runtime.entry_frame() == NULL);
4398 runtime.set_entry_frame(frame_);
4399
4400 Register heap_number_map = r6;
4401 __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
4402
4403 // Get the double value from the heap number into vfp register d0.
4404 __ ObjectToDoubleVFPRegister(tos, d0,
4405 scratch1, scratch2, heap_number_map, s0,
4406 runtime.entry_label());
4407
4408 // Calculate the square root of d0 and place result in a heap number object.
4409 __ vsqrt(d0, d0);
4410 __ AllocateHeapNumberWithValue(
4411 tos, d0, scratch1, scratch2, heap_number_map, runtime.entry_label());
4412 done.Jump();
4413
4414 runtime.Bind();
4415 // Push back the argument again for the runtime call.
4416 frame_->EmitPush(tos);
4417 frame_->CallRuntime(Runtime::kMath_sqrt, 1);
4418 __ Move(tos, r0);
4419
4420 done.Bind();
4421 frame_->EmitPush(tos);
4422 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004423}
4424
4425
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004426class DeferredStringCharCodeAt : public DeferredCode {
4427 public:
4428 DeferredStringCharCodeAt(Register object,
4429 Register index,
4430 Register scratch,
4431 Register result)
4432 : result_(result),
4433 char_code_at_generator_(object,
4434 index,
4435 scratch,
4436 result,
4437 &need_conversion_,
4438 &need_conversion_,
4439 &index_out_of_range_,
4440 STRING_INDEX_IS_NUMBER) {}
4441
4442 StringCharCodeAtGenerator* fast_case_generator() {
4443 return &char_code_at_generator_;
4444 }
4445
4446 virtual void Generate() {
4447 VirtualFrameRuntimeCallHelper call_helper(frame_state());
4448 char_code_at_generator_.GenerateSlow(masm(), call_helper);
4449
4450 __ bind(&need_conversion_);
4451 // Move the undefined value into the result register, which will
4452 // trigger conversion.
4453 __ LoadRoot(result_, Heap::kUndefinedValueRootIndex);
4454 __ jmp(exit_label());
4455
4456 __ bind(&index_out_of_range_);
4457 // When the index is out of range, the spec requires us to return
4458 // NaN.
4459 __ LoadRoot(result_, Heap::kNanValueRootIndex);
4460 __ jmp(exit_label());
4461 }
4462
4463 private:
4464 Register result_;
4465
4466 Label need_conversion_;
4467 Label index_out_of_range_;
4468
4469 StringCharCodeAtGenerator char_code_at_generator_;
4470};
4471
4472
4473// This generates code that performs a String.prototype.charCodeAt() call
4474// or returns a smi in order to trigger conversion.
4475void CodeGenerator::GenerateStringCharCodeAt(ZoneList<Expression*>* args) {
4476 VirtualFrame::SpilledScope spilled_scope(frame_);
4477 Comment(masm_, "[ GenerateStringCharCodeAt");
kasper.lund7276f142008-07-30 08:49:36 +00004478 ASSERT(args->length() == 2);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004479
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004480 Load(args->at(0));
4481 Load(args->at(1));
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004482
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004483 Register index = r1;
4484 Register object = r2;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004485
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004486 frame_->EmitPop(r1);
4487 frame_->EmitPop(r2);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004488
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004489 // We need two extra registers.
4490 Register scratch = r3;
4491 Register result = r0;
4492
4493 DeferredStringCharCodeAt* deferred =
4494 new DeferredStringCharCodeAt(object,
4495 index,
4496 scratch,
4497 result);
4498 deferred->fast_case_generator()->GenerateFast(masm_);
4499 deferred->BindExit();
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004500 frame_->EmitPush(result);
kasper.lund7276f142008-07-30 08:49:36 +00004501}
4502
4503
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004504class DeferredStringCharFromCode : public DeferredCode {
4505 public:
4506 DeferredStringCharFromCode(Register code,
4507 Register result)
4508 : char_from_code_generator_(code, result) {}
4509
4510 StringCharFromCodeGenerator* fast_case_generator() {
4511 return &char_from_code_generator_;
4512 }
4513
4514 virtual void Generate() {
4515 VirtualFrameRuntimeCallHelper call_helper(frame_state());
4516 char_from_code_generator_.GenerateSlow(masm(), call_helper);
4517 }
4518
4519 private:
4520 StringCharFromCodeGenerator char_from_code_generator_;
4521};
4522
4523
4524// Generates code for creating a one-char string from a char code.
4525void CodeGenerator::GenerateStringCharFromCode(ZoneList<Expression*>* args) {
4526 VirtualFrame::SpilledScope spilled_scope(frame_);
4527 Comment(masm_, "[ GenerateStringCharFromCode");
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004528 ASSERT(args->length() == 1);
4529
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004530 Load(args->at(0));
4531
ager@chromium.orgac091b72010-05-05 07:34:42 +00004532 Register code = r1;
ager@chromium.orgac091b72010-05-05 07:34:42 +00004533 Register result = r0;
4534
ager@chromium.orgac091b72010-05-05 07:34:42 +00004535 frame_->EmitPop(code);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004536
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004537 DeferredStringCharFromCode* deferred = new DeferredStringCharFromCode(
4538 code, result);
4539 deferred->fast_case_generator()->GenerateFast(masm_);
4540 deferred->BindExit();
4541 frame_->EmitPush(result);
4542}
4543
4544
4545class DeferredStringCharAt : public DeferredCode {
4546 public:
4547 DeferredStringCharAt(Register object,
4548 Register index,
4549 Register scratch1,
4550 Register scratch2,
4551 Register result)
4552 : result_(result),
4553 char_at_generator_(object,
4554 index,
4555 scratch1,
4556 scratch2,
4557 result,
4558 &need_conversion_,
4559 &need_conversion_,
4560 &index_out_of_range_,
4561 STRING_INDEX_IS_NUMBER) {}
4562
4563 StringCharAtGenerator* fast_case_generator() {
4564 return &char_at_generator_;
4565 }
4566
4567 virtual void Generate() {
4568 VirtualFrameRuntimeCallHelper call_helper(frame_state());
4569 char_at_generator_.GenerateSlow(masm(), call_helper);
4570
4571 __ bind(&need_conversion_);
4572 // Move smi zero into the result register, which will trigger
4573 // conversion.
4574 __ mov(result_, Operand(Smi::FromInt(0)));
4575 __ jmp(exit_label());
4576
4577 __ bind(&index_out_of_range_);
4578 // When the index is out of range, the spec requires us to return
4579 // the empty string.
4580 __ LoadRoot(result_, Heap::kEmptyStringRootIndex);
4581 __ jmp(exit_label());
4582 }
4583
4584 private:
4585 Register result_;
4586
4587 Label need_conversion_;
4588 Label index_out_of_range_;
4589
4590 StringCharAtGenerator char_at_generator_;
4591};
4592
4593
4594// This generates code that performs a String.prototype.charAt() call
4595// or returns a smi in order to trigger conversion.
4596void CodeGenerator::GenerateStringCharAt(ZoneList<Expression*>* args) {
4597 VirtualFrame::SpilledScope spilled_scope(frame_);
4598 Comment(masm_, "[ GenerateStringCharAt");
4599 ASSERT(args->length() == 2);
4600
4601 Load(args->at(0));
4602 Load(args->at(1));
4603
4604 Register index = r1;
4605 Register object = r2;
4606
4607 frame_->EmitPop(r1);
4608 frame_->EmitPop(r2);
4609
4610 // We need three extra registers.
4611 Register scratch1 = r3;
4612 Register scratch2 = r4;
4613 Register result = r0;
4614
4615 DeferredStringCharAt* deferred =
4616 new DeferredStringCharAt(object,
4617 index,
4618 scratch1,
4619 scratch2,
4620 result);
4621 deferred->fast_case_generator()->GenerateFast(masm_);
4622 deferred->BindExit();
ager@chromium.orgac091b72010-05-05 07:34:42 +00004623 frame_->EmitPush(result);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004624}
4625
4626
ager@chromium.org7c537e22008-10-16 08:43:32 +00004627void CodeGenerator::GenerateIsArray(ZoneList<Expression*>* args) {
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00004628 ASSERT(args->length() == 1);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004629 Load(args->at(0));
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004630 JumpTarget answer;
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00004631 // We need the CC bits to come out as not_equal in the case where the
4632 // object is a smi. This can't be done with the usual test opcode so
4633 // we use XOR to get the right CC bits.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004634 Register possible_array = frame_->PopToRegister();
4635 Register scratch = VirtualFrame::scratch0();
4636 __ and_(scratch, possible_array, Operand(kSmiTagMask));
4637 __ eor(scratch, scratch, Operand(kSmiTagMask), SetCC);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004638 answer.Branch(ne);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00004639 // It is a heap object - get the map. Check if the object is a JS array.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004640 __ CompareObjectType(possible_array, scratch, scratch, JS_ARRAY_TYPE);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004641 answer.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004642 cc_reg_ = eq;
4643}
4644
4645
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00004646void CodeGenerator::GenerateIsRegExp(ZoneList<Expression*>* args) {
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00004647 ASSERT(args->length() == 1);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004648 Load(args->at(0));
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00004649 JumpTarget answer;
4650 // We need the CC bits to come out as not_equal in the case where the
4651 // object is a smi. This can't be done with the usual test opcode so
4652 // we use XOR to get the right CC bits.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004653 Register possible_regexp = frame_->PopToRegister();
4654 Register scratch = VirtualFrame::scratch0();
4655 __ and_(scratch, possible_regexp, Operand(kSmiTagMask));
4656 __ eor(scratch, scratch, Operand(kSmiTagMask), SetCC);
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00004657 answer.Branch(ne);
4658 // It is a heap object - get the map. Check if the object is a regexp.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004659 __ CompareObjectType(possible_regexp, scratch, scratch, JS_REGEXP_TYPE);
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00004660 answer.Bind();
4661 cc_reg_ = eq;
4662}
4663
4664
ager@chromium.org6141cbe2009-11-20 12:14:52 +00004665void CodeGenerator::GenerateIsObject(ZoneList<Expression*>* args) {
4666 // This generates a fast version of:
4667 // (typeof(arg) === 'object' || %_ClassOf(arg) == 'RegExp')
ager@chromium.org6141cbe2009-11-20 12:14:52 +00004668 ASSERT(args->length() == 1);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004669 Load(args->at(0));
4670 Register possible_object = frame_->PopToRegister();
4671 __ tst(possible_object, Operand(kSmiTagMask));
ager@chromium.org6141cbe2009-11-20 12:14:52 +00004672 false_target()->Branch(eq);
4673
4674 __ LoadRoot(ip, Heap::kNullValueRootIndex);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004675 __ cmp(possible_object, ip);
ager@chromium.org6141cbe2009-11-20 12:14:52 +00004676 true_target()->Branch(eq);
4677
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004678 Register map_reg = VirtualFrame::scratch0();
4679 __ ldr(map_reg, FieldMemOperand(possible_object, HeapObject::kMapOffset));
ager@chromium.org6141cbe2009-11-20 12:14:52 +00004680 // Undetectable objects behave like undefined when tested with typeof.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004681 __ ldrb(possible_object, FieldMemOperand(map_reg, Map::kBitFieldOffset));
4682 __ tst(possible_object, Operand(1 << Map::kIsUndetectable));
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004683 false_target()->Branch(ne);
ager@chromium.org6141cbe2009-11-20 12:14:52 +00004684
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004685 __ ldrb(possible_object, FieldMemOperand(map_reg, Map::kInstanceTypeOffset));
4686 __ cmp(possible_object, Operand(FIRST_JS_OBJECT_TYPE));
ager@chromium.org6141cbe2009-11-20 12:14:52 +00004687 false_target()->Branch(lt);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004688 __ cmp(possible_object, Operand(LAST_JS_OBJECT_TYPE));
ager@chromium.org6141cbe2009-11-20 12:14:52 +00004689 cc_reg_ = le;
4690}
4691
4692
4693void CodeGenerator::GenerateIsFunction(ZoneList<Expression*>* args) {
4694 // This generates a fast version of:
4695 // (%_ClassOf(arg) === 'Function')
ager@chromium.org6141cbe2009-11-20 12:14:52 +00004696 ASSERT(args->length() == 1);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004697 Load(args->at(0));
4698 Register possible_function = frame_->PopToRegister();
4699 __ tst(possible_function, Operand(kSmiTagMask));
ager@chromium.org6141cbe2009-11-20 12:14:52 +00004700 false_target()->Branch(eq);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004701 Register map_reg = VirtualFrame::scratch0();
4702 Register scratch = VirtualFrame::scratch1();
4703 __ CompareObjectType(possible_function, map_reg, scratch, JS_FUNCTION_TYPE);
ager@chromium.org6141cbe2009-11-20 12:14:52 +00004704 cc_reg_ = eq;
4705}
4706
4707
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004708void CodeGenerator::GenerateIsUndetectableObject(ZoneList<Expression*>* args) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004709 ASSERT(args->length() == 1);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004710 Load(args->at(0));
4711 Register possible_undetectable = frame_->PopToRegister();
4712 __ tst(possible_undetectable, Operand(kSmiTagMask));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004713 false_target()->Branch(eq);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004714 Register scratch = VirtualFrame::scratch0();
4715 __ ldr(scratch,
4716 FieldMemOperand(possible_undetectable, HeapObject::kMapOffset));
4717 __ ldrb(scratch, FieldMemOperand(scratch, Map::kBitFieldOffset));
4718 __ tst(scratch, Operand(1 << Map::kIsUndetectable));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004719 cc_reg_ = ne;
4720}
4721
4722
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004723void CodeGenerator::GenerateIsConstructCall(ZoneList<Expression*>* args) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004724 ASSERT(args->length() == 0);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00004725
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004726 Register scratch0 = VirtualFrame::scratch0();
4727 Register scratch1 = VirtualFrame::scratch1();
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00004728 // Get the frame pointer for the calling frame.
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004729 __ ldr(scratch0, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00004730
4731 // Skip the arguments adaptor frame if it exists.
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004732 __ ldr(scratch1,
4733 MemOperand(scratch0, StandardFrameConstants::kContextOffset));
4734 __ cmp(scratch1, Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
4735 __ ldr(scratch0,
4736 MemOperand(scratch0, StandardFrameConstants::kCallerFPOffset), eq);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00004737
4738 // Check the marker in the calling frame.
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004739 __ ldr(scratch1,
4740 MemOperand(scratch0, StandardFrameConstants::kMarkerOffset));
4741 __ cmp(scratch1, Operand(Smi::FromInt(StackFrame::CONSTRUCT)));
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00004742 cc_reg_ = eq;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004743}
4744
4745
ager@chromium.org7c537e22008-10-16 08:43:32 +00004746void CodeGenerator::GenerateArgumentsLength(ZoneList<Expression*>* args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004747 ASSERT(args->length() == 0);
4748
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004749 Register tos = frame_->GetTOSRegister();
4750 Register scratch0 = VirtualFrame::scratch0();
4751 Register scratch1 = VirtualFrame::scratch1();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004752
lrn@chromium.org25156de2010-04-06 13:10:27 +00004753 // Check if the calling frame is an arguments adaptor frame.
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004754 __ ldr(scratch0,
4755 MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
4756 __ ldr(scratch1,
4757 MemOperand(scratch0, StandardFrameConstants::kContextOffset));
4758 __ cmp(scratch1, Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
4759
4760 // Get the number of formal parameters.
4761 __ mov(tos, Operand(Smi::FromInt(scope()->num_parameters())), LeaveCC, ne);
lrn@chromium.org25156de2010-04-06 13:10:27 +00004762
4763 // Arguments adaptor case: Read the arguments length from the
4764 // adaptor frame.
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004765 __ ldr(tos,
4766 MemOperand(scratch0, ArgumentsAdaptorFrameConstants::kLengthOffset),
4767 eq);
lrn@chromium.org25156de2010-04-06 13:10:27 +00004768
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004769 frame_->EmitPush(tos);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004770}
4771
4772
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00004773void CodeGenerator::GenerateArguments(ZoneList<Expression*>* args) {
ager@chromium.org357bf652010-04-12 11:30:10 +00004774 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004775 ASSERT(args->length() == 1);
4776
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00004777 // Satisfy contract with ArgumentsAccessStub:
4778 // Load the key into r1 and the formal parameters count into r0.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004779 Load(args->at(0));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004780 frame_->EmitPop(r1);
ager@chromium.org5c838252010-02-19 08:53:10 +00004781 __ mov(r0, Operand(Smi::FromInt(scope()->num_parameters())));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004782
4783 // Call the shared stub to get to arguments[key].
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004784 ArgumentsAccessStub stub(ArgumentsAccessStub::READ_ELEMENT);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004785 frame_->CallStub(&stub, 0);
4786 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004787}
4788
4789
ager@chromium.org357bf652010-04-12 11:30:10 +00004790void CodeGenerator::GenerateRandomHeapNumber(
4791 ZoneList<Expression*>* args) {
4792 VirtualFrame::SpilledScope spilled_scope(frame_);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00004793 ASSERT(args->length() == 0);
ager@chromium.org357bf652010-04-12 11:30:10 +00004794
4795 Label slow_allocate_heapnumber;
4796 Label heapnumber_allocated;
4797
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00004798 __ LoadRoot(r6, Heap::kHeapNumberMapRootIndex);
4799 __ AllocateHeapNumber(r4, r1, r2, r6, &slow_allocate_heapnumber);
ager@chromium.org357bf652010-04-12 11:30:10 +00004800 __ jmp(&heapnumber_allocated);
4801
4802 __ bind(&slow_allocate_heapnumber);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00004803 // To allocate a heap number, and ensure that it is not a smi, we
4804 // call the runtime function FUnaryMinus on 0, returning the double
4805 // -0.0. A new, distinct heap number is returned each time.
ager@chromium.org357bf652010-04-12 11:30:10 +00004806 __ mov(r0, Operand(Smi::FromInt(0)));
4807 __ push(r0);
4808 __ CallRuntime(Runtime::kNumberUnaryMinus, 1);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00004809 __ mov(r4, Operand(r0));
ager@chromium.org357bf652010-04-12 11:30:10 +00004810
4811 __ bind(&heapnumber_allocated);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00004812
4813 // Convert 32 random bits in r0 to 0.(32 random bits) in a double
4814 // by computing:
4815 // ( 1.(20 0s)(32 random bits) x 2^20 ) - (1.0 x 2^20)).
4816 if (CpuFeatures::IsSupported(VFP3)) {
4817 __ PrepareCallCFunction(0, r1);
4818 __ CallCFunction(ExternalReference::random_uint32_function(), 0);
4819
4820 CpuFeatures::Scope scope(VFP3);
4821 // 0x41300000 is the top half of 1.0 x 2^20 as a double.
4822 // Create this constant using mov/orr to avoid PC relative load.
4823 __ mov(r1, Operand(0x41000000));
4824 __ orr(r1, r1, Operand(0x300000));
4825 // Move 0x41300000xxxxxxxx (x = random bits) to VFP.
4826 __ vmov(d7, r0, r1);
4827 // Move 0x4130000000000000 to VFP.
4828 __ mov(r0, Operand(0));
4829 __ vmov(d8, r0, r1);
4830 // Subtract and store the result in the heap number.
4831 __ vsub(d7, d7, d8);
4832 __ sub(r0, r4, Operand(kHeapObjectTag));
4833 __ vstr(d7, r0, HeapNumber::kValueOffset);
4834 frame_->EmitPush(r4);
4835 } else {
4836 __ mov(r0, Operand(r4));
4837 __ PrepareCallCFunction(1, r1);
4838 __ CallCFunction(
4839 ExternalReference::fill_heap_number_with_random_function(), 1);
4840 frame_->EmitPush(r0);
4841 }
ager@chromium.orgeadaf222009-06-16 09:43:10 +00004842}
4843
4844
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00004845void CodeGenerator::GenerateStringAdd(ZoneList<Expression*>* args) {
4846 ASSERT_EQ(2, args->length());
4847
4848 Load(args->at(0));
4849 Load(args->at(1));
4850
ager@chromium.org5c838252010-02-19 08:53:10 +00004851 StringAddStub stub(NO_STRING_ADD_FLAGS);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004852 frame_->SpillAll();
ager@chromium.org5c838252010-02-19 08:53:10 +00004853 frame_->CallStub(&stub, 2);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00004854 frame_->EmitPush(r0);
4855}
4856
4857
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004858void CodeGenerator::GenerateSubString(ZoneList<Expression*>* args) {
4859 ASSERT_EQ(3, args->length());
4860
4861 Load(args->at(0));
4862 Load(args->at(1));
4863 Load(args->at(2));
4864
ager@chromium.org5c838252010-02-19 08:53:10 +00004865 SubStringStub stub;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004866 frame_->SpillAll();
ager@chromium.org5c838252010-02-19 08:53:10 +00004867 frame_->CallStub(&stub, 3);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004868 frame_->EmitPush(r0);
4869}
4870
4871
4872void CodeGenerator::GenerateStringCompare(ZoneList<Expression*>* args) {
4873 ASSERT_EQ(2, args->length());
4874
4875 Load(args->at(0));
4876 Load(args->at(1));
4877
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004878 StringCompareStub stub;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004879 frame_->SpillAll();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004880 frame_->CallStub(&stub, 2);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004881 frame_->EmitPush(r0);
4882}
4883
4884
4885void CodeGenerator::GenerateRegExpExec(ZoneList<Expression*>* args) {
4886 ASSERT_EQ(4, args->length());
4887
4888 Load(args->at(0));
4889 Load(args->at(1));
4890 Load(args->at(2));
4891 Load(args->at(3));
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00004892 RegExpExecStub stub;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004893 frame_->SpillAll();
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00004894 frame_->CallStub(&stub, 4);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004895 frame_->EmitPush(r0);
4896}
4897
4898
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00004899void CodeGenerator::GenerateRegExpConstructResult(ZoneList<Expression*>* args) {
4900 // No stub. This code only occurs a few times in regexp.js.
4901 const int kMaxInlineLength = 100;
4902 ASSERT_EQ(3, args->length());
4903 Load(args->at(0)); // Size of array, smi.
4904 Load(args->at(1)); // "index" property value.
4905 Load(args->at(2)); // "input" property value.
4906 {
4907 VirtualFrame::SpilledScope spilled_scope(frame_);
4908 Label slowcase;
4909 Label done;
4910 __ ldr(r1, MemOperand(sp, kPointerSize * 2));
4911 STATIC_ASSERT(kSmiTag == 0);
4912 STATIC_ASSERT(kSmiTagSize == 1);
4913 __ tst(r1, Operand(kSmiTagMask));
4914 __ b(ne, &slowcase);
4915 __ cmp(r1, Operand(Smi::FromInt(kMaxInlineLength)));
4916 __ b(hi, &slowcase);
4917 // Smi-tagging is equivalent to multiplying by 2.
4918 // Allocate RegExpResult followed by FixedArray with size in ebx.
4919 // JSArray: [Map][empty properties][Elements][Length-smi][index][input]
4920 // Elements: [Map][Length][..elements..]
4921 // Size of JSArray with two in-object properties and the header of a
4922 // FixedArray.
4923 int objects_size =
4924 (JSRegExpResult::kSize + FixedArray::kHeaderSize) / kPointerSize;
4925 __ mov(r5, Operand(r1, LSR, kSmiTagSize + kSmiShiftSize));
4926 __ add(r2, r5, Operand(objects_size));
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00004927 __ AllocateInNewSpace(
4928 r2, // In: Size, in words.
4929 r0, // Out: Start of allocation (tagged).
4930 r3, // Scratch register.
4931 r4, // Scratch register.
4932 &slowcase,
4933 static_cast<AllocationFlags>(TAG_OBJECT | SIZE_IN_WORDS));
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00004934 // r0: Start of allocated area, object-tagged.
4935 // r1: Number of elements in array, as smi.
4936 // r5: Number of elements, untagged.
4937
4938 // Set JSArray map to global.regexp_result_map().
4939 // Set empty properties FixedArray.
4940 // Set elements to point to FixedArray allocated right after the JSArray.
4941 // Interleave operations for better latency.
4942 __ ldr(r2, ContextOperand(cp, Context::GLOBAL_INDEX));
4943 __ add(r3, r0, Operand(JSRegExpResult::kSize));
4944 __ mov(r4, Operand(Factory::empty_fixed_array()));
4945 __ ldr(r2, FieldMemOperand(r2, GlobalObject::kGlobalContextOffset));
4946 __ str(r3, FieldMemOperand(r0, JSObject::kElementsOffset));
4947 __ ldr(r2, ContextOperand(r2, Context::REGEXP_RESULT_MAP_INDEX));
4948 __ str(r4, FieldMemOperand(r0, JSObject::kPropertiesOffset));
4949 __ str(r2, FieldMemOperand(r0, HeapObject::kMapOffset));
4950
4951 // Set input, index and length fields from arguments.
4952 __ ldm(ia_w, sp, static_cast<RegList>(r2.bit() | r4.bit()));
4953 __ str(r1, FieldMemOperand(r0, JSArray::kLengthOffset));
4954 __ add(sp, sp, Operand(kPointerSize));
4955 __ str(r4, FieldMemOperand(r0, JSRegExpResult::kIndexOffset));
4956 __ str(r2, FieldMemOperand(r0, JSRegExpResult::kInputOffset));
4957
4958 // Fill out the elements FixedArray.
4959 // r0: JSArray, tagged.
4960 // r3: FixedArray, tagged.
4961 // r5: Number of elements in array, untagged.
4962
4963 // Set map.
4964 __ mov(r2, Operand(Factory::fixed_array_map()));
4965 __ str(r2, FieldMemOperand(r3, HeapObject::kMapOffset));
4966 // Set FixedArray length.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004967 __ mov(r6, Operand(r5, LSL, kSmiTagSize));
4968 __ str(r6, FieldMemOperand(r3, FixedArray::kLengthOffset));
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00004969 // Fill contents of fixed-array with the-hole.
4970 __ mov(r2, Operand(Factory::the_hole_value()));
4971 __ add(r3, r3, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
4972 // Fill fixed array elements with hole.
4973 // r0: JSArray, tagged.
4974 // r2: the hole.
4975 // r3: Start of elements in FixedArray.
4976 // r5: Number of elements to fill.
4977 Label loop;
4978 __ tst(r5, Operand(r5));
4979 __ bind(&loop);
4980 __ b(le, &done); // Jump if r1 is negative or zero.
4981 __ sub(r5, r5, Operand(1), SetCC);
4982 __ str(r2, MemOperand(r3, r5, LSL, kPointerSizeLog2));
4983 __ jmp(&loop);
4984
4985 __ bind(&slowcase);
4986 __ CallRuntime(Runtime::kRegExpConstructResult, 3);
4987
4988 __ bind(&done);
4989 }
4990 frame_->Forget(3);
4991 frame_->EmitPush(r0);
4992}
4993
4994
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00004995class DeferredSearchCache: public DeferredCode {
4996 public:
4997 DeferredSearchCache(Register dst, Register cache, Register key)
4998 : dst_(dst), cache_(cache), key_(key) {
4999 set_comment("[ DeferredSearchCache");
5000 }
5001
5002 virtual void Generate();
5003
5004 private:
5005 Register dst_, cache_, key_;
5006};
5007
5008
5009void DeferredSearchCache::Generate() {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005010 __ Push(cache_, key_);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005011 __ CallRuntime(Runtime::kGetFromCache, 2);
5012 if (!dst_.is(r0)) {
5013 __ mov(dst_, r0);
5014 }
5015}
5016
5017
5018void CodeGenerator::GenerateGetFromCache(ZoneList<Expression*>* args) {
5019 ASSERT_EQ(2, args->length());
5020
5021 ASSERT_NE(NULL, args->at(0)->AsLiteral());
5022 int cache_id = Smi::cast(*(args->at(0)->AsLiteral()->handle()))->value();
5023
5024 Handle<FixedArray> jsfunction_result_caches(
5025 Top::global_context()->jsfunction_result_caches());
5026 if (jsfunction_result_caches->length() <= cache_id) {
5027 __ Abort("Attempt to use undefined cache.");
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005028 frame_->EmitPushRoot(Heap::kUndefinedValueRootIndex);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005029 return;
5030 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005031
5032 Load(args->at(1));
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005033
5034 VirtualFrame::SpilledScope spilled_scope(frame_);
5035
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005036 frame_->EmitPop(r2);
5037
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005038 __ ldr(r1, ContextOperand(cp, Context::GLOBAL_INDEX));
5039 __ ldr(r1, FieldMemOperand(r1, GlobalObject::kGlobalContextOffset));
5040 __ ldr(r1, ContextOperand(r1, Context::JSFUNCTION_RESULT_CACHES_INDEX));
5041 __ ldr(r1, FieldMemOperand(r1, FixedArray::OffsetOfElementAt(cache_id)));
5042
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005043 DeferredSearchCache* deferred = new DeferredSearchCache(r0, r1, r2);
5044
5045 const int kFingerOffset =
5046 FixedArray::OffsetOfElementAt(JSFunctionResultCache::kFingerIndex);
5047 ASSERT(kSmiTag == 0 && kSmiTagSize == 1);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005048 __ ldr(r0, FieldMemOperand(r1, kFingerOffset));
5049 // r0 now holds finger offset as a smi.
5050 __ add(r3, r1, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
5051 // r3 now points to the start of fixed array elements.
5052 __ ldr(r0, MemOperand(r3, r0, LSL, kPointerSizeLog2 - kSmiTagSize, PreIndex));
5053 // Note side effect of PreIndex: r3 now points to the key of the pair.
5054 __ cmp(r2, r0);
5055 deferred->Branch(ne);
5056
5057 __ ldr(r0, MemOperand(r3, kPointerSize));
5058
5059 deferred->BindExit();
5060 frame_->EmitPush(r0);
5061}
5062
5063
ager@chromium.org5c838252010-02-19 08:53:10 +00005064void CodeGenerator::GenerateNumberToString(ZoneList<Expression*>* args) {
5065 ASSERT_EQ(args->length(), 1);
5066
5067 // Load the argument on the stack and jump to the runtime.
5068 Load(args->at(0));
5069
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005070 NumberToStringStub stub;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005071 frame_->SpillAll();
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005072 frame_->CallStub(&stub, 1);
ager@chromium.org5c838252010-02-19 08:53:10 +00005073 frame_->EmitPush(r0);
5074}
5075
5076
ager@chromium.orgac091b72010-05-05 07:34:42 +00005077class DeferredSwapElements: public DeferredCode {
5078 public:
5079 DeferredSwapElements(Register object, Register index1, Register index2)
5080 : object_(object), index1_(index1), index2_(index2) {
5081 set_comment("[ DeferredSwapElements");
5082 }
5083
5084 virtual void Generate();
5085
5086 private:
5087 Register object_, index1_, index2_;
5088};
5089
5090
5091void DeferredSwapElements::Generate() {
5092 __ push(object_);
5093 __ push(index1_);
5094 __ push(index2_);
5095 __ CallRuntime(Runtime::kSwapElements, 3);
5096}
5097
5098
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00005099void CodeGenerator::GenerateSwapElements(ZoneList<Expression*>* args) {
5100 Comment cmnt(masm_, "[ GenerateSwapElements");
5101
5102 ASSERT_EQ(3, args->length());
5103
5104 Load(args->at(0));
5105 Load(args->at(1));
5106 Load(args->at(2));
5107
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005108 VirtualFrame::SpilledScope spilled_scope(frame_);
5109
ager@chromium.orgac091b72010-05-05 07:34:42 +00005110 Register index2 = r2;
5111 Register index1 = r1;
5112 Register object = r0;
5113 Register tmp1 = r3;
5114 Register tmp2 = r4;
5115
5116 frame_->EmitPop(index2);
5117 frame_->EmitPop(index1);
5118 frame_->EmitPop(object);
5119
5120 DeferredSwapElements* deferred =
5121 new DeferredSwapElements(object, index1, index2);
5122
5123 // Fetch the map and check if array is in fast case.
5124 // Check that object doesn't require security checks and
5125 // has no indexed interceptor.
5126 __ CompareObjectType(object, tmp1, tmp2, FIRST_JS_OBJECT_TYPE);
5127 deferred->Branch(lt);
5128 __ ldrb(tmp2, FieldMemOperand(tmp1, Map::kBitFieldOffset));
5129 __ tst(tmp2, Operand(KeyedLoadIC::kSlowCaseBitFieldMask));
5130 deferred->Branch(nz);
5131
5132 // Check the object's elements are in fast case.
5133 __ ldr(tmp1, FieldMemOperand(object, JSObject::kElementsOffset));
5134 __ ldr(tmp2, FieldMemOperand(tmp1, HeapObject::kMapOffset));
5135 __ LoadRoot(ip, Heap::kFixedArrayMapRootIndex);
5136 __ cmp(tmp2, ip);
5137 deferred->Branch(ne);
5138
5139 // Smi-tagging is equivalent to multiplying by 2.
5140 STATIC_ASSERT(kSmiTag == 0);
5141 STATIC_ASSERT(kSmiTagSize == 1);
5142
5143 // Check that both indices are smis.
5144 __ mov(tmp2, index1);
5145 __ orr(tmp2, tmp2, index2);
5146 __ tst(tmp2, Operand(kSmiTagMask));
5147 deferred->Branch(nz);
5148
5149 // Bring the offsets into the fixed array in tmp1 into index1 and
5150 // index2.
5151 __ mov(tmp2, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
5152 __ add(index1, tmp2, Operand(index1, LSL, kPointerSizeLog2 - kSmiTagSize));
5153 __ add(index2, tmp2, Operand(index2, LSL, kPointerSizeLog2 - kSmiTagSize));
5154
5155 // Swap elements.
5156 Register tmp3 = object;
5157 object = no_reg;
5158 __ ldr(tmp3, MemOperand(tmp1, index1));
5159 __ ldr(tmp2, MemOperand(tmp1, index2));
5160 __ str(tmp3, MemOperand(tmp1, index2));
5161 __ str(tmp2, MemOperand(tmp1, index1));
5162
5163 Label done;
5164 __ InNewSpace(tmp1, tmp2, eq, &done);
5165 // Possible optimization: do a check that both values are Smis
5166 // (or them and test against Smi mask.)
5167
5168 __ mov(tmp2, tmp1);
5169 RecordWriteStub recordWrite1(tmp1, index1, tmp3);
5170 __ CallStub(&recordWrite1);
5171
5172 RecordWriteStub recordWrite2(tmp2, index2, tmp3);
5173 __ CallStub(&recordWrite2);
5174
5175 __ bind(&done);
5176
5177 deferred->BindExit();
5178 __ LoadRoot(tmp1, Heap::kUndefinedValueRootIndex);
5179 frame_->EmitPush(tmp1);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00005180}
5181
5182
ager@chromium.org357bf652010-04-12 11:30:10 +00005183void CodeGenerator::GenerateCallFunction(ZoneList<Expression*>* args) {
5184 Comment cmnt(masm_, "[ GenerateCallFunction");
5185
5186 ASSERT(args->length() >= 2);
5187
5188 int n_args = args->length() - 2; // for receiver and function.
5189 Load(args->at(0)); // receiver
5190 for (int i = 0; i < n_args; i++) {
5191 Load(args->at(i + 1));
5192 }
5193 Load(args->at(n_args + 1)); // function
5194 frame_->CallJSFunction(n_args);
5195 frame_->EmitPush(r0);
5196}
5197
5198
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005199void CodeGenerator::GenerateMathSin(ZoneList<Expression*>* args) {
5200 ASSERT_EQ(args->length(), 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005201 Load(args->at(0));
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00005202 if (CpuFeatures::IsSupported(VFP3)) {
5203 TranscendentalCacheStub stub(TranscendentalCache::SIN);
5204 frame_->SpillAllButCopyTOSToR0();
5205 frame_->CallStub(&stub, 1);
5206 } else {
5207 frame_->CallRuntime(Runtime::kMath_sin, 1);
5208 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005209 frame_->EmitPush(r0);
5210}
5211
5212
5213void CodeGenerator::GenerateMathCos(ZoneList<Expression*>* args) {
5214 ASSERT_EQ(args->length(), 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005215 Load(args->at(0));
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00005216 if (CpuFeatures::IsSupported(VFP3)) {
5217 TranscendentalCacheStub stub(TranscendentalCache::COS);
5218 frame_->SpillAllButCopyTOSToR0();
5219 frame_->CallStub(&stub, 1);
5220 } else {
5221 frame_->CallRuntime(Runtime::kMath_cos, 1);
5222 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005223 frame_->EmitPush(r0);
5224}
5225
5226
ager@chromium.org7c537e22008-10-16 08:43:32 +00005227void CodeGenerator::GenerateObjectEquals(ZoneList<Expression*>* args) {
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005228 ASSERT(args->length() == 2);
5229
5230 // Load the two objects into registers and perform the comparison.
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00005231 Load(args->at(0));
5232 Load(args->at(1));
5233 Register lhs = frame_->PopToRegister();
5234 Register rhs = frame_->PopToRegister(lhs);
5235 __ cmp(lhs, rhs);
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005236 cc_reg_ = eq;
5237}
5238
5239
ager@chromium.org7c537e22008-10-16 08:43:32 +00005240void CodeGenerator::VisitCallRuntime(CallRuntime* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005241#ifdef DEBUG
5242 int original_height = frame_->height();
5243#endif
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005244 if (CheckForInlineRuntimeCall(node)) {
5245 ASSERT((has_cc() && frame_->height() == original_height) ||
5246 (!has_cc() && frame_->height() == original_height + 1));
5247 return;
5248 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005249
5250 ZoneList<Expression*>* args = node->arguments();
5251 Comment cmnt(masm_, "[ CallRuntime");
5252 Runtime::Function* function = node->function();
5253
ager@chromium.org41826e72009-03-30 13:30:57 +00005254 if (function == NULL) {
mads.s.ager31e71382008-08-13 09:32:07 +00005255 // Prepare stack for calling JS runtime function.
mads.s.ager31e71382008-08-13 09:32:07 +00005256 // Push the builtins object found in the current global object.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005257 Register scratch = VirtualFrame::scratch0();
5258 __ ldr(scratch, GlobalObject());
5259 Register builtins = frame_->GetTOSRegister();
5260 __ ldr(builtins, FieldMemOperand(scratch, GlobalObject::kBuiltinsOffset));
5261 frame_->EmitPush(builtins);
ager@chromium.org41826e72009-03-30 13:30:57 +00005262 }
mads.s.ager31e71382008-08-13 09:32:07 +00005263
ager@chromium.org41826e72009-03-30 13:30:57 +00005264 // Push the arguments ("left-to-right").
5265 int arg_count = args->length();
5266 for (int i = 0; i < arg_count; i++) {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005267 Load(args->at(i));
ager@chromium.org41826e72009-03-30 13:30:57 +00005268 }
mads.s.ager31e71382008-08-13 09:32:07 +00005269
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005270 VirtualFrame::SpilledScope spilled_scope(frame_);
5271
ager@chromium.org41826e72009-03-30 13:30:57 +00005272 if (function == NULL) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005273 // Call the JS runtime function.
ager@chromium.org5c838252010-02-19 08:53:10 +00005274 __ mov(r2, Operand(node->name()));
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005275 InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP;
5276 Handle<Code> stub = ComputeCallInitialize(arg_count, in_loop);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005277 frame_->CallCodeObject(stub, RelocInfo::CODE_TARGET, arg_count + 1);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005278 __ ldr(cp, frame_->Context());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005279 frame_->EmitPush(r0);
ager@chromium.org41826e72009-03-30 13:30:57 +00005280 } else {
5281 // Call the C runtime function.
5282 frame_->CallRuntime(function, arg_count);
5283 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005284 }
ager@chromium.orgac091b72010-05-05 07:34:42 +00005285 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005286}
5287
5288
ager@chromium.org7c537e22008-10-16 08:43:32 +00005289void CodeGenerator::VisitUnaryOperation(UnaryOperation* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005290#ifdef DEBUG
5291 int original_height = frame_->height();
5292#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005293 Comment cmnt(masm_, "[ UnaryOperation");
5294
5295 Token::Value op = node->op();
5296
5297 if (op == Token::NOT) {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005298 LoadCondition(node->expression(), false_target(), true_target(), true);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00005299 // LoadCondition may (and usually does) leave a test and branch to
5300 // be emitted by the caller. In that case, negate the condition.
5301 if (has_cc()) cc_reg_ = NegateCondition(cc_reg_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005302
5303 } else if (op == Token::DELETE) {
5304 Property* property = node->expression()->AsProperty();
mads.s.ager31e71382008-08-13 09:32:07 +00005305 Variable* variable = node->expression()->AsVariableProxy()->AsVariable();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005306 if (property != NULL) {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005307 Load(property->obj());
5308 Load(property->key());
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00005309 frame_->InvokeBuiltin(Builtins::DELETE, CALL_JS, 2);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005310 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005311
mads.s.ager31e71382008-08-13 09:32:07 +00005312 } else if (variable != NULL) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005313 Slot* slot = variable->slot();
5314 if (variable->is_global()) {
5315 LoadGlobal();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005316 frame_->EmitPush(Operand(variable->name()));
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00005317 frame_->InvokeBuiltin(Builtins::DELETE, CALL_JS, 2);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005318 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005319
5320 } else if (slot != NULL && slot->type() == Slot::LOOKUP) {
5321 // lookup the context holding the named variable
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005322 frame_->EmitPush(cp);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005323 frame_->EmitPush(Operand(variable->name()));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005324 frame_->CallRuntime(Runtime::kLookupContext, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005325 // r0: context
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005326 frame_->EmitPush(r0);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005327 frame_->EmitPush(Operand(variable->name()));
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00005328 frame_->InvokeBuiltin(Builtins::DELETE, CALL_JS, 2);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005329 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005330
mads.s.ager31e71382008-08-13 09:32:07 +00005331 } else {
5332 // Default: Result of deleting non-global, not dynamically
5333 // introduced variables is false.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005334 frame_->EmitPushRoot(Heap::kFalseValueRootIndex);
mads.s.ager31e71382008-08-13 09:32:07 +00005335 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005336
5337 } else {
5338 // Default: Result of deleting expressions is true.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005339 Load(node->expression()); // may have side-effects
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005340 frame_->Drop();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005341 frame_->EmitPushRoot(Heap::kTrueValueRootIndex);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005342 }
5343
5344 } else if (op == Token::TYPEOF) {
5345 // Special case for loading the typeof expression; see comment on
5346 // LoadTypeofExpression().
5347 LoadTypeofExpression(node->expression());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005348 frame_->CallRuntime(Runtime::kTypeof, 1);
5349 frame_->EmitPush(r0); // r0 has result
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005350
5351 } else {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005352 bool overwrite =
5353 (node->expression()->AsBinaryOperation() != NULL &&
5354 node->expression()->AsBinaryOperation()->ResultOverwriteAllowed());
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005355 Load(node->expression());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005356 switch (op) {
5357 case Token::NOT:
5358 case Token::DELETE:
5359 case Token::TYPEOF:
5360 UNREACHABLE(); // handled above
5361 break;
5362
5363 case Token::SUB: {
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00005364 frame_->PopToR0();
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005365 GenericUnaryOpStub stub(Token::SUB, overwrite);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005366 frame_->CallStub(&stub, 0);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005367 frame_->EmitPush(r0); // r0 has result
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005368 break;
5369 }
5370
5371 case Token::BIT_NOT: {
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00005372 Register tos = frame_->PopToRegister();
5373 JumpTarget not_smi_label;
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005374 JumpTarget continue_label;
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00005375 // Smi check.
5376 __ tst(tos, Operand(kSmiTagMask));
5377 not_smi_label.Branch(ne);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005378
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00005379 __ mvn(tos, Operand(tos));
5380 __ bic(tos, tos, Operand(kSmiTagMask)); // Bit-clear inverted smi-tag.
5381 frame_->EmitPush(tos);
5382 // The fast case is the first to jump to the continue label, so it gets
5383 // to decide the virtual frame layout.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005384 continue_label.Jump();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005385
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00005386 not_smi_label.Bind();
5387 frame_->SpillAll();
5388 __ Move(r0, tos);
5389 GenericUnaryOpStub stub(Token::BIT_NOT, overwrite);
5390 frame_->CallStub(&stub, 0);
5391 frame_->EmitPush(r0);
5392
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005393 continue_label.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005394 break;
5395 }
5396
5397 case Token::VOID:
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005398 frame_->Drop();
5399 frame_->EmitPushRoot(Heap::kUndefinedValueRootIndex);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005400 break;
5401
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005402 case Token::ADD: {
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00005403 Register tos = frame_->Peek();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005404 // Smi check.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005405 JumpTarget continue_label;
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00005406 __ tst(tos, Operand(kSmiTagMask));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005407 continue_label.Branch(eq);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00005408
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00005409 frame_->InvokeBuiltin(Builtins::TO_NUMBER, CALL_JS, 1);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00005410 frame_->EmitPush(r0);
5411
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005412 continue_label.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005413 break;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005414 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005415 default:
5416 UNREACHABLE();
5417 }
5418 }
kasperl@chromium.orge959c182009-07-27 08:59:04 +00005419 ASSERT(!has_valid_frame() ||
5420 (has_cc() && frame_->height() == original_height) ||
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005421 (!has_cc() && frame_->height() == original_height + 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005422}
5423
5424
ager@chromium.org7c537e22008-10-16 08:43:32 +00005425void CodeGenerator::VisitCountOperation(CountOperation* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005426#ifdef DEBUG
5427 int original_height = frame_->height();
5428#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005429 Comment cmnt(masm_, "[ CountOperation");
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00005430 VirtualFrame::RegisterAllocationScope scope(this);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005431
5432 bool is_postfix = node->is_postfix();
5433 bool is_increment = node->op() == Token::INC;
5434
5435 Variable* var = node->expression()->AsVariableProxy()->AsVariable();
5436 bool is_const = (var != NULL && var->mode() == Variable::CONST);
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00005437 bool is_slot = (var != NULL && var->mode() == Variable::VAR);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005438
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00005439 if (!is_const && is_slot && type_info(var->slot()).IsSmi()) {
5440 // The type info declares that this variable is always a Smi. That
5441 // means it is a Smi both before and after the increment/decrement.
5442 // Lets make use of that to make a very minimal count.
5443 Reference target(this, node->expression(), !is_const);
5444 ASSERT(!target.is_illegal());
5445 target.GetValue(); // Pushes the value.
5446 Register value = frame_->PopToRegister();
5447 if (is_postfix) frame_->EmitPush(value);
5448 if (is_increment) {
5449 __ add(value, value, Operand(Smi::FromInt(1)));
5450 } else {
5451 __ sub(value, value, Operand(Smi::FromInt(1)));
5452 }
5453 frame_->EmitPush(value);
5454 target.SetValue(NOT_CONST_INIT);
5455 if (is_postfix) frame_->Pop();
5456 ASSERT_EQ(original_height + 1, frame_->height());
5457 return;
5458 }
5459
5460 // If it's a postfix expression and its result is not ignored and the
5461 // reference is non-trivial, then push a placeholder on the stack now
5462 // to hold the result of the expression.
5463 bool placeholder_pushed = false;
5464 if (!is_slot && is_postfix) {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005465 frame_->EmitPush(Operand(Smi::FromInt(0)));
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00005466 placeholder_pushed = true;
mads.s.ager31e71382008-08-13 09:32:07 +00005467 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005468
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005469 // A constant reference is not saved to, so a constant reference is not a
5470 // compound assignment reference.
5471 { Reference target(this, node->expression(), !is_const);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005472 if (target.is_illegal()) {
5473 // Spoof the virtual frame to have the expected height (one higher
5474 // than on entry).
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00005475 if (!placeholder_pushed) frame_->EmitPush(Operand(Smi::FromInt(0)));
ager@chromium.orgac091b72010-05-05 07:34:42 +00005476 ASSERT_EQ(original_height + 1, frame_->height());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005477 return;
5478 }
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00005479
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005480 // This pushes 0, 1 or 2 words on the object to be used later when updating
5481 // the target. It also pushes the current value of the target.
ager@chromium.org357bf652010-04-12 11:30:10 +00005482 target.GetValue();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005483
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005484 JumpTarget slow;
5485 JumpTarget exit;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005486
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005487 Register value = frame_->PopToRegister();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005488
5489 // Postfix: Store the old value as the result.
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00005490 if (placeholder_pushed) {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005491 frame_->SetElementAt(value, target.size());
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00005492 } else if (is_postfix) {
5493 frame_->EmitPush(value);
5494 __ mov(VirtualFrame::scratch0(), value);
5495 value = VirtualFrame::scratch0();
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005496 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005497
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00005498 // Check for smi operand.
5499 __ tst(value, Operand(kSmiTagMask));
5500 slow.Branch(ne);
5501
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005502 // Perform optimistic increment/decrement.
5503 if (is_increment) {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005504 __ add(value, value, Operand(Smi::FromInt(1)), SetCC);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005505 } else {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005506 __ sub(value, value, Operand(Smi::FromInt(1)), SetCC);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005507 }
5508
5509 // If the increment/decrement didn't overflow, we're done.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005510 exit.Branch(vc);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005511
5512 // Revert optimistic increment/decrement.
5513 if (is_increment) {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005514 __ sub(value, value, Operand(Smi::FromInt(1)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005515 } else {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005516 __ add(value, value, Operand(Smi::FromInt(1)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005517 }
5518
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005519 // Slow case: Convert to number. At this point the
5520 // value to be incremented is in the value register..
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005521 slow.Bind();
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005522
5523 // Convert the operand to a number.
5524 frame_->EmitPush(value);
5525
kasperl@chromium.org8ccb0be2009-04-07 07:21:39 +00005526 {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005527 VirtualFrame::SpilledScope spilled(frame_);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00005528 frame_->InvokeBuiltin(Builtins::TO_NUMBER, CALL_JS, 1);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005529
5530 if (is_postfix) {
5531 // Postfix: store to result (on the stack).
5532 __ str(r0, frame_->ElementAt(target.size()));
5533 }
5534
5535 // Compute the new value.
5536 frame_->EmitPush(r0);
5537 frame_->EmitPush(Operand(Smi::FromInt(1)));
5538 if (is_increment) {
5539 frame_->CallRuntime(Runtime::kNumberAdd, 2);
5540 } else {
5541 frame_->CallRuntime(Runtime::kNumberSub, 2);
5542 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005543 }
5544
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005545 __ Move(value, r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005546 // Store the new value in the target if not const.
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005547 // At this point the answer is in the value register.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005548 exit.Bind();
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005549 frame_->EmitPush(value);
5550 // Set the target with the result, leaving the result on
5551 // top of the stack. Removes the target from the stack if
5552 // it has a non-zero size.
ager@chromium.org7c537e22008-10-16 08:43:32 +00005553 if (!is_const) target.SetValue(NOT_CONST_INIT);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005554 }
5555
5556 // Postfix: Discard the new value and use the old.
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005557 if (is_postfix) frame_->Pop();
ager@chromium.orgac091b72010-05-05 07:34:42 +00005558 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005559}
5560
5561
ager@chromium.orgb26c50a2010-03-26 09:27:16 +00005562void CodeGenerator::GenerateLogicalBooleanOperation(BinaryOperation* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005563 // According to ECMA-262 section 11.11, page 58, the binary logical
5564 // operators must yield the result of one of the two expressions
5565 // before any ToBoolean() conversions. This means that the value
5566 // produced by a && or || operator is not necessarily a boolean.
5567
5568 // NOTE: If the left hand side produces a materialized value (not in
5569 // the CC register), we force the right hand side to do the
5570 // same. This is necessary because we may have to branch to the exit
5571 // after evaluating the left hand side (due to the shortcut
5572 // semantics), but the compiler must (statically) know if the result
5573 // of compiling the binary operation is materialized or not.
ager@chromium.orgb26c50a2010-03-26 09:27:16 +00005574 if (node->op() == Token::AND) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005575 JumpTarget is_true;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005576 LoadCondition(node->left(), &is_true, false_target(), false);
kasperl@chromium.orge959c182009-07-27 08:59:04 +00005577 if (has_valid_frame() && !has_cc()) {
5578 // The left-hand side result is on top of the virtual frame.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005579 JumpTarget pop_and_continue;
5580 JumpTarget exit;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005581
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00005582 frame_->Dup();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005583 // Avoid popping the result if it converts to 'false' using the
5584 // standard ToBoolean() conversion as described in ECMA-262,
5585 // section 9.2, page 30.
mads.s.ager31e71382008-08-13 09:32:07 +00005586 ToBoolean(&pop_and_continue, &exit);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005587 Branch(false, &exit);
5588
5589 // Pop the result of evaluating the first part.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005590 pop_and_continue.Bind();
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00005591 frame_->Pop();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005592
5593 // Evaluate right side expression.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005594 is_true.Bind();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005595 Load(node->right());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005596
5597 // Exit (always with a materialized value).
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005598 exit.Bind();
kasperl@chromium.orge959c182009-07-27 08:59:04 +00005599 } else if (has_cc() || is_true.is_linked()) {
5600 // The left-hand side is either (a) partially compiled to
5601 // control flow with a final branch left to emit or (b) fully
5602 // compiled to control flow and possibly true.
5603 if (has_cc()) {
5604 Branch(false, false_target());
5605 }
5606 is_true.Bind();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005607 LoadCondition(node->right(), true_target(), false_target(), false);
kasperl@chromium.orge959c182009-07-27 08:59:04 +00005608 } else {
5609 // Nothing to do.
5610 ASSERT(!has_valid_frame() && !has_cc() && !is_true.is_linked());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005611 }
5612
ager@chromium.orgb26c50a2010-03-26 09:27:16 +00005613 } else {
5614 ASSERT(node->op() == Token::OR);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005615 JumpTarget is_false;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005616 LoadCondition(node->left(), true_target(), &is_false, false);
kasperl@chromium.orge959c182009-07-27 08:59:04 +00005617 if (has_valid_frame() && !has_cc()) {
5618 // The left-hand side result is on top of the virtual frame.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005619 JumpTarget pop_and_continue;
5620 JumpTarget exit;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005621
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00005622 frame_->Dup();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005623 // Avoid popping the result if it converts to 'true' using the
5624 // standard ToBoolean() conversion as described in ECMA-262,
5625 // section 9.2, page 30.
mads.s.ager31e71382008-08-13 09:32:07 +00005626 ToBoolean(&exit, &pop_and_continue);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005627 Branch(true, &exit);
5628
5629 // Pop the result of evaluating the first part.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005630 pop_and_continue.Bind();
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00005631 frame_->Pop();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005632
5633 // Evaluate right side expression.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005634 is_false.Bind();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005635 Load(node->right());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005636
5637 // Exit (always with a materialized value).
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005638 exit.Bind();
kasperl@chromium.orge959c182009-07-27 08:59:04 +00005639 } else if (has_cc() || is_false.is_linked()) {
5640 // The left-hand side is either (a) partially compiled to
5641 // control flow with a final branch left to emit or (b) fully
5642 // compiled to control flow and possibly false.
5643 if (has_cc()) {
5644 Branch(true, true_target());
5645 }
5646 is_false.Bind();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005647 LoadCondition(node->right(), true_target(), false_target(), false);
kasperl@chromium.orge959c182009-07-27 08:59:04 +00005648 } else {
5649 // Nothing to do.
5650 ASSERT(!has_valid_frame() && !has_cc() && !is_false.is_linked());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005651 }
ager@chromium.orgb26c50a2010-03-26 09:27:16 +00005652 }
5653}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005654
ager@chromium.orgb26c50a2010-03-26 09:27:16 +00005655
5656void CodeGenerator::VisitBinaryOperation(BinaryOperation* node) {
5657#ifdef DEBUG
5658 int original_height = frame_->height();
5659#endif
ager@chromium.orgb26c50a2010-03-26 09:27:16 +00005660 Comment cmnt(masm_, "[ BinaryOperation");
5661
5662 if (node->op() == Token::AND || node->op() == Token::OR) {
5663 GenerateLogicalBooleanOperation(node);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005664 } else {
5665 // Optimize for the case where (at least) one of the expressions
5666 // is a literal small integer.
5667 Literal* lliteral = node->left()->AsLiteral();
5668 Literal* rliteral = node->right()->AsLiteral();
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005669 // NOTE: The code below assumes that the slow cases (calls to runtime)
5670 // never return a constant/immutable object.
5671 bool overwrite_left =
5672 (node->left()->AsBinaryOperation() != NULL &&
5673 node->left()->AsBinaryOperation()->ResultOverwriteAllowed());
5674 bool overwrite_right =
5675 (node->right()->AsBinaryOperation() != NULL &&
5676 node->right()->AsBinaryOperation()->ResultOverwriteAllowed());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005677
5678 if (rliteral != NULL && rliteral->handle()->IsSmi()) {
ager@chromium.org357bf652010-04-12 11:30:10 +00005679 VirtualFrame::RegisterAllocationScope scope(this);
5680 Load(node->left());
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00005681 if (frame_->KnownSmiAt(0)) overwrite_left = false;
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00005682 SmiOperation(node->op(),
5683 rliteral->handle(),
5684 false,
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00005685 overwrite_left ? OVERWRITE_LEFT : NO_OVERWRITE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005686 } else if (lliteral != NULL && lliteral->handle()->IsSmi()) {
ager@chromium.org357bf652010-04-12 11:30:10 +00005687 VirtualFrame::RegisterAllocationScope scope(this);
5688 Load(node->right());
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00005689 if (frame_->KnownSmiAt(0)) overwrite_right = false;
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00005690 SmiOperation(node->op(),
5691 lliteral->handle(),
5692 true,
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00005693 overwrite_right ? OVERWRITE_RIGHT : NO_OVERWRITE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005694 } else {
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00005695 GenerateInlineSmi inline_smi =
5696 loop_nesting() > 0 ? GENERATE_INLINE_SMI : DONT_GENERATE_INLINE_SMI;
5697 if (lliteral != NULL) {
5698 ASSERT(!lliteral->handle()->IsSmi());
5699 inline_smi = DONT_GENERATE_INLINE_SMI;
5700 }
5701 if (rliteral != NULL) {
5702 ASSERT(!rliteral->handle()->IsSmi());
5703 inline_smi = DONT_GENERATE_INLINE_SMI;
5704 }
ager@chromium.org357bf652010-04-12 11:30:10 +00005705 VirtualFrame::RegisterAllocationScope scope(this);
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005706 OverwriteMode overwrite_mode = NO_OVERWRITE;
5707 if (overwrite_left) {
5708 overwrite_mode = OVERWRITE_LEFT;
5709 } else if (overwrite_right) {
5710 overwrite_mode = OVERWRITE_RIGHT;
5711 }
ager@chromium.org357bf652010-04-12 11:30:10 +00005712 Load(node->left());
5713 Load(node->right());
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00005714 GenericBinaryOperation(node->op(), overwrite_mode, inline_smi);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005715 }
5716 }
kasperl@chromium.orge959c182009-07-27 08:59:04 +00005717 ASSERT(!has_valid_frame() ||
5718 (has_cc() && frame_->height() == original_height) ||
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005719 (!has_cc() && frame_->height() == original_height + 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005720}
5721
5722
ager@chromium.org7c537e22008-10-16 08:43:32 +00005723void CodeGenerator::VisitThisFunction(ThisFunction* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005724#ifdef DEBUG
5725 int original_height = frame_->height();
5726#endif
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00005727 frame_->EmitPush(MemOperand(frame_->Function()));
ager@chromium.orgac091b72010-05-05 07:34:42 +00005728 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005729}
5730
5731
ager@chromium.org7c537e22008-10-16 08:43:32 +00005732void CodeGenerator::VisitCompareOperation(CompareOperation* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005733#ifdef DEBUG
5734 int original_height = frame_->height();
5735#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005736 Comment cmnt(masm_, "[ CompareOperation");
5737
ager@chromium.org357bf652010-04-12 11:30:10 +00005738 VirtualFrame::RegisterAllocationScope nonspilled_scope(this);
5739
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005740 // Get the expressions from the node.
5741 Expression* left = node->left();
5742 Expression* right = node->right();
5743 Token::Value op = node->op();
5744
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005745 // To make null checks efficient, we check if either left or right is the
5746 // literal 'null'. If so, we optimize the code by inlining a null check
5747 // instead of calling the (very) general runtime routine for checking
5748 // equality.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005749 if (op == Token::EQ || op == Token::EQ_STRICT) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005750 bool left_is_null =
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005751 left->AsLiteral() != NULL && left->AsLiteral()->IsNull();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005752 bool right_is_null =
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005753 right->AsLiteral() != NULL && right->AsLiteral()->IsNull();
5754 // The 'null' value can only be equal to 'null' or 'undefined'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005755 if (left_is_null || right_is_null) {
ager@chromium.org357bf652010-04-12 11:30:10 +00005756 Load(left_is_null ? right : left);
5757 Register tos = frame_->PopToRegister();
ager@chromium.orgab99eea2009-08-25 07:05:41 +00005758 __ LoadRoot(ip, Heap::kNullValueRootIndex);
ager@chromium.org357bf652010-04-12 11:30:10 +00005759 __ cmp(tos, ip);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005760
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005761 // The 'null' value is only equal to 'undefined' if using non-strict
5762 // comparisons.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005763 if (op != Token::EQ_STRICT) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005764 true_target()->Branch(eq);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005765
ager@chromium.orgab99eea2009-08-25 07:05:41 +00005766 __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
ager@chromium.org357bf652010-04-12 11:30:10 +00005767 __ cmp(tos, Operand(ip));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005768 true_target()->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005769
ager@chromium.org357bf652010-04-12 11:30:10 +00005770 __ tst(tos, Operand(kSmiTagMask));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005771 false_target()->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005772
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005773 // It can be an undetectable object.
ager@chromium.org357bf652010-04-12 11:30:10 +00005774 __ ldr(tos, FieldMemOperand(tos, HeapObject::kMapOffset));
5775 __ ldrb(tos, FieldMemOperand(tos, Map::kBitFieldOffset));
5776 __ and_(tos, tos, Operand(1 << Map::kIsUndetectable));
5777 __ cmp(tos, Operand(1 << Map::kIsUndetectable));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005778 }
5779
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005780 cc_reg_ = eq;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005781 ASSERT(has_cc() && frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005782 return;
5783 }
5784 }
5785
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005786 // To make typeof testing for natives implemented in JavaScript really
5787 // efficient, we generate special code for expressions of the form:
5788 // 'typeof <expression> == <string>'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005789 UnaryOperation* operation = left->AsUnaryOperation();
5790 if ((op == Token::EQ || op == Token::EQ_STRICT) &&
5791 (operation != NULL && operation->op() == Token::TYPEOF) &&
5792 (right->AsLiteral() != NULL &&
5793 right->AsLiteral()->handle()->IsString())) {
5794 Handle<String> check(String::cast(*right->AsLiteral()->handle()));
5795
ager@chromium.org357bf652010-04-12 11:30:10 +00005796 // Load the operand, move it to a register.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005797 LoadTypeofExpression(operation->expression());
ager@chromium.org357bf652010-04-12 11:30:10 +00005798 Register tos = frame_->PopToRegister();
5799
ager@chromium.org357bf652010-04-12 11:30:10 +00005800 Register scratch = VirtualFrame::scratch0();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005801
5802 if (check->Equals(Heap::number_symbol())) {
ager@chromium.org357bf652010-04-12 11:30:10 +00005803 __ tst(tos, Operand(kSmiTagMask));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005804 true_target()->Branch(eq);
ager@chromium.org357bf652010-04-12 11:30:10 +00005805 __ ldr(tos, FieldMemOperand(tos, HeapObject::kMapOffset));
ager@chromium.orgab99eea2009-08-25 07:05:41 +00005806 __ LoadRoot(ip, Heap::kHeapNumberMapRootIndex);
ager@chromium.org357bf652010-04-12 11:30:10 +00005807 __ cmp(tos, ip);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005808 cc_reg_ = eq;
5809
5810 } else if (check->Equals(Heap::string_symbol())) {
ager@chromium.org357bf652010-04-12 11:30:10 +00005811 __ tst(tos, Operand(kSmiTagMask));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005812 false_target()->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005813
ager@chromium.org357bf652010-04-12 11:30:10 +00005814 __ ldr(tos, FieldMemOperand(tos, HeapObject::kMapOffset));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005815
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005816 // It can be an undetectable string object.
ager@chromium.org357bf652010-04-12 11:30:10 +00005817 __ ldrb(scratch, FieldMemOperand(tos, Map::kBitFieldOffset));
5818 __ and_(scratch, scratch, Operand(1 << Map::kIsUndetectable));
5819 __ cmp(scratch, Operand(1 << Map::kIsUndetectable));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005820 false_target()->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005821
ager@chromium.org357bf652010-04-12 11:30:10 +00005822 __ ldrb(scratch, FieldMemOperand(tos, Map::kInstanceTypeOffset));
5823 __ cmp(scratch, Operand(FIRST_NONSTRING_TYPE));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005824 cc_reg_ = lt;
5825
5826 } else if (check->Equals(Heap::boolean_symbol())) {
ager@chromium.orgab99eea2009-08-25 07:05:41 +00005827 __ LoadRoot(ip, Heap::kTrueValueRootIndex);
ager@chromium.org357bf652010-04-12 11:30:10 +00005828 __ cmp(tos, ip);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005829 true_target()->Branch(eq);
ager@chromium.orgab99eea2009-08-25 07:05:41 +00005830 __ LoadRoot(ip, Heap::kFalseValueRootIndex);
ager@chromium.org357bf652010-04-12 11:30:10 +00005831 __ cmp(tos, ip);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005832 cc_reg_ = eq;
5833
5834 } else if (check->Equals(Heap::undefined_symbol())) {
ager@chromium.orgab99eea2009-08-25 07:05:41 +00005835 __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
ager@chromium.org357bf652010-04-12 11:30:10 +00005836 __ cmp(tos, ip);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005837 true_target()->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005838
ager@chromium.org357bf652010-04-12 11:30:10 +00005839 __ tst(tos, Operand(kSmiTagMask));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005840 false_target()->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005841
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005842 // It can be an undetectable object.
ager@chromium.org357bf652010-04-12 11:30:10 +00005843 __ ldr(tos, FieldMemOperand(tos, HeapObject::kMapOffset));
5844 __ ldrb(scratch, FieldMemOperand(tos, Map::kBitFieldOffset));
5845 __ and_(scratch, scratch, Operand(1 << Map::kIsUndetectable));
5846 __ cmp(scratch, Operand(1 << Map::kIsUndetectable));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005847
5848 cc_reg_ = eq;
5849
5850 } else if (check->Equals(Heap::function_symbol())) {
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);
ager@chromium.org357bf652010-04-12 11:30:10 +00005853 Register map_reg = scratch;
5854 __ CompareObjectType(tos, map_reg, tos, JS_FUNCTION_TYPE);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005855 true_target()->Branch(eq);
5856 // Regular expressions are callable so typeof == 'function'.
ager@chromium.org357bf652010-04-12 11:30:10 +00005857 __ CompareInstanceType(map_reg, tos, JS_REGEXP_TYPE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005858 cc_reg_ = eq;
5859
5860 } else if (check->Equals(Heap::object_symbol())) {
ager@chromium.org357bf652010-04-12 11:30:10 +00005861 __ tst(tos, Operand(kSmiTagMask));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005862 false_target()->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005863
ager@chromium.orgab99eea2009-08-25 07:05:41 +00005864 __ LoadRoot(ip, Heap::kNullValueRootIndex);
ager@chromium.org357bf652010-04-12 11:30:10 +00005865 __ cmp(tos, ip);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005866 true_target()->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005867
ager@chromium.org357bf652010-04-12 11:30:10 +00005868 Register map_reg = scratch;
5869 __ CompareObjectType(tos, map_reg, tos, JS_REGEXP_TYPE);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005870 false_target()->Branch(eq);
5871
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005872 // It can be an undetectable object.
ager@chromium.org357bf652010-04-12 11:30:10 +00005873 __ ldrb(tos, FieldMemOperand(map_reg, Map::kBitFieldOffset));
5874 __ and_(tos, tos, Operand(1 << Map::kIsUndetectable));
5875 __ cmp(tos, Operand(1 << Map::kIsUndetectable));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005876 false_target()->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005877
ager@chromium.org357bf652010-04-12 11:30:10 +00005878 __ ldrb(tos, FieldMemOperand(map_reg, Map::kInstanceTypeOffset));
5879 __ cmp(tos, Operand(FIRST_JS_OBJECT_TYPE));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005880 false_target()->Branch(lt);
ager@chromium.org357bf652010-04-12 11:30:10 +00005881 __ cmp(tos, Operand(LAST_JS_OBJECT_TYPE));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005882 cc_reg_ = le;
5883
5884 } else {
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005885 // Uncommon case: typeof testing against a string literal that is
5886 // never returned from the typeof operator.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005887 false_target()->Jump();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005888 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005889 ASSERT(!has_valid_frame() ||
5890 (has_cc() && frame_->height() == original_height));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005891 return;
5892 }
5893
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005894 switch (op) {
5895 case Token::EQ:
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005896 Comparison(eq, left, right, false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005897 break;
5898
5899 case Token::LT:
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005900 Comparison(lt, left, right);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005901 break;
5902
5903 case Token::GT:
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005904 Comparison(gt, left, right);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005905 break;
5906
5907 case Token::LTE:
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005908 Comparison(le, left, right);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005909 break;
5910
5911 case Token::GTE:
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005912 Comparison(ge, left, right);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005913 break;
5914
5915 case Token::EQ_STRICT:
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005916 Comparison(eq, left, right, true);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005917 break;
5918
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005919 case Token::IN: {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005920 Load(left);
5921 Load(right);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00005922 frame_->InvokeBuiltin(Builtins::IN, CALL_JS, 2);
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00005923 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005924 break;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005925 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005926
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005927 case Token::INSTANCEOF: {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005928 Load(left);
5929 Load(right);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00005930 InstanceofStub stub;
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00005931 frame_->CallStub(&stub, 2);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00005932 // At this point if instanceof succeeded then r0 == 0.
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00005933 __ tst(r0, Operand(r0));
ager@chromium.org7c537e22008-10-16 08:43:32 +00005934 cc_reg_ = eq;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005935 break;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005936 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005937
5938 default:
5939 UNREACHABLE();
5940 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005941 ASSERT((has_cc() && frame_->height() == original_height) ||
5942 (!has_cc() && frame_->height() == original_height + 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005943}
5944
5945
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005946class DeferredReferenceGetNamedValue: public DeferredCode {
5947 public:
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005948 explicit DeferredReferenceGetNamedValue(Register receiver,
5949 Handle<String> name)
5950 : receiver_(receiver), name_(name) {
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005951 set_comment("[ DeferredReferenceGetNamedValue");
5952 }
5953
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005954 virtual void Generate();
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005955
5956 private:
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005957 Register receiver_;
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005958 Handle<String> name_;
5959};
5960
5961
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005962// Convention for this is that on entry the receiver is in a register that
5963// is not used by the stack. On exit the answer is found in that same
5964// register and the stack has the same height.
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005965void DeferredReferenceGetNamedValue::Generate() {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005966#ifdef DEBUG
5967 int expected_height = frame_state()->frame()->height();
5968#endif
5969 VirtualFrame copied_frame(*frame_state()->frame());
5970 copied_frame.SpillAll();
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005971
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005972 Register scratch1 = VirtualFrame::scratch0();
5973 Register scratch2 = VirtualFrame::scratch1();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005974 ASSERT(!receiver_.is(scratch1) && !receiver_.is(scratch2));
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005975 __ DecrementCounter(&Counters::named_load_inline, 1, scratch1, scratch2);
5976 __ IncrementCounter(&Counters::named_load_inline_miss, 1, scratch1, scratch2);
5977
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005978 // Ensure receiver in r0 and name in r2 to match load ic calling convention.
5979 __ Move(r0, receiver_);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005980 __ mov(r2, Operand(name_));
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005981
5982 // The rest of the instructions in the deferred code must be together.
5983 { Assembler::BlockConstPoolScope block_const_pool(masm_);
5984 Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize));
5985 __ Call(ic, RelocInfo::CODE_TARGET);
5986 // The call must be followed by a nop(1) instruction to indicate that the
5987 // in-object has been inlined.
5988 __ nop(PROPERTY_ACCESS_INLINED);
5989
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005990 // At this point the answer is in r0. We move it to the expected register
5991 // if necessary.
5992 __ Move(receiver_, r0);
5993
5994 // Now go back to the frame that we entered with. This will not overwrite
5995 // the receiver register since that register was not in use when we came
5996 // in. The instructions emitted by this merge are skipped over by the
5997 // inline load patching mechanism when looking for the branch instruction
5998 // that tells it where the code to patch is.
5999 copied_frame.MergeTo(frame_state()->frame());
6000
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006001 // Block the constant pool for one more instruction after leaving this
6002 // constant pool block scope to include the branch instruction ending the
6003 // deferred code.
6004 __ BlockConstPoolFor(1);
6005 }
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006006 ASSERT_EQ(expected_height, frame_state()->frame()->height());
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006007}
6008
6009
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006010class DeferredReferenceGetKeyedValue: public DeferredCode {
6011 public:
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00006012 DeferredReferenceGetKeyedValue(Register key, Register receiver)
6013 : key_(key), receiver_(receiver) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006014 set_comment("[ DeferredReferenceGetKeyedValue");
6015 }
6016
6017 virtual void Generate();
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00006018
6019 private:
6020 Register key_;
6021 Register receiver_;
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006022};
6023
6024
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00006025// Takes key and register in r0 and r1 or vice versa. Returns result
6026// in r0.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006027void DeferredReferenceGetKeyedValue::Generate() {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00006028 ASSERT((key_.is(r0) && receiver_.is(r1)) ||
6029 (key_.is(r1) && receiver_.is(r0)));
6030
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00006031 VirtualFrame copied_frame(*frame_state()->frame());
6032 copied_frame.SpillAll();
6033
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006034 Register scratch1 = VirtualFrame::scratch0();
6035 Register scratch2 = VirtualFrame::scratch1();
6036 __ DecrementCounter(&Counters::keyed_load_inline, 1, scratch1, scratch2);
6037 __ IncrementCounter(&Counters::keyed_load_inline_miss, 1, scratch1, scratch2);
6038
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00006039 // Ensure key in r0 and receiver in r1 to match keyed load ic calling
6040 // convention.
6041 if (key_.is(r1)) {
6042 __ Swap(r0, r1, ip);
6043 }
6044
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006045 // The rest of the instructions in the deferred code must be together.
6046 { Assembler::BlockConstPoolScope block_const_pool(masm_);
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00006047 // Call keyed load IC. It has the arguments key and receiver in r0 and r1.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006048 Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
6049 __ Call(ic, RelocInfo::CODE_TARGET);
6050 // The call must be followed by a nop instruction to indicate that the
6051 // keyed load has been inlined.
6052 __ nop(PROPERTY_ACCESS_INLINED);
6053
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00006054 // Now go back to the frame that we entered with. This will not overwrite
6055 // the receiver or key registers since they were not in use when we came
6056 // in. The instructions emitted by this merge are skipped over by the
6057 // inline load patching mechanism when looking for the branch instruction
6058 // that tells it where the code to patch is.
6059 copied_frame.MergeTo(frame_state()->frame());
6060
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006061 // Block the constant pool for one more instruction after leaving this
6062 // constant pool block scope to include the branch instruction ending the
6063 // deferred code.
6064 __ BlockConstPoolFor(1);
6065 }
6066}
6067
6068
6069class DeferredReferenceSetKeyedValue: public DeferredCode {
6070 public:
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006071 DeferredReferenceSetKeyedValue(Register value,
6072 Register key,
6073 Register receiver)
6074 : value_(value), key_(key), receiver_(receiver) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006075 set_comment("[ DeferredReferenceSetKeyedValue");
6076 }
6077
6078 virtual void Generate();
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006079
6080 private:
6081 Register value_;
6082 Register key_;
6083 Register receiver_;
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006084};
6085
6086
6087void DeferredReferenceSetKeyedValue::Generate() {
6088 Register scratch1 = VirtualFrame::scratch0();
6089 Register scratch2 = VirtualFrame::scratch1();
6090 __ DecrementCounter(&Counters::keyed_store_inline, 1, scratch1, scratch2);
6091 __ IncrementCounter(
6092 &Counters::keyed_store_inline_miss, 1, scratch1, scratch2);
6093
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006094 // Ensure value in r0, key in r1 and receiver in r2 to match keyed store ic
6095 // calling convention.
6096 if (value_.is(r1)) {
6097 __ Swap(r0, r1, ip);
6098 }
6099 ASSERT(receiver_.is(r2));
6100
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006101 // The rest of the instructions in the deferred code must be together.
6102 { Assembler::BlockConstPoolScope block_const_pool(masm_);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006103 // Call keyed store IC. It has the arguments value, key and receiver in r0,
6104 // r1 and r2.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006105 Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize));
6106 __ Call(ic, RelocInfo::CODE_TARGET);
6107 // The call must be followed by a nop instruction to indicate that the
6108 // keyed store has been inlined.
6109 __ nop(PROPERTY_ACCESS_INLINED);
6110
6111 // Block the constant pool for one more instruction after leaving this
6112 // constant pool block scope to include the branch instruction ending the
6113 // deferred code.
6114 __ BlockConstPoolFor(1);
6115 }
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006116}
6117
6118
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006119// Consumes the top of stack (the receiver) and pushes the result instead.
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006120void CodeGenerator::EmitNamedLoad(Handle<String> name, bool is_contextual) {
6121 if (is_contextual || scope()->is_global_scope() || loop_nesting() == 0) {
6122 Comment cmnt(masm(), "[ Load from named Property");
6123 // Setup the name register and call load IC.
ager@chromium.orgac091b72010-05-05 07:34:42 +00006124 frame_->CallLoadIC(name,
6125 is_contextual
6126 ? RelocInfo::CODE_TARGET_CONTEXT
6127 : RelocInfo::CODE_TARGET);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006128 frame_->EmitPush(r0); // Push answer.
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006129 } else {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006130 // Inline the in-object property case.
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006131 Comment cmnt(masm(), "[ Inlined named property load");
6132
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006133 // Counter will be decremented in the deferred code. Placed here to avoid
6134 // having it in the instruction stream below where patching will occur.
6135 __ IncrementCounter(&Counters::named_load_inline, 1,
6136 frame_->scratch0(), frame_->scratch1());
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006137
6138 // The following instructions are the inlined load of an in-object property.
6139 // Parts of this code is patched, so the exact instructions generated needs
6140 // to be fixed. Therefore the instruction pool is blocked when generating
6141 // this code
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006142
6143 // Load the receiver from the stack.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006144 Register receiver = frame_->PopToRegister();
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006145
6146 DeferredReferenceGetNamedValue* deferred =
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006147 new DeferredReferenceGetNamedValue(receiver, name);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006148
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006149#ifdef DEBUG
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006150 int kInlinedNamedLoadInstructions = 7;
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006151 Label check_inlined_codesize;
6152 masm_->bind(&check_inlined_codesize);
6153#endif
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006154
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006155 { Assembler::BlockConstPoolScope block_const_pool(masm_);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006156 // Check that the receiver is a heap object.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006157 __ tst(receiver, Operand(kSmiTagMask));
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006158 deferred->Branch(eq);
6159
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006160 Register scratch = VirtualFrame::scratch0();
6161 Register scratch2 = VirtualFrame::scratch1();
6162
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006163 // Check the map. The null map used below is patched by the inline cache
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006164 // code. Therefore we can't use a LoadRoot call.
6165 __ ldr(scratch, FieldMemOperand(receiver, HeapObject::kMapOffset));
6166 __ mov(scratch2, Operand(Factory::null_value()));
6167 __ cmp(scratch, scratch2);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006168 deferred->Branch(ne);
6169
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006170 // Initially use an invalid index. The index will be patched by the
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006171 // inline cache code.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006172 __ ldr(receiver, MemOperand(receiver, 0));
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006173
6174 // Make sure that the expected number of instructions are generated.
6175 ASSERT_EQ(kInlinedNamedLoadInstructions,
6176 masm_->InstructionsGeneratedSince(&check_inlined_codesize));
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006177 }
6178
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006179 deferred->BindExit();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006180 // At this point the receiver register has the result, either from the
6181 // deferred code or from the inlined code.
6182 frame_->EmitPush(receiver);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006183 }
6184}
6185
6186
ager@chromium.orgac091b72010-05-05 07:34:42 +00006187void CodeGenerator::EmitNamedStore(Handle<String> name, bool is_contextual) {
6188#ifdef DEBUG
6189 int expected_height = frame_->height() - (is_contextual ? 1 : 2);
6190#endif
6191 frame_->CallStoreIC(name, is_contextual);
6192
6193 ASSERT_EQ(expected_height, frame_->height());
6194}
6195
6196
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006197void CodeGenerator::EmitKeyedLoad() {
6198 if (loop_nesting() == 0) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006199 Comment cmnt(masm_, "[ Load from keyed property");
6200 frame_->CallKeyedLoadIC();
6201 } else {
6202 // Inline the keyed load.
6203 Comment cmnt(masm_, "[ Inlined load from keyed property");
6204
6205 // Counter will be decremented in the deferred code. Placed here to avoid
6206 // having it in the instruction stream below where patching will occur.
6207 __ IncrementCounter(&Counters::keyed_load_inline, 1,
6208 frame_->scratch0(), frame_->scratch1());
6209
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00006210 // Load the key and receiver from the stack.
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00006211 bool key_is_known_smi = frame_->KnownSmiAt(0);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00006212 Register key = frame_->PopToRegister();
6213 Register receiver = frame_->PopToRegister(key);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006214
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00006215 // The deferred code expects key and receiver in registers.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006216 DeferredReferenceGetKeyedValue* deferred =
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00006217 new DeferredReferenceGetKeyedValue(key, receiver);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006218
6219 // Check that the receiver is a heap object.
6220 __ tst(receiver, Operand(kSmiTagMask));
6221 deferred->Branch(eq);
6222
6223 // The following instructions are the part of the inlined load keyed
6224 // property code which can be patched. Therefore the exact number of
6225 // instructions generated need to be fixed, so the constant pool is blocked
6226 // while generating this code.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006227 { Assembler::BlockConstPoolScope block_const_pool(masm_);
6228 Register scratch1 = VirtualFrame::scratch0();
6229 Register scratch2 = VirtualFrame::scratch1();
6230 // Check the map. The null map used below is patched by the inline cache
6231 // code.
6232 __ ldr(scratch1, FieldMemOperand(receiver, HeapObject::kMapOffset));
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00006233
6234 // Check that the key is a smi.
6235 if (!key_is_known_smi) {
6236 __ tst(key, Operand(kSmiTagMask));
6237 deferred->Branch(ne);
6238 }
6239
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00006240#ifdef DEBUG
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00006241 Label check_inlined_codesize;
6242 masm_->bind(&check_inlined_codesize);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00006243#endif
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006244 __ mov(scratch2, Operand(Factory::null_value()));
6245 __ cmp(scratch1, scratch2);
6246 deferred->Branch(ne);
6247
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006248 // Get the elements array from the receiver and check that it
6249 // is not a dictionary.
6250 __ ldr(scratch1, FieldMemOperand(receiver, JSObject::kElementsOffset));
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00006251 if (FLAG_debug_code) {
6252 __ ldr(scratch2, FieldMemOperand(scratch1, JSObject::kMapOffset));
6253 __ LoadRoot(ip, Heap::kFixedArrayMapRootIndex);
6254 __ cmp(scratch2, ip);
6255 __ Assert(eq, "JSObject with fast elements map has slow elements");
6256 }
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006257
6258 // Check that key is within bounds. Use unsigned comparison to handle
6259 // negative keys.
6260 __ ldr(scratch2, FieldMemOperand(scratch1, FixedArray::kLengthOffset));
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006261 __ cmp(scratch2, key);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006262 deferred->Branch(ls); // Unsigned less equal.
6263
6264 // Load and check that the result is not the hole (key is a smi).
6265 __ LoadRoot(scratch2, Heap::kTheHoleValueRootIndex);
6266 __ add(scratch1,
6267 scratch1,
6268 Operand(FixedArray::kHeaderSize - kHeapObjectTag));
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00006269 __ ldr(scratch1,
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006270 MemOperand(scratch1, key, LSL,
6271 kPointerSizeLog2 - (kSmiTagSize + kSmiShiftSize)));
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00006272 __ cmp(scratch1, scratch2);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006273 deferred->Branch(eq);
6274
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00006275 __ mov(r0, scratch1);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006276 // Make sure that the expected number of instructions are generated.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00006277 ASSERT_EQ(GetInlinedKeyedLoadInstructionsAfterPatch(),
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006278 masm_->InstructionsGeneratedSince(&check_inlined_codesize));
6279 }
6280
6281 deferred->BindExit();
6282 }
6283}
6284
6285
6286void CodeGenerator::EmitKeyedStore(StaticType* key_type) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006287 // Generate inlined version of the keyed store if the code is in a loop
6288 // and the key is likely to be a smi.
6289 if (loop_nesting() > 0 && key_type->IsLikelySmi()) {
6290 // Inline the keyed store.
6291 Comment cmnt(masm_, "[ Inlined store to keyed property");
6292
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006293 Register scratch1 = VirtualFrame::scratch0();
6294 Register scratch2 = VirtualFrame::scratch1();
6295 Register scratch3 = r3;
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006296
6297 // Counter will be decremented in the deferred code. Placed here to avoid
6298 // having it in the instruction stream below where patching will occur.
6299 __ IncrementCounter(&Counters::keyed_store_inline, 1,
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006300 scratch1, scratch2);
6301
6302 // Load the value, key and receiver from the stack.
6303 Register value = frame_->PopToRegister();
6304 Register key = frame_->PopToRegister(value);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00006305 VirtualFrame::SpilledScope spilled(frame_);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006306 Register receiver = r2;
6307 frame_->EmitPop(receiver);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006308
6309 // The deferred code expects value, key and receiver in registers.
6310 DeferredReferenceSetKeyedValue* deferred =
6311 new DeferredReferenceSetKeyedValue(value, key, receiver);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006312
6313 // Check that the value is a smi. As this inlined code does not set the
6314 // write barrier it is only possible to store smi values.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006315 __ tst(value, Operand(kSmiTagMask));
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006316 deferred->Branch(ne);
6317
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006318 // Check that the key is a smi.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006319 __ tst(key, Operand(kSmiTagMask));
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006320 deferred->Branch(ne);
6321
6322 // Check that the receiver is a heap object.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006323 __ tst(receiver, Operand(kSmiTagMask));
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006324 deferred->Branch(eq);
6325
6326 // Check that the receiver is a JSArray.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006327 __ CompareObjectType(receiver, scratch1, scratch1, JS_ARRAY_TYPE);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006328 deferred->Branch(ne);
6329
6330 // Check that the key is within bounds. Both the key and the length of
6331 // the JSArray are smis. Use unsigned comparison to handle negative keys.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006332 __ ldr(scratch1, FieldMemOperand(receiver, JSArray::kLengthOffset));
6333 __ cmp(scratch1, key);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006334 deferred->Branch(ls); // Unsigned less equal.
6335
6336 // The following instructions are the part of the inlined store keyed
6337 // property code which can be patched. Therefore the exact number of
6338 // instructions generated need to be fixed, so the constant pool is blocked
6339 // while generating this code.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006340 { Assembler::BlockConstPoolScope block_const_pool(masm_);
6341 // Get the elements array from the receiver and check that it
6342 // is not a dictionary.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006343 __ ldr(scratch1, FieldMemOperand(receiver, JSObject::kElementsOffset));
6344 __ ldr(scratch2, FieldMemOperand(scratch1, JSObject::kMapOffset));
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006345 // Read the fixed array map from the constant pool (not from the root
6346 // array) so that the value can be patched. When debugging, we patch this
6347 // comparison to always fail so that we will hit the IC call in the
6348 // deferred code which will allow the debugger to break for fast case
6349 // stores.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006350#ifdef DEBUG
6351 Label check_inlined_codesize;
6352 masm_->bind(&check_inlined_codesize);
6353#endif
6354 __ mov(scratch3, Operand(Factory::fixed_array_map()));
6355 __ cmp(scratch2, scratch3);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006356 deferred->Branch(ne);
6357
6358 // Store the value.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006359 __ add(scratch1, scratch1,
6360 Operand(FixedArray::kHeaderSize - kHeapObjectTag));
6361 __ str(value,
6362 MemOperand(scratch1, key, LSL,
6363 kPointerSizeLog2 - (kSmiTagSize + kSmiShiftSize)));
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006364
6365 // Make sure that the expected number of instructions are generated.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006366 ASSERT_EQ(kInlinedKeyedStoreInstructionsAfterPatch,
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006367 masm_->InstructionsGeneratedSince(&check_inlined_codesize));
6368 }
6369
6370 deferred->BindExit();
6371 } else {
6372 frame()->CallKeyedStoreIC();
6373 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006374}
6375
6376
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006377#ifdef DEBUG
6378bool CodeGenerator::HasValidEntryRegisters() { return true; }
6379#endif
6380
6381
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006382#undef __
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006383#define __ ACCESS_MASM(masm)
6384
ager@chromium.org7c537e22008-10-16 08:43:32 +00006385Handle<String> Reference::GetName() {
6386 ASSERT(type_ == NAMED);
6387 Property* property = expression_->AsProperty();
6388 if (property == NULL) {
6389 // Global variable reference treated as a named property reference.
6390 VariableProxy* proxy = expression_->AsVariableProxy();
6391 ASSERT(proxy->AsVariable() != NULL);
6392 ASSERT(proxy->AsVariable()->is_global());
6393 return proxy->name();
6394 } else {
6395 Literal* raw_name = property->key()->AsLiteral();
6396 ASSERT(raw_name != NULL);
6397 return Handle<String>(String::cast(*raw_name->handle()));
6398 }
6399}
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006400
ager@chromium.org7c537e22008-10-16 08:43:32 +00006401
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006402void Reference::DupIfPersist() {
6403 if (persist_after_get_) {
6404 switch (type_) {
6405 case KEYED:
6406 cgen_->frame()->Dup2();
6407 break;
6408 case NAMED:
6409 cgen_->frame()->Dup();
6410 // Fall through.
6411 case UNLOADED:
6412 case ILLEGAL:
6413 case SLOT:
6414 // Do nothing.
6415 ;
6416 }
6417 } else {
6418 set_unloaded();
6419 }
6420}
6421
6422
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006423void Reference::GetValue() {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006424 ASSERT(cgen_->HasValidEntryRegisters());
ager@chromium.org7c537e22008-10-16 08:43:32 +00006425 ASSERT(!is_illegal());
6426 ASSERT(!cgen_->has_cc());
6427 MacroAssembler* masm = cgen_->masm();
6428 Property* property = expression_->AsProperty();
6429 if (property != NULL) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006430 cgen_->CodeForSourcePosition(property->position());
ager@chromium.org7c537e22008-10-16 08:43:32 +00006431 }
6432
6433 switch (type_) {
6434 case SLOT: {
6435 Comment cmnt(masm, "[ Load from Slot");
6436 Slot* slot = expression_->AsVariableProxy()->AsVariable()->slot();
6437 ASSERT(slot != NULL);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006438 DupIfPersist();
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006439 cgen_->LoadFromSlotCheckForArguments(slot, NOT_INSIDE_TYPEOF);
ager@chromium.org7c537e22008-10-16 08:43:32 +00006440 break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006441 }
6442
ager@chromium.org7c537e22008-10-16 08:43:32 +00006443 case NAMED: {
ager@chromium.org7c537e22008-10-16 08:43:32 +00006444 Variable* var = expression_->AsVariableProxy()->AsVariable();
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006445 bool is_global = var != NULL;
6446 ASSERT(!is_global || var->is_global());
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006447 Handle<String> name = GetName();
6448 DupIfPersist();
6449 cgen_->EmitNamedLoad(name, is_global);
ager@chromium.org7c537e22008-10-16 08:43:32 +00006450 break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006451 }
6452
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006453 case KEYED: {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006454 ASSERT(property != NULL);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006455 DupIfPersist();
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006456 cgen_->EmitKeyedLoad();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006457 cgen_->frame()->EmitPush(r0);
6458 break;
6459 }
6460
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006461 default:
6462 UNREACHABLE();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006463 }
6464}
6465
6466
ager@chromium.org7c537e22008-10-16 08:43:32 +00006467void Reference::SetValue(InitState init_state) {
6468 ASSERT(!is_illegal());
6469 ASSERT(!cgen_->has_cc());
6470 MacroAssembler* masm = cgen_->masm();
ager@chromium.org3bf7b912008-11-17 09:09:45 +00006471 VirtualFrame* frame = cgen_->frame();
ager@chromium.org7c537e22008-10-16 08:43:32 +00006472 Property* property = expression_->AsProperty();
6473 if (property != NULL) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006474 cgen_->CodeForSourcePosition(property->position());
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006475 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006476
ager@chromium.org7c537e22008-10-16 08:43:32 +00006477 switch (type_) {
6478 case SLOT: {
6479 Comment cmnt(masm, "[ Store to Slot");
6480 Slot* slot = expression_->AsVariableProxy()->AsVariable()->slot();
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006481 cgen_->StoreToSlot(slot, init_state);
ager@chromium.orgac091b72010-05-05 07:34:42 +00006482 set_unloaded();
ager@chromium.org7c537e22008-10-16 08:43:32 +00006483 break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006484 }
6485
ager@chromium.org7c537e22008-10-16 08:43:32 +00006486 case NAMED: {
6487 Comment cmnt(masm, "[ Store to named Property");
ager@chromium.orgac091b72010-05-05 07:34:42 +00006488 cgen_->EmitNamedStore(GetName(), false);
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00006489 frame->EmitPush(r0);
ager@chromium.org5c838252010-02-19 08:53:10 +00006490 set_unloaded();
ager@chromium.org7c537e22008-10-16 08:43:32 +00006491 break;
6492 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006493
ager@chromium.org7c537e22008-10-16 08:43:32 +00006494 case KEYED: {
6495 Comment cmnt(masm, "[ Store to keyed Property");
6496 Property* property = expression_->AsProperty();
6497 ASSERT(property != NULL);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006498 cgen_->CodeForSourcePosition(property->position());
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006499 cgen_->EmitKeyedStore(property->key()->type());
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00006500 frame->EmitPush(r0);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006501 set_unloaded();
ager@chromium.org7c537e22008-10-16 08:43:32 +00006502 break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006503 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00006504
6505 default:
6506 UNREACHABLE();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006507 }
6508}
6509
6510
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006511void FastNewClosureStub::Generate(MacroAssembler* masm) {
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006512 // Create a new closure from the given function info in new
6513 // space. Set the context to the current context in cp.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006514 Label gc;
6515
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006516 // Pop the function info from the stack.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006517 __ pop(r3);
6518
6519 // Attempt to allocate new JSFunction in new space.
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00006520 __ AllocateInNewSpace(JSFunction::kSize,
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006521 r0,
6522 r1,
6523 r2,
6524 &gc,
6525 TAG_OBJECT);
6526
6527 // Compute the function map in the current global context and set that
6528 // as the map of the allocated object.
6529 __ ldr(r2, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_INDEX)));
6530 __ ldr(r2, FieldMemOperand(r2, GlobalObject::kGlobalContextOffset));
6531 __ ldr(r2, MemOperand(r2, Context::SlotOffset(Context::FUNCTION_MAP_INDEX)));
6532 __ str(r2, FieldMemOperand(r0, HeapObject::kMapOffset));
6533
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006534 // Initialize the rest of the function. We don't have to update the
6535 // write barrier because the allocated object is in new space.
6536 __ LoadRoot(r1, Heap::kEmptyFixedArrayRootIndex);
6537 __ LoadRoot(r2, Heap::kTheHoleValueRootIndex);
6538 __ str(r1, FieldMemOperand(r0, JSObject::kPropertiesOffset));
6539 __ str(r1, FieldMemOperand(r0, JSObject::kElementsOffset));
6540 __ str(r2, FieldMemOperand(r0, JSFunction::kPrototypeOrInitialMapOffset));
6541 __ str(r3, FieldMemOperand(r0, JSFunction::kSharedFunctionInfoOffset));
6542 __ str(cp, FieldMemOperand(r0, JSFunction::kContextOffset));
6543 __ str(r1, FieldMemOperand(r0, JSFunction::kLiteralsOffset));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006544
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006545 // Return result. The argument function info has been popped already.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006546 __ Ret();
6547
6548 // Create a new closure through the slower runtime call.
6549 __ bind(&gc);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006550 __ Push(cp, r3);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006551 __ TailCallRuntime(Runtime::kNewClosure, 2, 1);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006552}
6553
6554
6555void FastNewContextStub::Generate(MacroAssembler* masm) {
6556 // Try to allocate the context in new space.
6557 Label gc;
6558 int length = slots_ + Context::MIN_CONTEXT_SLOTS;
6559
6560 // Attempt to allocate the context in new space.
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00006561 __ AllocateInNewSpace(FixedArray::SizeFor(length),
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006562 r0,
6563 r1,
6564 r2,
6565 &gc,
6566 TAG_OBJECT);
6567
6568 // Load the function from the stack.
ager@chromium.org5c838252010-02-19 08:53:10 +00006569 __ ldr(r3, MemOperand(sp, 0));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006570
6571 // Setup the object header.
6572 __ LoadRoot(r2, Heap::kContextMapRootIndex);
6573 __ str(r2, FieldMemOperand(r0, HeapObject::kMapOffset));
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006574 __ mov(r2, Operand(Smi::FromInt(length)));
6575 __ str(r2, FieldMemOperand(r0, FixedArray::kLengthOffset));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006576
6577 // Setup the fixed slots.
6578 __ mov(r1, Operand(Smi::FromInt(0)));
6579 __ str(r3, MemOperand(r0, Context::SlotOffset(Context::CLOSURE_INDEX)));
6580 __ str(r0, MemOperand(r0, Context::SlotOffset(Context::FCONTEXT_INDEX)));
6581 __ str(r1, MemOperand(r0, Context::SlotOffset(Context::PREVIOUS_INDEX)));
6582 __ str(r1, MemOperand(r0, Context::SlotOffset(Context::EXTENSION_INDEX)));
6583
6584 // Copy the global object from the surrounding context.
6585 __ ldr(r1, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_INDEX)));
6586 __ str(r1, MemOperand(r0, Context::SlotOffset(Context::GLOBAL_INDEX)));
6587
6588 // Initialize the rest of the slots to undefined.
6589 __ LoadRoot(r1, Heap::kUndefinedValueRootIndex);
6590 for (int i = Context::MIN_CONTEXT_SLOTS; i < length; i++) {
6591 __ str(r1, MemOperand(r0, Context::SlotOffset(i)));
6592 }
6593
6594 // Remove the on-stack argument and return.
6595 __ mov(cp, r0);
6596 __ pop();
6597 __ Ret();
6598
6599 // Need to collect. Call into runtime system.
6600 __ bind(&gc);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006601 __ TailCallRuntime(Runtime::kNewContext, 1, 1);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006602}
6603
6604
ager@chromium.org5c838252010-02-19 08:53:10 +00006605void FastCloneShallowArrayStub::Generate(MacroAssembler* masm) {
6606 // Stack layout on entry:
6607 //
6608 // [sp]: constant elements.
6609 // [sp + kPointerSize]: literal index.
6610 // [sp + (2 * kPointerSize)]: literals array.
6611
6612 // All sizes here are multiples of kPointerSize.
6613 int elements_size = (length_ > 0) ? FixedArray::SizeFor(length_) : 0;
6614 int size = JSArray::kSize + elements_size;
6615
6616 // Load boilerplate object into r3 and check if we need to create a
6617 // boilerplate.
6618 Label slow_case;
6619 __ ldr(r3, MemOperand(sp, 2 * kPointerSize));
6620 __ ldr(r0, MemOperand(sp, 1 * kPointerSize));
6621 __ add(r3, r3, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
6622 __ ldr(r3, MemOperand(r3, r0, LSL, kPointerSizeLog2 - kSmiTagSize));
6623 __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
6624 __ cmp(r3, ip);
6625 __ b(eq, &slow_case);
6626
6627 // Allocate both the JS array and the elements array in one big
6628 // allocation. This avoids multiple limit checks.
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00006629 __ AllocateInNewSpace(size,
ager@chromium.org5c838252010-02-19 08:53:10 +00006630 r0,
6631 r1,
6632 r2,
6633 &slow_case,
6634 TAG_OBJECT);
6635
6636 // Copy the JS array part.
6637 for (int i = 0; i < JSArray::kSize; i += kPointerSize) {
6638 if ((i != JSArray::kElementsOffset) || (length_ == 0)) {
6639 __ ldr(r1, FieldMemOperand(r3, i));
6640 __ str(r1, FieldMemOperand(r0, i));
6641 }
6642 }
6643
6644 if (length_ > 0) {
6645 // Get hold of the elements array of the boilerplate and setup the
6646 // elements pointer in the resulting object.
6647 __ ldr(r3, FieldMemOperand(r3, JSArray::kElementsOffset));
6648 __ add(r2, r0, Operand(JSArray::kSize));
6649 __ str(r2, FieldMemOperand(r0, JSArray::kElementsOffset));
6650
6651 // Copy the elements array.
6652 for (int i = 0; i < elements_size; i += kPointerSize) {
6653 __ ldr(r1, FieldMemOperand(r3, i));
6654 __ str(r1, FieldMemOperand(r2, i));
6655 }
6656 }
6657
6658 // Return and remove the on-stack parameters.
6659 __ add(sp, sp, Operand(3 * kPointerSize));
6660 __ Ret();
6661
6662 __ bind(&slow_case);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006663 __ TailCallRuntime(Runtime::kCreateArrayLiteralShallow, 3, 1);
ager@chromium.org5c838252010-02-19 08:53:10 +00006664}
6665
6666
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006667// Takes a Smi and converts to an IEEE 64 bit floating point value in two
6668// registers. The format is 1 sign bit, 11 exponent bits (biased 1023) and
6669// 52 fraction bits (20 in the first word, 32 in the second). Zeros is a
6670// scratch register. Destroys the source register. No GC occurs during this
6671// stub so you don't have to set up the frame.
6672class ConvertToDoubleStub : public CodeStub {
6673 public:
6674 ConvertToDoubleStub(Register result_reg_1,
6675 Register result_reg_2,
6676 Register source_reg,
6677 Register scratch_reg)
6678 : result1_(result_reg_1),
6679 result2_(result_reg_2),
6680 source_(source_reg),
6681 zeros_(scratch_reg) { }
6682
6683 private:
6684 Register result1_;
6685 Register result2_;
6686 Register source_;
6687 Register zeros_;
6688
6689 // Minor key encoding in 16 bits.
6690 class ModeBits: public BitField<OverwriteMode, 0, 2> {};
6691 class OpBits: public BitField<Token::Value, 2, 14> {};
6692
6693 Major MajorKey() { return ConvertToDouble; }
6694 int MinorKey() {
6695 // Encode the parameters in a unique 16 bit value.
6696 return result1_.code() +
6697 (result2_.code() << 4) +
6698 (source_.code() << 8) +
6699 (zeros_.code() << 12);
6700 }
6701
6702 void Generate(MacroAssembler* masm);
6703
6704 const char* GetName() { return "ConvertToDoubleStub"; }
6705
6706#ifdef DEBUG
6707 void Print() { PrintF("ConvertToDoubleStub\n"); }
6708#endif
6709};
6710
6711
6712void ConvertToDoubleStub::Generate(MacroAssembler* masm) {
6713#ifndef BIG_ENDIAN_FLOATING_POINT
6714 Register exponent = result1_;
6715 Register mantissa = result2_;
6716#else
6717 Register exponent = result2_;
6718 Register mantissa = result1_;
6719#endif
6720 Label not_special;
6721 // Convert from Smi to integer.
6722 __ mov(source_, Operand(source_, ASR, kSmiTagSize));
6723 // Move sign bit from source to destination. This works because the sign bit
6724 // in the exponent word of the double has the same position and polarity as
6725 // the 2's complement sign bit in a Smi.
6726 ASSERT(HeapNumber::kSignMask == 0x80000000u);
6727 __ and_(exponent, source_, Operand(HeapNumber::kSignMask), SetCC);
6728 // Subtract from 0 if source was negative.
6729 __ rsb(source_, source_, Operand(0), LeaveCC, ne);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006730
6731 // We have -1, 0 or 1, which we treat specially. Register source_ contains
6732 // absolute value: it is either equal to 1 (special case of -1 and 1),
6733 // greater than 1 (not a special case) or less than 1 (special case of 0).
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006734 __ cmp(source_, Operand(1));
6735 __ b(gt, &not_special);
6736
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006737 // For 1 or -1 we need to or in the 0 exponent (biased to 1023).
6738 static const uint32_t exponent_word_for_1 =
6739 HeapNumber::kExponentBias << HeapNumber::kExponentShift;
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006740 __ orr(exponent, exponent, Operand(exponent_word_for_1), LeaveCC, eq);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006741 // 1, 0 and -1 all have 0 for the second word.
6742 __ mov(mantissa, Operand(0));
6743 __ Ret();
6744
6745 __ bind(&not_special);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006746 // Count leading zeros. Uses mantissa for a scratch register on pre-ARM5.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006747 // Gets the wrong answer for 0, but we already checked for that case above.
lrn@chromium.org32d961d2010-06-30 09:09:34 +00006748 __ CountLeadingZeros(zeros_, source_, mantissa);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006749 // Compute exponent and or it into the exponent register.
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00006750 // We use mantissa as a scratch register here. Use a fudge factor to
6751 // divide the constant 31 + HeapNumber::kExponentBias, 0x41d, into two parts
6752 // that fit in the ARM's constant field.
6753 int fudge = 0x400;
6754 __ rsb(mantissa, zeros_, Operand(31 + HeapNumber::kExponentBias - fudge));
6755 __ add(mantissa, mantissa, Operand(fudge));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006756 __ orr(exponent,
6757 exponent,
6758 Operand(mantissa, LSL, HeapNumber::kExponentShift));
6759 // Shift up the source chopping the top bit off.
6760 __ add(zeros_, zeros_, Operand(1));
6761 // This wouldn't work for 1.0 or -1.0 as the shift would be 32 which means 0.
6762 __ mov(source_, Operand(source_, LSL, zeros_));
6763 // Compute lower part of fraction (last 12 bits).
6764 __ mov(mantissa, Operand(source_, LSL, HeapNumber::kMantissaBitsInTopWord));
6765 // And the top (top 20 bits).
6766 __ orr(exponent,
6767 exponent,
6768 Operand(source_, LSR, 32 - HeapNumber::kMantissaBitsInTopWord));
6769 __ Ret();
6770}
6771
6772
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006773// See comment for class.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006774void WriteInt32ToHeapNumberStub::Generate(MacroAssembler* masm) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006775 Label max_negative_int;
6776 // the_int_ has the answer which is a signed int32 but not a Smi.
6777 // We test for the special value that has a different exponent. This test
6778 // has the neat side effect of setting the flags according to the sign.
6779 ASSERT(HeapNumber::kSignMask == 0x80000000u);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00006780 __ cmp(the_int_, Operand(0x80000000u));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006781 __ b(eq, &max_negative_int);
6782 // Set up the correct exponent in scratch_. All non-Smi int32s have the same.
6783 // A non-Smi integer is 1.xxx * 2^30 so the exponent is 30 (biased).
6784 uint32_t non_smi_exponent =
6785 (HeapNumber::kExponentBias + 30) << HeapNumber::kExponentShift;
6786 __ mov(scratch_, Operand(non_smi_exponent));
6787 // Set the sign bit in scratch_ if the value was negative.
6788 __ orr(scratch_, scratch_, Operand(HeapNumber::kSignMask), LeaveCC, cs);
6789 // Subtract from 0 if the value was negative.
6790 __ rsb(the_int_, the_int_, Operand(0), LeaveCC, cs);
6791 // We should be masking the implict first digit of the mantissa away here,
6792 // but it just ends up combining harmlessly with the last digit of the
6793 // exponent that happens to be 1. The sign bit is 0 so we shift 10 to get
6794 // the most significant 1 to hit the last bit of the 12 bit sign and exponent.
6795 ASSERT(((1 << HeapNumber::kExponentShift) & non_smi_exponent) != 0);
6796 const int shift_distance = HeapNumber::kNonMantissaBitsInTopWord - 2;
6797 __ orr(scratch_, scratch_, Operand(the_int_, LSR, shift_distance));
6798 __ str(scratch_, FieldMemOperand(the_heap_number_,
6799 HeapNumber::kExponentOffset));
6800 __ mov(scratch_, Operand(the_int_, LSL, 32 - shift_distance));
6801 __ str(scratch_, FieldMemOperand(the_heap_number_,
6802 HeapNumber::kMantissaOffset));
6803 __ Ret();
6804
6805 __ bind(&max_negative_int);
6806 // The max negative int32 is stored as a positive number in the mantissa of
6807 // a double because it uses a sign bit instead of using two's complement.
6808 // The actual mantissa bits stored are all 0 because the implicit most
6809 // significant 1 bit is not stored.
6810 non_smi_exponent += 1 << HeapNumber::kExponentShift;
6811 __ mov(ip, Operand(HeapNumber::kSignMask | non_smi_exponent));
6812 __ str(ip, FieldMemOperand(the_heap_number_, HeapNumber::kExponentOffset));
6813 __ mov(ip, Operand(0));
6814 __ str(ip, FieldMemOperand(the_heap_number_, HeapNumber::kMantissaOffset));
6815 __ Ret();
6816}
6817
6818
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006819// Handle the case where the lhs and rhs are the same object.
6820// Equality is almost reflexive (everything but NaN), so this is a test
6821// for "identity and not NaN".
6822static void EmitIdenticalObjectComparison(MacroAssembler* masm,
6823 Label* slow,
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006824 Condition cc,
6825 bool never_nan_nan) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006826 Label not_identical;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006827 Label heap_number, return_equal;
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006828 __ cmp(r0, r1);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006829 __ b(ne, &not_identical);
6830
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006831 // The two objects are identical. If we know that one of them isn't NaN then
6832 // we now know they test equal.
6833 if (cc != eq || !never_nan_nan) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006834 // Test for NaN. Sadly, we can't just compare to Factory::nan_value(),
6835 // so we do the second best thing - test it ourselves.
6836 // They are both equal and they are not both Smis so both of them are not
6837 // Smis. If it's not a heap number, then return equal.
6838 if (cc == lt || cc == gt) {
6839 __ CompareObjectType(r0, r4, r4, FIRST_JS_OBJECT_TYPE);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006840 __ b(ge, slow);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006841 } else {
6842 __ CompareObjectType(r0, r4, r4, HEAP_NUMBER_TYPE);
6843 __ b(eq, &heap_number);
6844 // Comparing JS objects with <=, >= is complicated.
6845 if (cc != eq) {
6846 __ cmp(r4, Operand(FIRST_JS_OBJECT_TYPE));
6847 __ b(ge, slow);
6848 // Normally here we fall through to return_equal, but undefined is
6849 // special: (undefined == undefined) == true, but
6850 // (undefined <= undefined) == false! See ECMAScript 11.8.5.
6851 if (cc == le || cc == ge) {
6852 __ cmp(r4, Operand(ODDBALL_TYPE));
6853 __ b(ne, &return_equal);
6854 __ LoadRoot(r2, Heap::kUndefinedValueRootIndex);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006855 __ cmp(r0, r2);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006856 __ b(ne, &return_equal);
6857 if (cc == le) {
6858 // undefined <= undefined should fail.
6859 __ mov(r0, Operand(GREATER));
6860 } else {
6861 // undefined >= undefined should fail.
6862 __ mov(r0, Operand(LESS));
6863 }
6864 __ mov(pc, Operand(lr)); // Return.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006865 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006866 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006867 }
6868 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006869
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006870 __ bind(&return_equal);
6871 if (cc == lt) {
6872 __ mov(r0, Operand(GREATER)); // Things aren't less than themselves.
6873 } else if (cc == gt) {
6874 __ mov(r0, Operand(LESS)); // Things aren't greater than themselves.
6875 } else {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006876 __ mov(r0, Operand(EQUAL)); // Things are <=, >=, ==, === themselves.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006877 }
6878 __ mov(pc, Operand(lr)); // Return.
6879
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006880 if (cc != eq || !never_nan_nan) {
6881 // For less and greater we don't have to check for NaN since the result of
6882 // x < x is false regardless. For the others here is some code to check
6883 // for NaN.
6884 if (cc != lt && cc != gt) {
6885 __ bind(&heap_number);
6886 // It is a heap number, so return non-equal if it's NaN and equal if it's
6887 // not NaN.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006888
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006889 // The representation of NaN values has all exponent bits (52..62) set,
6890 // and not all mantissa bits (0..51) clear.
6891 // Read top bits of double representation (second word of value).
6892 __ ldr(r2, FieldMemOperand(r0, HeapNumber::kExponentOffset));
6893 // Test that exponent bits are all set.
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00006894 __ Sbfx(r3, r2, HeapNumber::kExponentShift, HeapNumber::kExponentBits);
6895 // NaNs have all-one exponents so they sign extend to -1.
6896 __ cmp(r3, Operand(-1));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006897 __ b(ne, &return_equal);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006898
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006899 // Shift out flag and all exponent bits, retaining only mantissa.
6900 __ mov(r2, Operand(r2, LSL, HeapNumber::kNonMantissaBitsInTopWord));
6901 // Or with all low-bits of mantissa.
6902 __ ldr(r3, FieldMemOperand(r0, HeapNumber::kMantissaOffset));
6903 __ orr(r0, r3, Operand(r2), SetCC);
6904 // For equal we already have the right value in r0: Return zero (equal)
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006905 // if all bits in mantissa are zero (it's an Infinity) and non-zero if
6906 // not (it's a NaN). For <= and >= we need to load r0 with the failing
6907 // value if it's a NaN.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006908 if (cc != eq) {
6909 // All-zero means Infinity means equal.
6910 __ mov(pc, Operand(lr), LeaveCC, eq); // Return equal
6911 if (cc == le) {
6912 __ mov(r0, Operand(GREATER)); // NaN <= NaN should fail.
6913 } else {
6914 __ mov(r0, Operand(LESS)); // NaN >= NaN should fail.
6915 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006916 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006917 __ mov(pc, Operand(lr)); // Return.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006918 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006919 // No fall through here.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006920 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006921
6922 __ bind(&not_identical);
6923}
6924
6925
6926// See comment at call site.
6927static void EmitSmiNonsmiComparison(MacroAssembler* masm,
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006928 Label* lhs_not_nan,
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006929 Label* slow,
6930 bool strict) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006931 Label rhs_is_smi;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006932 __ tst(r0, Operand(kSmiTagMask));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006933 __ b(eq, &rhs_is_smi);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006934
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006935 // Lhs is a Smi. Check whether the rhs is a heap number.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006936 __ CompareObjectType(r0, r4, r4, HEAP_NUMBER_TYPE);
6937 if (strict) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006938 // If rhs is not a number and lhs is a Smi then strict equality cannot
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006939 // succeed. Return non-equal (r0 is already not zero)
6940 __ mov(pc, Operand(lr), LeaveCC, ne); // Return.
6941 } else {
6942 // Smi compared non-strictly with a non-Smi non-heap-number. Call
6943 // the runtime.
6944 __ b(ne, slow);
6945 }
6946
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006947 // Lhs (r1) is a smi, rhs (r0) is a number.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006948 if (CpuFeatures::IsSupported(VFP3)) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006949 // Convert lhs to a double in d7 .
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006950 CpuFeatures::Scope scope(VFP3);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006951 __ mov(r7, Operand(r1, ASR, kSmiTagSize));
6952 __ vmov(s15, r7);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006953 __ vcvt_f64_s32(d7, s15);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006954 // Load the double from rhs, tagged HeapNumber r0, to d6.
6955 __ sub(r7, r0, Operand(kHeapObjectTag));
6956 __ vldr(d6, r7, HeapNumber::kValueOffset);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006957 } else {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006958 __ push(lr);
6959 // Convert lhs to a double in r2, r3.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006960 __ mov(r7, Operand(r1));
6961 ConvertToDoubleStub stub1(r3, r2, r7, r6);
6962 __ Call(stub1.GetCode(), RelocInfo::CODE_TARGET);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006963 // Load rhs to a double in r0, r1.
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00006964 __ Ldrd(r0, r1, FieldMemOperand(r0, HeapNumber::kValueOffset));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006965 __ pop(lr);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006966 }
6967
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006968 // We now have both loaded as doubles but we can skip the lhs nan check
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006969 // since it's a smi.
6970 __ jmp(lhs_not_nan);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006971
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006972 __ bind(&rhs_is_smi);
6973 // Rhs is a smi. Check whether the non-smi lhs is a heap number.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006974 __ CompareObjectType(r1, r4, r4, HEAP_NUMBER_TYPE);
6975 if (strict) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006976 // If lhs is not a number and rhs is a smi then strict equality cannot
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006977 // succeed. Return non-equal.
6978 __ mov(r0, Operand(1), LeaveCC, ne); // Non-zero indicates not equal.
6979 __ mov(pc, Operand(lr), LeaveCC, ne); // Return.
6980 } else {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006981 // Smi compared non-strictly with a non-smi non-heap-number. Call
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006982 // the runtime.
6983 __ b(ne, slow);
6984 }
6985
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006986 // Rhs (r0) is a smi, lhs (r1) is a heap number.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006987 if (CpuFeatures::IsSupported(VFP3)) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006988 // Convert rhs to a double in d6 .
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006989 CpuFeatures::Scope scope(VFP3);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006990 // Load the double from lhs, tagged HeapNumber r1, to d7.
6991 __ sub(r7, r1, Operand(kHeapObjectTag));
6992 __ vldr(d7, r7, HeapNumber::kValueOffset);
6993 __ mov(r7, Operand(r0, ASR, kSmiTagSize));
6994 __ vmov(s13, r7);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006995 __ vcvt_f64_s32(d6, s13);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006996 } else {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006997 __ push(lr);
6998 // Load lhs to a double in r2, r3.
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00006999 __ Ldrd(r2, r3, FieldMemOperand(r1, HeapNumber::kValueOffset));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007000 // Convert rhs to a double in r0, r1.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007001 __ mov(r7, Operand(r0));
7002 ConvertToDoubleStub stub2(r1, r0, r7, r6);
7003 __ Call(stub2.GetCode(), RelocInfo::CODE_TARGET);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007004 __ pop(lr);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007005 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007006 // Fall through to both_loaded_as_doubles.
7007}
7008
7009
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007010void EmitNanCheck(MacroAssembler* masm, Label* lhs_not_nan, Condition cc) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007011 bool exp_first = (HeapNumber::kExponentOffset == HeapNumber::kValueOffset);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007012 Register rhs_exponent = exp_first ? r0 : r1;
7013 Register lhs_exponent = exp_first ? r2 : r3;
7014 Register rhs_mantissa = exp_first ? r1 : r0;
7015 Register lhs_mantissa = exp_first ? r3 : r2;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007016 Label one_is_nan, neither_is_nan;
7017
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00007018 __ Sbfx(r4,
7019 lhs_exponent,
7020 HeapNumber::kExponentShift,
7021 HeapNumber::kExponentBits);
7022 // NaNs have all-one exponents so they sign extend to -1.
7023 __ cmp(r4, Operand(-1));
7024 __ b(ne, lhs_not_nan);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007025 __ mov(r4,
7026 Operand(lhs_exponent, LSL, HeapNumber::kNonMantissaBitsInTopWord),
7027 SetCC);
7028 __ b(ne, &one_is_nan);
7029 __ cmp(lhs_mantissa, Operand(0));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007030 __ b(ne, &one_is_nan);
7031
7032 __ bind(lhs_not_nan);
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00007033 __ Sbfx(r4,
7034 rhs_exponent,
7035 HeapNumber::kExponentShift,
7036 HeapNumber::kExponentBits);
7037 // NaNs have all-one exponents so they sign extend to -1.
7038 __ cmp(r4, Operand(-1));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007039 __ b(ne, &neither_is_nan);
7040 __ mov(r4,
7041 Operand(rhs_exponent, LSL, HeapNumber::kNonMantissaBitsInTopWord),
7042 SetCC);
7043 __ b(ne, &one_is_nan);
7044 __ cmp(rhs_mantissa, Operand(0));
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007045 __ b(eq, &neither_is_nan);
7046
7047 __ bind(&one_is_nan);
7048 // NaN comparisons always fail.
7049 // Load whatever we need in r0 to make the comparison fail.
7050 if (cc == lt || cc == le) {
7051 __ mov(r0, Operand(GREATER));
7052 } else {
7053 __ mov(r0, Operand(LESS));
7054 }
7055 __ mov(pc, Operand(lr)); // Return.
7056
7057 __ bind(&neither_is_nan);
7058}
7059
7060
7061// See comment at call site.
7062static void EmitTwoNonNanDoubleComparison(MacroAssembler* masm, Condition cc) {
7063 bool exp_first = (HeapNumber::kExponentOffset == HeapNumber::kValueOffset);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007064 Register rhs_exponent = exp_first ? r0 : r1;
7065 Register lhs_exponent = exp_first ? r2 : r3;
7066 Register rhs_mantissa = exp_first ? r1 : r0;
7067 Register lhs_mantissa = exp_first ? r3 : r2;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007068
7069 // r0, r1, r2, r3 have the two doubles. Neither is a NaN.
7070 if (cc == eq) {
7071 // Doubles are not equal unless they have the same bit pattern.
7072 // Exception: 0 and -0.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007073 __ cmp(rhs_mantissa, Operand(lhs_mantissa));
7074 __ orr(r0, rhs_mantissa, Operand(lhs_mantissa), LeaveCC, ne);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007075 // Return non-zero if the numbers are unequal.
7076 __ mov(pc, Operand(lr), LeaveCC, ne);
7077
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007078 __ sub(r0, rhs_exponent, Operand(lhs_exponent), SetCC);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007079 // If exponents are equal then return 0.
7080 __ mov(pc, Operand(lr), LeaveCC, eq);
7081
7082 // Exponents are unequal. The only way we can return that the numbers
7083 // are equal is if one is -0 and the other is 0. We already dealt
7084 // with the case where both are -0 or both are 0.
7085 // We start by seeing if the mantissas (that are equal) or the bottom
7086 // 31 bits of the rhs exponent are non-zero. If so we return not
7087 // equal.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007088 __ orr(r4, lhs_mantissa, Operand(lhs_exponent, LSL, kSmiTagSize), SetCC);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007089 __ mov(r0, Operand(r4), LeaveCC, ne);
7090 __ mov(pc, Operand(lr), LeaveCC, ne); // Return conditionally.
7091 // Now they are equal if and only if the lhs exponent is zero in its
7092 // low 31 bits.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007093 __ mov(r0, Operand(rhs_exponent, LSL, kSmiTagSize));
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007094 __ mov(pc, Operand(lr));
7095 } else {
7096 // Call a native function to do a comparison between two non-NaNs.
7097 // Call C routine that may not cause GC or other trouble.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00007098 __ push(lr);
7099 __ PrepareCallCFunction(4, r5); // Two doubles count as 4 arguments.
7100 __ CallCFunction(ExternalReference::compare_doubles(), 4);
7101 __ pop(pc); // Return.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007102 }
7103}
7104
7105
7106// See comment at call site.
7107static void EmitStrictTwoHeapObjectCompare(MacroAssembler* masm) {
7108 // If either operand is a JSObject or an oddball value, then they are
7109 // not equal since their pointers are different.
7110 // There is no test for undetectability in strict equality.
7111 ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
7112 Label first_non_object;
7113 // Get the type of the first operand into r2 and compare it with
7114 // FIRST_JS_OBJECT_TYPE.
7115 __ CompareObjectType(r0, r2, r2, FIRST_JS_OBJECT_TYPE);
7116 __ b(lt, &first_non_object);
7117
7118 // Return non-zero (r0 is not zero)
7119 Label return_not_equal;
7120 __ bind(&return_not_equal);
7121 __ mov(pc, Operand(lr)); // Return.
7122
7123 __ bind(&first_non_object);
7124 // Check for oddballs: true, false, null, undefined.
7125 __ cmp(r2, Operand(ODDBALL_TYPE));
7126 __ b(eq, &return_not_equal);
7127
7128 __ CompareObjectType(r1, r3, r3, FIRST_JS_OBJECT_TYPE);
7129 __ b(ge, &return_not_equal);
7130
7131 // Check for oddballs: true, false, null, undefined.
7132 __ cmp(r3, Operand(ODDBALL_TYPE));
7133 __ b(eq, &return_not_equal);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007134
7135 // Now that we have the types we might as well check for symbol-symbol.
7136 // Ensure that no non-strings have the symbol bit set.
7137 ASSERT(kNotStringTag + kIsSymbolMask > LAST_TYPE);
7138 ASSERT(kSymbolTag != 0);
7139 __ and_(r2, r2, Operand(r3));
7140 __ tst(r2, Operand(kIsSymbolMask));
7141 __ b(ne, &return_not_equal);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007142}
7143
7144
7145// See comment at call site.
7146static void EmitCheckForTwoHeapNumbers(MacroAssembler* masm,
7147 Label* both_loaded_as_doubles,
7148 Label* not_heap_numbers,
7149 Label* slow) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007150 __ CompareObjectType(r0, r3, r2, HEAP_NUMBER_TYPE);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007151 __ b(ne, not_heap_numbers);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007152 __ ldr(r2, FieldMemOperand(r1, HeapObject::kMapOffset));
7153 __ cmp(r2, r3);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007154 __ b(ne, slow); // First was a heap number, second wasn't. Go slow case.
7155
7156 // Both are heap numbers. Load them up then jump to the code we have
7157 // for that.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007158 if (CpuFeatures::IsSupported(VFP3)) {
7159 CpuFeatures::Scope scope(VFP3);
7160 __ sub(r7, r0, Operand(kHeapObjectTag));
7161 __ vldr(d6, r7, HeapNumber::kValueOffset);
7162 __ sub(r7, r1, Operand(kHeapObjectTag));
7163 __ vldr(d7, r7, HeapNumber::kValueOffset);
7164 } else {
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00007165 __ Ldrd(r2, r3, FieldMemOperand(r1, HeapNumber::kValueOffset));
7166 __ Ldrd(r0, r1, FieldMemOperand(r0, HeapNumber::kValueOffset));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007167 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007168 __ jmp(both_loaded_as_doubles);
7169}
7170
7171
7172// Fast negative check for symbol-to-symbol equality.
7173static void EmitCheckForSymbols(MacroAssembler* masm, Label* slow) {
7174 // r2 is object type of r0.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007175 // Ensure that no non-strings have the symbol bit set.
7176 ASSERT(kNotStringTag + kIsSymbolMask > LAST_TYPE);
7177 ASSERT(kSymbolTag != 0);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007178 __ tst(r2, Operand(kIsSymbolMask));
7179 __ b(eq, slow);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007180 __ ldr(r3, FieldMemOperand(r1, HeapObject::kMapOffset));
7181 __ ldrb(r3, FieldMemOperand(r3, Map::kInstanceTypeOffset));
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007182 __ tst(r3, Operand(kIsSymbolMask));
7183 __ b(eq, slow);
7184
7185 // Both are symbols. We already checked they weren't the same pointer
7186 // so they are not equal.
7187 __ mov(r0, Operand(1)); // Non-zero indicates not equal.
7188 __ mov(pc, Operand(lr)); // Return.
7189}
7190
7191
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007192void NumberToStringStub::GenerateLookupNumberStringCache(MacroAssembler* masm,
7193 Register object,
7194 Register result,
7195 Register scratch1,
7196 Register scratch2,
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00007197 Register scratch3,
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007198 bool object_is_smi,
7199 Label* not_found) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007200 // Use of registers. Register result is used as a temporary.
7201 Register number_string_cache = result;
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00007202 Register mask = scratch3;
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007203
7204 // Load the number string cache.
7205 __ LoadRoot(number_string_cache, Heap::kNumberStringCacheRootIndex);
7206
7207 // Make the hash mask from the length of the number string cache. It
7208 // contains two elements (number and string) for each cache entry.
7209 __ ldr(mask, FieldMemOperand(number_string_cache, FixedArray::kLengthOffset));
ricow@chromium.org30ce4112010-05-31 10:38:25 +00007210 // Divide length by two (length is a smi).
7211 __ mov(mask, Operand(mask, ASR, kSmiTagSize + 1));
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007212 __ sub(mask, mask, Operand(1)); // Make mask.
7213
7214 // Calculate the entry in the number string cache. The hash value in the
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00007215 // number string cache for smis is just the smi value, and the hash for
7216 // doubles is the xor of the upper and lower words. See
7217 // Heap::GetNumberStringCache.
7218 Label is_smi;
7219 Label load_result_from_cache;
7220 if (!object_is_smi) {
7221 __ BranchOnSmi(object, &is_smi);
7222 if (CpuFeatures::IsSupported(VFP3)) {
7223 CpuFeatures::Scope scope(VFP3);
7224 __ CheckMap(object,
7225 scratch1,
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00007226 Heap::kHeapNumberMapRootIndex,
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00007227 not_found,
7228 true);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007229
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00007230 ASSERT_EQ(8, kDoubleSize);
7231 __ add(scratch1,
7232 object,
7233 Operand(HeapNumber::kValueOffset - kHeapObjectTag));
7234 __ ldm(ia, scratch1, scratch1.bit() | scratch2.bit());
7235 __ eor(scratch1, scratch1, Operand(scratch2));
7236 __ and_(scratch1, scratch1, Operand(mask));
7237
7238 // Calculate address of entry in string cache: each entry consists
7239 // of two pointer sized fields.
7240 __ add(scratch1,
7241 number_string_cache,
7242 Operand(scratch1, LSL, kPointerSizeLog2 + 1));
7243
7244 Register probe = mask;
7245 __ ldr(probe,
7246 FieldMemOperand(scratch1, FixedArray::kHeaderSize));
7247 __ BranchOnSmi(probe, not_found);
7248 __ sub(scratch2, object, Operand(kHeapObjectTag));
7249 __ vldr(d0, scratch2, HeapNumber::kValueOffset);
7250 __ sub(probe, probe, Operand(kHeapObjectTag));
7251 __ vldr(d1, probe, HeapNumber::kValueOffset);
7252 __ vcmp(d0, d1);
7253 __ vmrs(pc);
7254 __ b(ne, not_found); // The cache did not contain this value.
7255 __ b(&load_result_from_cache);
7256 } else {
7257 __ b(not_found);
7258 }
7259 }
7260
7261 __ bind(&is_smi);
7262 Register scratch = scratch1;
7263 __ and_(scratch, mask, Operand(object, ASR, 1));
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007264 // Calculate address of entry in string cache: each entry consists
7265 // of two pointer sized fields.
7266 __ add(scratch,
7267 number_string_cache,
7268 Operand(scratch, LSL, kPointerSizeLog2 + 1));
7269
7270 // Check if the entry is the smi we are looking for.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00007271 Register probe = mask;
7272 __ ldr(probe, FieldMemOperand(scratch, FixedArray::kHeaderSize));
7273 __ cmp(object, probe);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007274 __ b(ne, not_found);
7275
7276 // Get the result from the cache.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00007277 __ bind(&load_result_from_cache);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007278 __ ldr(result,
7279 FieldMemOperand(scratch, FixedArray::kHeaderSize + kPointerSize));
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007280 __ IncrementCounter(&Counters::number_to_string_native,
7281 1,
7282 scratch1,
7283 scratch2);
7284}
7285
7286
7287void NumberToStringStub::Generate(MacroAssembler* masm) {
7288 Label runtime;
7289
7290 __ ldr(r1, MemOperand(sp, 0));
7291
7292 // Generate code to lookup number in the number string cache.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00007293 GenerateLookupNumberStringCache(masm, r1, r0, r2, r3, r4, false, &runtime);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007294 __ add(sp, sp, Operand(1 * kPointerSize));
7295 __ Ret();
7296
7297 __ bind(&runtime);
7298 // Handle number to string in the runtime system if not found in the cache.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00007299 __ TailCallRuntime(Runtime::kNumberToStringSkipCache, 1, 1);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007300}
7301
7302
ager@chromium.orgac091b72010-05-05 07:34:42 +00007303void RecordWriteStub::Generate(MacroAssembler* masm) {
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00007304 __ RecordWriteHelper(object_, Operand(offset_), offset_, scratch_);
ager@chromium.orgac091b72010-05-05 07:34:42 +00007305 __ Ret();
7306}
7307
7308
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007309// On entry r0 (rhs) and r1 (lhs) are the values to be compared.
7310// On exit r0 is 0, positive or negative to indicate the result of
7311// the comparison.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007312void CompareStub::Generate(MacroAssembler* masm) {
7313 Label slow; // Call builtin.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007314 Label not_smis, both_loaded_as_doubles, lhs_not_nan;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007315
7316 // NOTICE! This code is only reached after a smi-fast-case check, so
7317 // it is certain that at least one operand isn't a smi.
7318
7319 // Handle the case where the objects are identical. Either returns the answer
7320 // or goes to slow. Only falls through if the objects were not identical.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007321 EmitIdenticalObjectComparison(masm, &slow, cc_, never_nan_nan_);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007322
7323 // If either is a Smi (we know that not both are), then they can only
7324 // be strictly equal if the other is a HeapNumber.
7325 ASSERT_EQ(0, kSmiTag);
7326 ASSERT_EQ(0, Smi::FromInt(0));
7327 __ and_(r2, r0, Operand(r1));
7328 __ tst(r2, Operand(kSmiTagMask));
7329 __ b(ne, &not_smis);
7330 // One operand is a smi. EmitSmiNonsmiComparison generates code that can:
7331 // 1) Return the answer.
7332 // 2) Go to slow.
7333 // 3) Fall through to both_loaded_as_doubles.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007334 // 4) Jump to lhs_not_nan.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007335 // In cases 3 and 4 we have found out we were dealing with a number-number
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007336 // comparison. If VFP3 is supported the double values of the numbers have
7337 // been loaded into d7 and d6. Otherwise, the double values have been loaded
7338 // into r0, r1, r2, and r3.
7339 EmitSmiNonsmiComparison(masm, &lhs_not_nan, &slow, strict_);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007340
7341 __ bind(&both_loaded_as_doubles);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007342 // The arguments have been converted to doubles and stored in d6 and d7, if
7343 // VFP3 is supported, or in r0, r1, r2, and r3.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007344 if (CpuFeatures::IsSupported(VFP3)) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007345 __ bind(&lhs_not_nan);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007346 CpuFeatures::Scope scope(VFP3);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007347 Label no_nan;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007348 // ARMv7 VFP3 instructions to implement double precision comparison.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007349 __ vcmp(d7, d6);
7350 __ vmrs(pc); // Move vector status bits to normal status bits.
7351 Label nan;
7352 __ b(vs, &nan);
7353 __ mov(r0, Operand(EQUAL), LeaveCC, eq);
7354 __ mov(r0, Operand(LESS), LeaveCC, lt);
7355 __ mov(r0, Operand(GREATER), LeaveCC, gt);
7356 __ mov(pc, Operand(lr));
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007357
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007358 __ bind(&nan);
7359 // If one of the sides was a NaN then the v flag is set. Load r0 with
7360 // whatever it takes to make the comparison fail, since comparisons with NaN
7361 // always fail.
7362 if (cc_ == lt || cc_ == le) {
7363 __ mov(r0, Operand(GREATER));
7364 } else {
7365 __ mov(r0, Operand(LESS));
7366 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007367 __ mov(pc, Operand(lr));
7368 } else {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007369 // Checks for NaN in the doubles we have loaded. Can return the answer or
7370 // fall through if neither is a NaN. Also binds lhs_not_nan.
7371 EmitNanCheck(masm, &lhs_not_nan, cc_);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007372 // Compares two doubles in r0, r1, r2, r3 that are not NaNs. Returns the
7373 // answer. Never falls through.
7374 EmitTwoNonNanDoubleComparison(masm, cc_);
7375 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007376
7377 __ bind(&not_smis);
7378 // At this point we know we are dealing with two different objects,
7379 // and neither of them is a Smi. The objects are in r0 and r1.
7380 if (strict_) {
7381 // This returns non-equal for some object types, or falls through if it
7382 // was not lucky.
7383 EmitStrictTwoHeapObjectCompare(masm);
7384 }
7385
7386 Label check_for_symbols;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007387 Label flat_string_check;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007388 // Check for heap-number-heap-number comparison. Can jump to slow case,
7389 // or load both doubles into r0, r1, r2, r3 and jump to the code that handles
7390 // that case. If the inputs are not doubles then jumps to check_for_symbols.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007391 // In this case r2 will contain the type of r0. Never falls through.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007392 EmitCheckForTwoHeapNumbers(masm,
7393 &both_loaded_as_doubles,
7394 &check_for_symbols,
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007395 &flat_string_check);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007396
7397 __ bind(&check_for_symbols);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007398 // In the strict case the EmitStrictTwoHeapObjectCompare already took care of
7399 // symbols.
7400 if (cc_ == eq && !strict_) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007401 // Either jumps to slow or returns the answer. Assumes that r2 is the type
7402 // of r0 on entry.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007403 EmitCheckForSymbols(masm, &flat_string_check);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007404 }
7405
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007406 // Check for both being sequential ASCII strings, and inline if that is the
7407 // case.
7408 __ bind(&flat_string_check);
7409
7410 __ JumpIfNonSmisNotBothSequentialAsciiStrings(r0, r1, r2, r3, &slow);
7411
7412 __ IncrementCounter(&Counters::string_compare_native, 1, r2, r3);
7413 StringCompareStub::GenerateCompareFlatAsciiStrings(masm,
7414 r1,
7415 r0,
7416 r2,
7417 r3,
7418 r4,
7419 r5);
7420 // Never falls through to here.
7421
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007422 __ bind(&slow);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007423
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00007424 __ Push(r1, r0);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007425 // Figure out which native to call and setup the arguments.
7426 Builtins::JavaScript native;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007427 if (cc_ == eq) {
7428 native = strict_ ? Builtins::STRICT_EQUALS : Builtins::EQUALS;
7429 } else {
7430 native = Builtins::COMPARE;
7431 int ncr; // NaN compare result
7432 if (cc_ == lt || cc_ == le) {
7433 ncr = GREATER;
7434 } else {
7435 ASSERT(cc_ == gt || cc_ == ge); // remaining cases
7436 ncr = LESS;
7437 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007438 __ mov(r0, Operand(Smi::FromInt(ncr)));
7439 __ push(r0);
7440 }
7441
7442 // Call the native; it returns -1 (less), 0 (equal), or 1 (greater)
7443 // tagged as a small integer.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007444 __ InvokeBuiltin(native, JUMP_JS);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007445}
7446
7447
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00007448// We fall into this code if the operands were Smis, but the result was
7449// not (eg. overflow). We branch into this code (to the not_smi label) if
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007450// the operands were not both Smi. The operands are in r0 and r1. In order
7451// to call the C-implemented binary fp operation routines we need to end up
7452// with the double precision floating point operands in r0 and r1 (for the
7453// value in r1) and r2 and r3 (for the value in r0).
ager@chromium.org357bf652010-04-12 11:30:10 +00007454void GenericBinaryOpStub::HandleBinaryOpSlowCases(
7455 MacroAssembler* masm,
7456 Label* not_smi,
7457 Register lhs,
7458 Register rhs,
7459 const Builtins::JavaScript& builtin) {
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00007460 Label slow, slow_reverse, do_the_call;
ager@chromium.org357bf652010-04-12 11:30:10 +00007461 bool use_fp_registers = CpuFeatures::IsSupported(VFP3) && Token::MOD != op_;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007462
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00007463 ASSERT((lhs.is(r0) && rhs.is(r1)) || (lhs.is(r1) && rhs.is(r0)));
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00007464 Register heap_number_map = r6;
ager@chromium.org357bf652010-04-12 11:30:10 +00007465
7466 if (ShouldGenerateSmiCode()) {
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00007467 __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
7468
ager@chromium.org357bf652010-04-12 11:30:10 +00007469 // Smi-smi case (overflow).
7470 // Since both are Smis there is no heap number to overwrite, so allocate.
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00007471 // The new heap number is in r5. r3 and r7 are scratch.
7472 __ AllocateHeapNumber(
7473 r5, r3, r7, heap_number_map, lhs.is(r0) ? &slow_reverse : &slow);
ager@chromium.org357bf652010-04-12 11:30:10 +00007474
7475 // If we have floating point hardware, inline ADD, SUB, MUL, and DIV,
7476 // using registers d7 and d6 for the double values.
lrn@chromium.org32d961d2010-06-30 09:09:34 +00007477 if (CpuFeatures::IsSupported(VFP3)) {
ager@chromium.org357bf652010-04-12 11:30:10 +00007478 CpuFeatures::Scope scope(VFP3);
7479 __ mov(r7, Operand(rhs, ASR, kSmiTagSize));
7480 __ vmov(s15, r7);
7481 __ vcvt_f64_s32(d7, s15);
7482 __ mov(r7, Operand(lhs, ASR, kSmiTagSize));
7483 __ vmov(s13, r7);
7484 __ vcvt_f64_s32(d6, s13);
lrn@chromium.org32d961d2010-06-30 09:09:34 +00007485 if (!use_fp_registers) {
7486 __ vmov(r2, r3, d7);
7487 __ vmov(r0, r1, d6);
7488 }
ager@chromium.org357bf652010-04-12 11:30:10 +00007489 } else {
lrn@chromium.org32d961d2010-06-30 09:09:34 +00007490 // Write Smi from rhs to r3 and r2 in double format. r9 is scratch.
ager@chromium.org357bf652010-04-12 11:30:10 +00007491 __ mov(r7, Operand(rhs));
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00007492 ConvertToDoubleStub stub1(r3, r2, r7, r9);
ager@chromium.org357bf652010-04-12 11:30:10 +00007493 __ push(lr);
7494 __ Call(stub1.GetCode(), RelocInfo::CODE_TARGET);
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00007495 // Write Smi from lhs to r1 and r0 in double format. r9 is scratch.
ager@chromium.org357bf652010-04-12 11:30:10 +00007496 __ mov(r7, Operand(lhs));
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00007497 ConvertToDoubleStub stub2(r1, r0, r7, r9);
ager@chromium.org357bf652010-04-12 11:30:10 +00007498 __ Call(stub2.GetCode(), RelocInfo::CODE_TARGET);
7499 __ pop(lr);
7500 }
7501 __ jmp(&do_the_call); // Tail call. No return.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007502 }
7503
ager@chromium.org357bf652010-04-12 11:30:10 +00007504 // We branch here if at least one of r0 and r1 is not a Smi.
7505 __ bind(not_smi);
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00007506 __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007507
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00007508 // After this point we have the left hand side in r1 and the right hand side
7509 // in r0.
ager@chromium.org357bf652010-04-12 11:30:10 +00007510 if (lhs.is(r0)) {
7511 __ Swap(r0, r1, ip);
7512 }
7513
7514 if (ShouldGenerateFPCode()) {
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00007515 Label r0_is_smi, r1_is_smi, finished_loading_r0, finished_loading_r1;
7516
ager@chromium.org357bf652010-04-12 11:30:10 +00007517 if (runtime_operands_type_ == BinaryOpIC::DEFAULT) {
7518 switch (op_) {
7519 case Token::ADD:
7520 case Token::SUB:
7521 case Token::MUL:
7522 case Token::DIV:
7523 GenerateTypeTransition(masm);
7524 break;
7525
7526 default:
7527 break;
7528 }
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00007529 // Restore heap number map register.
7530 __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
ager@chromium.org357bf652010-04-12 11:30:10 +00007531 }
7532
7533 if (mode_ == NO_OVERWRITE) {
7534 // In the case where there is no chance of an overwritable float we may as
7535 // well do the allocation immediately while r0 and r1 are untouched.
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00007536 __ AllocateHeapNumber(r5, r3, r7, heap_number_map, &slow);
ager@chromium.org357bf652010-04-12 11:30:10 +00007537 }
7538
7539 // Move r0 to a double in r2-r3.
7540 __ tst(r0, Operand(kSmiTagMask));
7541 __ b(eq, &r0_is_smi); // It's a Smi so don't check it's a heap number.
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00007542 __ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset));
7543 __ AssertRegisterIsRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
7544 __ cmp(r4, heap_number_map);
ager@chromium.org357bf652010-04-12 11:30:10 +00007545 __ b(ne, &slow);
7546 if (mode_ == OVERWRITE_RIGHT) {
7547 __ mov(r5, Operand(r0)); // Overwrite this heap number.
7548 }
7549 if (use_fp_registers) {
7550 CpuFeatures::Scope scope(VFP3);
7551 // Load the double from tagged HeapNumber r0 to d7.
7552 __ sub(r7, r0, Operand(kHeapObjectTag));
7553 __ vldr(d7, r7, HeapNumber::kValueOffset);
7554 } else {
7555 // Calling convention says that second double is in r2 and r3.
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00007556 __ Ldrd(r2, r3, FieldMemOperand(r0, HeapNumber::kValueOffset));
ager@chromium.org357bf652010-04-12 11:30:10 +00007557 }
7558 __ jmp(&finished_loading_r0);
7559 __ bind(&r0_is_smi);
7560 if (mode_ == OVERWRITE_RIGHT) {
7561 // We can't overwrite a Smi so get address of new heap number into r5.
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00007562 __ AllocateHeapNumber(r5, r4, r7, heap_number_map, &slow);
ager@chromium.org357bf652010-04-12 11:30:10 +00007563 }
7564
lrn@chromium.org32d961d2010-06-30 09:09:34 +00007565 if (CpuFeatures::IsSupported(VFP3)) {
ager@chromium.org357bf652010-04-12 11:30:10 +00007566 CpuFeatures::Scope scope(VFP3);
7567 // Convert smi in r0 to double in d7.
7568 __ mov(r7, Operand(r0, ASR, kSmiTagSize));
7569 __ vmov(s15, r7);
7570 __ vcvt_f64_s32(d7, s15);
lrn@chromium.org32d961d2010-06-30 09:09:34 +00007571 if (!use_fp_registers) {
7572 __ vmov(r2, r3, d7);
7573 }
ager@chromium.org357bf652010-04-12 11:30:10 +00007574 } else {
7575 // Write Smi from r0 to r3 and r2 in double format.
7576 __ mov(r7, Operand(r0));
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00007577 ConvertToDoubleStub stub3(r3, r2, r7, r4);
ager@chromium.org357bf652010-04-12 11:30:10 +00007578 __ push(lr);
7579 __ Call(stub3.GetCode(), RelocInfo::CODE_TARGET);
7580 __ pop(lr);
7581 }
7582
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00007583 // HEAP_NUMBERS stub is slower than GENERIC on a pair of smis.
7584 // r0 is known to be a smi. If r1 is also a smi then switch to GENERIC.
7585 Label r1_is_not_smi;
7586 if (runtime_operands_type_ == BinaryOpIC::HEAP_NUMBERS) {
7587 __ tst(r1, Operand(kSmiTagMask));
7588 __ b(ne, &r1_is_not_smi);
7589 GenerateTypeTransition(masm);
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00007590 // Restore heap number map register.
7591 __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00007592 __ jmp(&r1_is_smi);
7593 }
7594
ager@chromium.org357bf652010-04-12 11:30:10 +00007595 __ bind(&finished_loading_r0);
7596
7597 // Move r1 to a double in r0-r1.
7598 __ tst(r1, Operand(kSmiTagMask));
7599 __ b(eq, &r1_is_smi); // It's a Smi so don't check it's a heap number.
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00007600 __ bind(&r1_is_not_smi);
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00007601 __ ldr(r4, FieldMemOperand(r1, HeapNumber::kMapOffset));
7602 __ AssertRegisterIsRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
7603 __ cmp(r4, heap_number_map);
ager@chromium.org357bf652010-04-12 11:30:10 +00007604 __ b(ne, &slow);
7605 if (mode_ == OVERWRITE_LEFT) {
7606 __ mov(r5, Operand(r1)); // Overwrite this heap number.
7607 }
7608 if (use_fp_registers) {
7609 CpuFeatures::Scope scope(VFP3);
7610 // Load the double from tagged HeapNumber r1 to d6.
7611 __ sub(r7, r1, Operand(kHeapObjectTag));
7612 __ vldr(d6, r7, HeapNumber::kValueOffset);
7613 } else {
7614 // Calling convention says that first double is in r0 and r1.
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00007615 __ Ldrd(r0, r1, FieldMemOperand(r1, HeapNumber::kValueOffset));
ager@chromium.org357bf652010-04-12 11:30:10 +00007616 }
7617 __ jmp(&finished_loading_r1);
7618 __ bind(&r1_is_smi);
7619 if (mode_ == OVERWRITE_LEFT) {
7620 // We can't overwrite a Smi so get address of new heap number into r5.
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00007621 __ AllocateHeapNumber(r5, r4, r7, heap_number_map, &slow);
ager@chromium.org357bf652010-04-12 11:30:10 +00007622 }
7623
lrn@chromium.org32d961d2010-06-30 09:09:34 +00007624 if (CpuFeatures::IsSupported(VFP3)) {
ager@chromium.org357bf652010-04-12 11:30:10 +00007625 CpuFeatures::Scope scope(VFP3);
7626 // Convert smi in r1 to double in d6.
7627 __ mov(r7, Operand(r1, ASR, kSmiTagSize));
7628 __ vmov(s13, r7);
7629 __ vcvt_f64_s32(d6, s13);
lrn@chromium.org32d961d2010-06-30 09:09:34 +00007630 if (!use_fp_registers) {
7631 __ vmov(r0, r1, d6);
7632 }
ager@chromium.org357bf652010-04-12 11:30:10 +00007633 } else {
7634 // Write Smi from r1 to r1 and r0 in double format.
7635 __ mov(r7, Operand(r1));
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00007636 ConvertToDoubleStub stub4(r1, r0, r7, r9);
ager@chromium.org357bf652010-04-12 11:30:10 +00007637 __ push(lr);
7638 __ Call(stub4.GetCode(), RelocInfo::CODE_TARGET);
7639 __ pop(lr);
7640 }
7641
7642 __ bind(&finished_loading_r1);
7643
7644 __ bind(&do_the_call);
7645 // If we are inlining the operation using VFP3 instructions for
7646 // add, subtract, multiply, or divide, the arguments are in d6 and d7.
7647 if (use_fp_registers) {
7648 CpuFeatures::Scope scope(VFP3);
7649 // ARMv7 VFP3 instructions to implement
7650 // double precision, add, subtract, multiply, divide.
7651
7652 if (Token::MUL == op_) {
7653 __ vmul(d5, d6, d7);
7654 } else if (Token::DIV == op_) {
7655 __ vdiv(d5, d6, d7);
7656 } else if (Token::ADD == op_) {
7657 __ vadd(d5, d6, d7);
7658 } else if (Token::SUB == op_) {
7659 __ vsub(d5, d6, d7);
7660 } else {
7661 UNREACHABLE();
7662 }
7663 __ sub(r0, r5, Operand(kHeapObjectTag));
7664 __ vstr(d5, r0, HeapNumber::kValueOffset);
7665 __ add(r0, r0, Operand(kHeapObjectTag));
7666 __ mov(pc, lr);
7667 } else {
7668 // If we did not inline the operation, then the arguments are in:
7669 // r0: Left value (least significant part of mantissa).
7670 // r1: Left value (sign, exponent, top of mantissa).
7671 // r2: Right value (least significant part of mantissa).
7672 // r3: Right value (sign, exponent, top of mantissa).
7673 // r5: Address of heap number for result.
7674
7675 __ push(lr); // For later.
7676 __ PrepareCallCFunction(4, r4); // Two doubles count as 4 arguments.
7677 // Call C routine that may not cause GC or other trouble. r5 is callee
7678 // save.
7679 __ CallCFunction(ExternalReference::double_fp_operation(op_), 4);
7680 // Store answer in the overwritable heap number.
7681 #if !defined(USE_ARM_EABI)
7682 // Double returned in fp coprocessor register 0 and 1, encoded as register
7683 // cr8. Offsets must be divisible by 4 for coprocessor so we need to
7684 // substract the tag from r5.
7685 __ sub(r4, r5, Operand(kHeapObjectTag));
7686 __ stc(p1, cr8, MemOperand(r4, HeapNumber::kValueOffset));
7687 #else
7688 // Double returned in registers 0 and 1.
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00007689 __ Strd(r0, r1, FieldMemOperand(r5, HeapNumber::kValueOffset));
ager@chromium.org357bf652010-04-12 11:30:10 +00007690 #endif
7691 __ mov(r0, Operand(r5));
7692 // And we are done.
7693 __ pop(pc);
7694 }
7695 }
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00007696
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00007697 if (lhs.is(r0)) {
7698 __ b(&slow);
7699 __ bind(&slow_reverse);
7700 __ Swap(r0, r1, ip);
7701 }
7702
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00007703 heap_number_map = no_reg; // Don't use this any more from here on.
7704
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007705 // We jump to here if something goes wrong (one param is not a number of any
7706 // sort or new-space allocation fails).
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007707 __ bind(&slow);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007708
7709 // Push arguments to the stack
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00007710 __ Push(r1, r0);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007711
ager@chromium.org357bf652010-04-12 11:30:10 +00007712 if (Token::ADD == op_) {
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007713 // Test for string arguments before calling runtime.
7714 // r1 : first argument
7715 // r0 : second argument
7716 // sp[0] : second argument
ager@chromium.org5c838252010-02-19 08:53:10 +00007717 // sp[4] : first argument
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007718
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007719 Label not_strings, not_string1, string1, string1_smi2;
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007720 __ tst(r1, Operand(kSmiTagMask));
7721 __ b(eq, &not_string1);
7722 __ CompareObjectType(r1, r2, r2, FIRST_NONSTRING_TYPE);
7723 __ b(ge, &not_string1);
7724
7725 // First argument is a a string, test second.
7726 __ tst(r0, Operand(kSmiTagMask));
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007727 __ b(eq, &string1_smi2);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007728 __ CompareObjectType(r0, r2, r2, FIRST_NONSTRING_TYPE);
7729 __ b(ge, &string1);
7730
7731 // First and second argument are strings.
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007732 StringAddStub string_add_stub(NO_STRING_CHECK_IN_STUB);
7733 __ TailCallStub(&string_add_stub);
7734
7735 __ bind(&string1_smi2);
7736 // First argument is a string, second is a smi. Try to lookup the number
7737 // string for the smi in the number string cache.
7738 NumberToStringStub::GenerateLookupNumberStringCache(
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00007739 masm, r0, r2, r4, r5, r6, true, &string1);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007740
7741 // Replace second argument on stack and tailcall string add stub to make
7742 // the result.
7743 __ str(r2, MemOperand(sp, 0));
7744 __ TailCallStub(&string_add_stub);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007745
7746 // Only first argument is a string.
7747 __ bind(&string1);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007748 __ InvokeBuiltin(Builtins::STRING_ADD_LEFT, JUMP_JS);
7749
7750 // First argument was not a string, test second.
7751 __ bind(&not_string1);
7752 __ tst(r0, Operand(kSmiTagMask));
7753 __ b(eq, &not_strings);
7754 __ CompareObjectType(r0, r2, r2, FIRST_NONSTRING_TYPE);
7755 __ b(ge, &not_strings);
7756
7757 // Only second argument is a string.
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007758 __ InvokeBuiltin(Builtins::STRING_ADD_RIGHT, JUMP_JS);
7759
7760 __ bind(&not_strings);
7761 }
7762
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007763 __ InvokeBuiltin(builtin, JUMP_JS); // Tail call. No return.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007764}
7765
7766
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007767// Tries to get a signed int32 out of a double precision floating point heap
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007768// number. Rounds towards 0. Fastest for doubles that are in the ranges
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007769// -0x7fffffff to -0x40000000 or 0x40000000 to 0x7fffffff. This corresponds
7770// almost to the range of signed int32 values that are not Smis. Jumps to the
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007771// label 'slow' if the double isn't in the range -0x80000000.0 to 0x80000000.0
7772// (excluding the endpoints).
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007773static void GetInt32(MacroAssembler* masm,
7774 Register source,
7775 Register dest,
7776 Register scratch,
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007777 Register scratch2,
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007778 Label* slow) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007779 Label right_exponent, done;
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007780 // Get exponent word.
7781 __ ldr(scratch, FieldMemOperand(source, HeapNumber::kExponentOffset));
7782 // Get exponent alone in scratch2.
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00007783 __ Ubfx(scratch2,
7784 scratch,
7785 HeapNumber::kExponentShift,
7786 HeapNumber::kExponentBits);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007787 // Load dest with zero. We use this either for the final shift or
7788 // for the answer.
7789 __ mov(dest, Operand(0));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007790 // Check whether the exponent matches a 32 bit signed int that is not a Smi.
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007791 // A non-Smi integer is 1.xxx * 2^30 so the exponent is 30 (biased). This is
7792 // the exponent that we are fastest at and also the highest exponent we can
7793 // handle here.
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00007794 const uint32_t non_smi_exponent = HeapNumber::kExponentBias + 30;
7795 // The non_smi_exponent, 0x41d, is too big for ARM's immediate field so we
7796 // split it up to avoid a constant pool entry. You can't do that in general
7797 // for cmp because of the overflow flag, but we know the exponent is in the
7798 // range 0-2047 so there is no overflow.
7799 int fudge_factor = 0x400;
7800 __ sub(scratch2, scratch2, Operand(fudge_factor));
7801 __ cmp(scratch2, Operand(non_smi_exponent - fudge_factor));
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007802 // If we have a match of the int32-but-not-Smi exponent then skip some logic.
7803 __ b(eq, &right_exponent);
7804 // If the exponent is higher than that then go to slow case. This catches
7805 // numbers that don't fit in a signed int32, infinities and NaNs.
7806 __ b(gt, slow);
7807
7808 // We know the exponent is smaller than 30 (biased). If it is less than
7809 // 0 (biased) then the number is smaller in magnitude than 1.0 * 2^0, ie
7810 // it rounds to zero.
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00007811 const uint32_t zero_exponent = HeapNumber::kExponentBias + 0;
7812 __ sub(scratch2, scratch2, Operand(zero_exponent - fudge_factor), SetCC);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007813 // Dest already has a Smi zero.
7814 __ b(lt, &done);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007815 if (!CpuFeatures::IsSupported(VFP3)) {
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00007816 // We have an exponent between 0 and 30 in scratch2. Subtract from 30 to
7817 // get how much to shift down.
7818 __ rsb(dest, scratch2, Operand(30));
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007819 }
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007820 __ bind(&right_exponent);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007821 if (CpuFeatures::IsSupported(VFP3)) {
7822 CpuFeatures::Scope scope(VFP3);
7823 // ARMv7 VFP3 instructions implementing double precision to integer
7824 // conversion using round to zero.
7825 __ ldr(scratch2, FieldMemOperand(source, HeapNumber::kMantissaOffset));
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00007826 __ vmov(d7, scratch2, scratch);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007827 __ vcvt_s32_f64(s15, d7);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00007828 __ vmov(dest, s15);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007829 } else {
7830 // Get the top bits of the mantissa.
7831 __ and_(scratch2, scratch, Operand(HeapNumber::kMantissaMask));
7832 // Put back the implicit 1.
7833 __ orr(scratch2, scratch2, Operand(1 << HeapNumber::kExponentShift));
7834 // Shift up the mantissa bits to take up the space the exponent used to
7835 // take. We just orred in the implicit bit so that took care of one and
7836 // we want to leave the sign bit 0 so we subtract 2 bits from the shift
7837 // distance.
7838 const int shift_distance = HeapNumber::kNonMantissaBitsInTopWord - 2;
7839 __ mov(scratch2, Operand(scratch2, LSL, shift_distance));
7840 // Put sign in zero flag.
7841 __ tst(scratch, Operand(HeapNumber::kSignMask));
7842 // Get the second half of the double. For some exponents we don't
7843 // actually need this because the bits get shifted out again, but
7844 // it's probably slower to test than just to do it.
7845 __ ldr(scratch, FieldMemOperand(source, HeapNumber::kMantissaOffset));
7846 // Shift down 22 bits to get the last 10 bits.
7847 __ orr(scratch, scratch2, Operand(scratch, LSR, 32 - shift_distance));
7848 // Move down according to the exponent.
7849 __ mov(dest, Operand(scratch, LSR, dest));
7850 // Fix sign if sign bit was set.
7851 __ rsb(dest, dest, Operand(0), LeaveCC, ne);
7852 }
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007853 __ bind(&done);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007854}
7855
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007856// For bitwise ops where the inputs are not both Smis we here try to determine
7857// whether both inputs are either Smis or at least heap numbers that can be
7858// represented by a 32 bit signed value. We truncate towards zero as required
7859// by the ES spec. If this is the case we do the bitwise op and see if the
7860// result is a Smi. If so, great, otherwise we try to find a heap number to
7861// write the answer into (either by allocating or by overwriting).
ager@chromium.org357bf652010-04-12 11:30:10 +00007862// On entry the operands are in lhs and rhs. On exit the answer is in r0.
7863void GenericBinaryOpStub::HandleNonSmiBitwiseOp(MacroAssembler* masm,
7864 Register lhs,
7865 Register rhs) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007866 Label slow, result_not_a_smi;
ager@chromium.org357bf652010-04-12 11:30:10 +00007867 Label rhs_is_smi, lhs_is_smi;
7868 Label done_checking_rhs, done_checking_lhs;
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007869
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00007870 Register heap_number_map = r6;
7871 __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
7872
ager@chromium.org357bf652010-04-12 11:30:10 +00007873 __ tst(lhs, Operand(kSmiTagMask));
7874 __ 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 +00007875 __ ldr(r4, FieldMemOperand(lhs, HeapNumber::kMapOffset));
7876 __ cmp(r4, heap_number_map);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007877 __ b(ne, &slow);
ager@chromium.org357bf652010-04-12 11:30:10 +00007878 GetInt32(masm, lhs, r3, r5, r4, &slow);
7879 __ jmp(&done_checking_lhs);
7880 __ bind(&lhs_is_smi);
7881 __ mov(r3, Operand(lhs, ASR, 1));
7882 __ bind(&done_checking_lhs);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007883
ager@chromium.org357bf652010-04-12 11:30:10 +00007884 __ tst(rhs, Operand(kSmiTagMask));
7885 __ 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 +00007886 __ ldr(r4, FieldMemOperand(rhs, HeapNumber::kMapOffset));
7887 __ cmp(r4, heap_number_map);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007888 __ b(ne, &slow);
ager@chromium.org357bf652010-04-12 11:30:10 +00007889 GetInt32(masm, rhs, r2, r5, r4, &slow);
7890 __ jmp(&done_checking_rhs);
7891 __ bind(&rhs_is_smi);
7892 __ mov(r2, Operand(rhs, ASR, 1));
7893 __ bind(&done_checking_rhs);
7894
7895 ASSERT(((lhs.is(r0) && rhs.is(r1)) || (lhs.is(r1) && rhs.is(r0))));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007896
7897 // r0 and r1: Original operands (Smi or heap numbers).
7898 // r2 and r3: Signed int32 operands.
7899 switch (op_) {
7900 case Token::BIT_OR: __ orr(r2, r2, Operand(r3)); break;
7901 case Token::BIT_XOR: __ eor(r2, r2, Operand(r3)); break;
7902 case Token::BIT_AND: __ and_(r2, r2, Operand(r3)); break;
7903 case Token::SAR:
7904 // Use only the 5 least significant bits of the shift count.
7905 __ and_(r2, r2, Operand(0x1f));
7906 __ mov(r2, Operand(r3, ASR, r2));
7907 break;
7908 case Token::SHR:
7909 // Use only the 5 least significant bits of the shift count.
7910 __ and_(r2, r2, Operand(0x1f));
7911 __ mov(r2, Operand(r3, LSR, r2), SetCC);
7912 // SHR is special because it is required to produce a positive answer.
7913 // The code below for writing into heap numbers isn't capable of writing
7914 // the register as an unsigned int so we go to slow case if we hit this
7915 // case.
7916 __ b(mi, &slow);
7917 break;
7918 case Token::SHL:
7919 // Use only the 5 least significant bits of the shift count.
7920 __ and_(r2, r2, Operand(0x1f));
7921 __ mov(r2, Operand(r3, LSL, r2));
7922 break;
7923 default: UNREACHABLE();
7924 }
7925 // check that the *signed* result fits in a smi
7926 __ add(r3, r2, Operand(0x40000000), SetCC);
7927 __ b(mi, &result_not_a_smi);
7928 __ mov(r0, Operand(r2, LSL, kSmiTagSize));
7929 __ Ret();
7930
7931 Label have_to_allocate, got_a_heap_number;
7932 __ bind(&result_not_a_smi);
7933 switch (mode_) {
7934 case OVERWRITE_RIGHT: {
ager@chromium.org357bf652010-04-12 11:30:10 +00007935 __ tst(rhs, Operand(kSmiTagMask));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007936 __ b(eq, &have_to_allocate);
ager@chromium.org357bf652010-04-12 11:30:10 +00007937 __ mov(r5, Operand(rhs));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007938 break;
7939 }
7940 case OVERWRITE_LEFT: {
ager@chromium.org357bf652010-04-12 11:30:10 +00007941 __ tst(lhs, Operand(kSmiTagMask));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007942 __ b(eq, &have_to_allocate);
ager@chromium.org357bf652010-04-12 11:30:10 +00007943 __ mov(r5, Operand(lhs));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007944 break;
7945 }
7946 case NO_OVERWRITE: {
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00007947 // Get a new heap number in r5. r4 and r7 are scratch.
7948 __ AllocateHeapNumber(r5, r4, r7, heap_number_map, &slow);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007949 }
7950 default: break;
7951 }
7952 __ bind(&got_a_heap_number);
7953 // r2: Answer as signed int32.
7954 // r5: Heap number to write answer into.
7955
7956 // Nothing can go wrong now, so move the heap number to r0, which is the
7957 // result.
7958 __ mov(r0, Operand(r5));
7959
7960 // Tail call that writes the int32 in r2 to the heap number in r0, using
7961 // r3 as scratch. r0 is preserved and returned.
7962 WriteInt32ToHeapNumberStub stub(r2, r0, r3);
7963 __ Jump(stub.GetCode(), RelocInfo::CODE_TARGET);
7964
7965 if (mode_ != NO_OVERWRITE) {
7966 __ bind(&have_to_allocate);
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00007967 // Get a new heap number in r5. r4 and r7 are scratch.
7968 __ AllocateHeapNumber(r5, r4, r7, heap_number_map, &slow);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007969 __ jmp(&got_a_heap_number);
7970 }
7971
7972 // If all else failed then we go to the runtime system.
7973 __ bind(&slow);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00007974 __ Push(lhs, rhs); // Restore stack.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007975 switch (op_) {
7976 case Token::BIT_OR:
7977 __ InvokeBuiltin(Builtins::BIT_OR, JUMP_JS);
7978 break;
7979 case Token::BIT_AND:
7980 __ InvokeBuiltin(Builtins::BIT_AND, JUMP_JS);
7981 break;
7982 case Token::BIT_XOR:
7983 __ InvokeBuiltin(Builtins::BIT_XOR, JUMP_JS);
7984 break;
7985 case Token::SAR:
7986 __ InvokeBuiltin(Builtins::SAR, JUMP_JS);
7987 break;
7988 case Token::SHR:
7989 __ InvokeBuiltin(Builtins::SHR, JUMP_JS);
7990 break;
7991 case Token::SHL:
7992 __ InvokeBuiltin(Builtins::SHL, JUMP_JS);
7993 break;
7994 default:
7995 UNREACHABLE();
7996 }
7997}
7998
7999
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008000// Can we multiply by x with max two shifts and an add.
8001// This answers yes to all integers from 2 to 10.
8002static bool IsEasyToMultiplyBy(int x) {
8003 if (x < 2) return false; // Avoid special cases.
8004 if (x > (Smi::kMaxValue + 1) >> 2) return false; // Almost always overflows.
8005 if (IsPowerOf2(x)) return true; // Simple shift.
8006 if (PopCountLessThanEqual2(x)) return true; // Shift and add and shift.
8007 if (IsPowerOf2(x + 1)) return true; // Patterns like 11111.
8008 return false;
8009}
8010
8011
8012// Can multiply by anything that IsEasyToMultiplyBy returns true for.
8013// Source and destination may be the same register. This routine does
8014// not set carry and overflow the way a mul instruction would.
8015static void MultiplyByKnownInt(MacroAssembler* masm,
8016 Register source,
8017 Register destination,
8018 int known_int) {
8019 if (IsPowerOf2(known_int)) {
8020 __ mov(destination, Operand(source, LSL, BitPosition(known_int)));
8021 } else if (PopCountLessThanEqual2(known_int)) {
8022 int first_bit = BitPosition(known_int);
8023 int second_bit = BitPosition(known_int ^ (1 << first_bit));
8024 __ add(destination, source, Operand(source, LSL, second_bit - first_bit));
8025 if (first_bit != 0) {
8026 __ mov(destination, Operand(destination, LSL, first_bit));
8027 }
8028 } else {
8029 ASSERT(IsPowerOf2(known_int + 1)); // Patterns like 1111.
8030 int the_bit = BitPosition(known_int + 1);
8031 __ rsb(destination, source, Operand(source, LSL, the_bit));
8032 }
8033}
8034
8035
8036// This function (as opposed to MultiplyByKnownInt) takes the known int in a
8037// a register for the cases where it doesn't know a good trick, and may deliver
8038// a result that needs shifting.
8039static void MultiplyByKnownInt2(
8040 MacroAssembler* masm,
8041 Register result,
8042 Register source,
8043 Register known_int_register, // Smi tagged.
8044 int known_int,
8045 int* required_shift) { // Including Smi tag shift
8046 switch (known_int) {
8047 case 3:
8048 __ add(result, source, Operand(source, LSL, 1));
8049 *required_shift = 1;
8050 break;
8051 case 5:
8052 __ add(result, source, Operand(source, LSL, 2));
8053 *required_shift = 1;
8054 break;
8055 case 6:
8056 __ add(result, source, Operand(source, LSL, 1));
8057 *required_shift = 2;
8058 break;
8059 case 7:
8060 __ rsb(result, source, Operand(source, LSL, 3));
8061 *required_shift = 1;
8062 break;
8063 case 9:
8064 __ add(result, source, Operand(source, LSL, 3));
8065 *required_shift = 1;
8066 break;
8067 case 10:
8068 __ add(result, source, Operand(source, LSL, 2));
8069 *required_shift = 2;
8070 break;
8071 default:
8072 ASSERT(!IsPowerOf2(known_int)); // That would be very inefficient.
8073 __ mul(result, source, known_int_register);
8074 *required_shift = 0;
8075 }
8076}
8077
8078
lrn@chromium.org32d961d2010-06-30 09:09:34 +00008079// This uses versions of the sum-of-digits-to-see-if-a-number-is-divisible-by-3
8080// trick. See http://en.wikipedia.org/wiki/Divisibility_rule
8081// Takes the sum of the digits base (mask + 1) repeatedly until we have a
8082// number from 0 to mask. On exit the 'eq' condition flags are set if the
8083// answer is exactly the mask.
8084void IntegerModStub::DigitSum(MacroAssembler* masm,
8085 Register lhs,
8086 int mask,
8087 int shift,
8088 Label* entry) {
8089 ASSERT(mask > 0);
8090 ASSERT(mask <= 0xff); // This ensures we don't need ip to use it.
8091 Label loop;
8092 __ bind(&loop);
8093 __ and_(ip, lhs, Operand(mask));
8094 __ add(lhs, ip, Operand(lhs, LSR, shift));
8095 __ bind(entry);
8096 __ cmp(lhs, Operand(mask));
8097 __ b(gt, &loop);
8098}
8099
8100
8101void IntegerModStub::DigitSum(MacroAssembler* masm,
8102 Register lhs,
8103 Register scratch,
8104 int mask,
8105 int shift1,
8106 int shift2,
8107 Label* entry) {
8108 ASSERT(mask > 0);
8109 ASSERT(mask <= 0xff); // This ensures we don't need ip to use it.
8110 Label loop;
8111 __ bind(&loop);
8112 __ bic(scratch, lhs, Operand(mask));
8113 __ and_(ip, lhs, Operand(mask));
8114 __ add(lhs, ip, Operand(lhs, LSR, shift1));
8115 __ add(lhs, lhs, Operand(scratch, LSR, shift2));
8116 __ bind(entry);
8117 __ cmp(lhs, Operand(mask));
8118 __ b(gt, &loop);
8119}
8120
8121
8122// Splits the number into two halves (bottom half has shift bits). The top
8123// half is subtracted from the bottom half. If the result is negative then
8124// rhs is added.
8125void IntegerModStub::ModGetInRangeBySubtraction(MacroAssembler* masm,
8126 Register lhs,
8127 int shift,
8128 int rhs) {
8129 int mask = (1 << shift) - 1;
8130 __ and_(ip, lhs, Operand(mask));
8131 __ sub(lhs, ip, Operand(lhs, LSR, shift), SetCC);
8132 __ add(lhs, lhs, Operand(rhs), LeaveCC, mi);
8133}
8134
8135
8136void IntegerModStub::ModReduce(MacroAssembler* masm,
8137 Register lhs,
8138 int max,
8139 int denominator) {
8140 int limit = denominator;
8141 while (limit * 2 <= max) limit *= 2;
8142 while (limit >= denominator) {
8143 __ cmp(lhs, Operand(limit));
8144 __ sub(lhs, lhs, Operand(limit), LeaveCC, ge);
8145 limit >>= 1;
8146 }
8147}
8148
8149
8150void IntegerModStub::ModAnswer(MacroAssembler* masm,
8151 Register result,
8152 Register shift_distance,
8153 Register mask_bits,
8154 Register sum_of_digits) {
8155 __ add(result, mask_bits, Operand(sum_of_digits, LSL, shift_distance));
8156 __ Ret();
8157}
8158
8159
8160// See comment for class.
8161void IntegerModStub::Generate(MacroAssembler* masm) {
8162 __ mov(lhs_, Operand(lhs_, LSR, shift_distance_));
8163 __ bic(odd_number_, odd_number_, Operand(1));
8164 __ mov(odd_number_, Operand(odd_number_, LSL, 1));
8165 // We now have (odd_number_ - 1) * 2 in the register.
8166 // Build a switch out of branches instead of data because it avoids
8167 // having to teach the assembler about intra-code-object pointers
8168 // that are not in relative branch instructions.
8169 Label mod3, mod5, mod7, mod9, mod11, mod13, mod15, mod17, mod19;
8170 Label mod21, mod23, mod25;
8171 { Assembler::BlockConstPoolScope block_const_pool(masm);
8172 __ add(pc, pc, Operand(odd_number_));
8173 // When you read pc it is always 8 ahead, but when you write it you always
8174 // write the actual value. So we put in two nops to take up the slack.
8175 __ nop();
8176 __ nop();
8177 __ b(&mod3);
8178 __ b(&mod5);
8179 __ b(&mod7);
8180 __ b(&mod9);
8181 __ b(&mod11);
8182 __ b(&mod13);
8183 __ b(&mod15);
8184 __ b(&mod17);
8185 __ b(&mod19);
8186 __ b(&mod21);
8187 __ b(&mod23);
8188 __ b(&mod25);
8189 }
8190
8191 // For each denominator we find a multiple that is almost only ones
8192 // when expressed in binary. Then we do the sum-of-digits trick for
8193 // that number. If the multiple is not 1 then we have to do a little
8194 // more work afterwards to get the answer into the 0-denominator-1
8195 // range.
8196 DigitSum(masm, lhs_, 3, 2, &mod3); // 3 = b11.
8197 __ sub(lhs_, lhs_, Operand(3), LeaveCC, eq);
8198 ModAnswer(masm, result_, shift_distance_, mask_bits_, lhs_);
8199
8200 DigitSum(masm, lhs_, 0xf, 4, &mod5); // 5 * 3 = b1111.
8201 ModGetInRangeBySubtraction(masm, lhs_, 2, 5);
8202 ModAnswer(masm, result_, shift_distance_, mask_bits_, lhs_);
8203
8204 DigitSum(masm, lhs_, 7, 3, &mod7); // 7 = b111.
8205 __ sub(lhs_, lhs_, Operand(7), LeaveCC, eq);
8206 ModAnswer(masm, result_, shift_distance_, mask_bits_, lhs_);
8207
8208 DigitSum(masm, lhs_, 0x3f, 6, &mod9); // 7 * 9 = b111111.
8209 ModGetInRangeBySubtraction(masm, lhs_, 3, 9);
8210 ModAnswer(masm, result_, shift_distance_, mask_bits_, lhs_);
8211
8212 DigitSum(masm, lhs_, r5, 0x3f, 6, 3, &mod11); // 5 * 11 = b110111.
8213 ModReduce(masm, lhs_, 0x3f, 11);
8214 ModAnswer(masm, result_, shift_distance_, mask_bits_, lhs_);
8215
8216 DigitSum(masm, lhs_, r5, 0xff, 8, 5, &mod13); // 19 * 13 = b11110111.
8217 ModReduce(masm, lhs_, 0xff, 13);
8218 ModAnswer(masm, result_, shift_distance_, mask_bits_, lhs_);
8219
8220 DigitSum(masm, lhs_, 0xf, 4, &mod15); // 15 = b1111.
8221 __ sub(lhs_, lhs_, Operand(15), LeaveCC, eq);
8222 ModAnswer(masm, result_, shift_distance_, mask_bits_, lhs_);
8223
8224 DigitSum(masm, lhs_, 0xff, 8, &mod17); // 15 * 17 = b11111111.
8225 ModGetInRangeBySubtraction(masm, lhs_, 4, 17);
8226 ModAnswer(masm, result_, shift_distance_, mask_bits_, lhs_);
8227
8228 DigitSum(masm, lhs_, r5, 0xff, 8, 5, &mod19); // 13 * 19 = b11110111.
8229 ModReduce(masm, lhs_, 0xff, 19);
8230 ModAnswer(masm, result_, shift_distance_, mask_bits_, lhs_);
8231
8232 DigitSum(masm, lhs_, 0x3f, 6, &mod21); // 3 * 21 = b111111.
8233 ModReduce(masm, lhs_, 0x3f, 21);
8234 ModAnswer(masm, result_, shift_distance_, mask_bits_, lhs_);
8235
8236 DigitSum(masm, lhs_, r5, 0xff, 8, 7, &mod23); // 11 * 23 = b11111101.
8237 ModReduce(masm, lhs_, 0xff, 23);
8238 ModAnswer(masm, result_, shift_distance_, mask_bits_, lhs_);
8239
8240 DigitSum(masm, lhs_, r5, 0x7f, 7, 6, &mod25); // 5 * 25 = b1111101.
8241 ModReduce(masm, lhs_, 0x7f, 25);
8242 ModAnswer(masm, result_, shift_distance_, mask_bits_, lhs_);
8243}
8244
8245
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00008246const char* GenericBinaryOpStub::GetName() {
8247 if (name_ != NULL) return name_;
8248 const int len = 100;
8249 name_ = Bootstrapper::AllocateAutoDeletedArray(len);
8250 if (name_ == NULL) return "OOM";
8251 const char* op_name = Token::Name(op_);
8252 const char* overwrite_name;
8253 switch (mode_) {
8254 case NO_OVERWRITE: overwrite_name = "Alloc"; break;
8255 case OVERWRITE_RIGHT: overwrite_name = "OverwriteRight"; break;
8256 case OVERWRITE_LEFT: overwrite_name = "OverwriteLeft"; break;
8257 default: overwrite_name = "UnknownOverwrite"; break;
8258 }
8259
8260 OS::SNPrintF(Vector<char>(name_, len),
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00008261 "GenericBinaryOpStub_%s_%s%s_%s",
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00008262 op_name,
8263 overwrite_name,
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00008264 specialized_on_rhs_ ? "_ConstantRhs" : "",
8265 BinaryOpIC::GetName(runtime_operands_type_));
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00008266 return name_;
8267}
8268
8269
ager@chromium.org5c838252010-02-19 08:53:10 +00008270
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008271void GenericBinaryOpStub::Generate(MacroAssembler* masm) {
ager@chromium.org357bf652010-04-12 11:30:10 +00008272 // lhs_ : x
8273 // rhs_ : y
8274 // r0 : result
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008275
ager@chromium.org357bf652010-04-12 11:30:10 +00008276 Register result = r0;
8277 Register lhs = lhs_;
8278 Register rhs = rhs_;
8279
8280 // This code can't cope with other register allocations yet.
8281 ASSERT(result.is(r0) &&
8282 ((lhs.is(r0) && rhs.is(r1)) ||
8283 (lhs.is(r1) && rhs.is(r0))));
8284
8285 Register smi_test_reg = VirtualFrame::scratch0();
8286 Register scratch = VirtualFrame::scratch1();
8287
8288 // All ops need to know whether we are dealing with two Smis. Set up
8289 // smi_test_reg to tell us that.
8290 if (ShouldGenerateSmiCode()) {
8291 __ orr(smi_test_reg, lhs, Operand(rhs));
8292 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008293
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008294 switch (op_) {
8295 case Token::ADD: {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008296 Label not_smi;
8297 // Fast path.
ager@chromium.org357bf652010-04-12 11:30:10 +00008298 if (ShouldGenerateSmiCode()) {
8299 ASSERT(kSmiTag == 0); // Adjust code below.
8300 __ tst(smi_test_reg, Operand(kSmiTagMask));
8301 __ b(ne, &not_smi);
8302 __ add(r0, r1, Operand(r0), SetCC); // Add y optimistically.
8303 // Return if no overflow.
8304 __ Ret(vc);
8305 __ sub(r0, r0, Operand(r1)); // Revert optimistic add.
8306 }
8307 HandleBinaryOpSlowCases(masm, &not_smi, lhs, rhs, Builtins::ADD);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008308 break;
8309 }
8310
8311 case Token::SUB: {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008312 Label not_smi;
8313 // Fast path.
ager@chromium.org357bf652010-04-12 11:30:10 +00008314 if (ShouldGenerateSmiCode()) {
8315 ASSERT(kSmiTag == 0); // Adjust code below.
8316 __ tst(smi_test_reg, Operand(kSmiTagMask));
8317 __ b(ne, &not_smi);
8318 if (lhs.is(r1)) {
8319 __ sub(r0, r1, Operand(r0), SetCC); // Subtract y optimistically.
8320 // Return if no overflow.
8321 __ Ret(vc);
8322 __ sub(r0, r1, Operand(r0)); // Revert optimistic subtract.
8323 } else {
8324 __ sub(r0, r0, Operand(r1), SetCC); // Subtract y optimistically.
8325 // Return if no overflow.
8326 __ Ret(vc);
8327 __ add(r0, r0, Operand(r1)); // Revert optimistic subtract.
8328 }
8329 }
8330 HandleBinaryOpSlowCases(masm, &not_smi, lhs, rhs, Builtins::SUB);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008331 break;
8332 }
8333
8334 case Token::MUL: {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008335 Label not_smi, slow;
ager@chromium.org357bf652010-04-12 11:30:10 +00008336 if (ShouldGenerateSmiCode()) {
8337 ASSERT(kSmiTag == 0); // adjust code below
8338 __ tst(smi_test_reg, Operand(kSmiTagMask));
8339 Register scratch2 = smi_test_reg;
8340 smi_test_reg = no_reg;
8341 __ b(ne, &not_smi);
8342 // Remove tag from one operand (but keep sign), so that result is Smi.
8343 __ mov(ip, Operand(rhs, ASR, kSmiTagSize));
8344 // Do multiplication
8345 // scratch = lower 32 bits of ip * lhs.
8346 __ smull(scratch, scratch2, lhs, ip);
8347 // Go slow on overflows (overflow bit is not set).
8348 __ mov(ip, Operand(scratch, ASR, 31));
8349 // No overflow if higher 33 bits are identical.
8350 __ cmp(ip, Operand(scratch2));
8351 __ b(ne, &slow);
8352 // Go slow on zero result to handle -0.
8353 __ tst(scratch, Operand(scratch));
8354 __ mov(result, Operand(scratch), LeaveCC, ne);
8355 __ Ret(ne);
8356 // We need -0 if we were multiplying a negative number with 0 to get 0.
8357 // We know one of them was zero.
8358 __ add(scratch2, rhs, Operand(lhs), SetCC);
8359 __ mov(result, Operand(Smi::FromInt(0)), LeaveCC, pl);
8360 __ Ret(pl); // Return Smi 0 if the non-zero one was positive.
8361 // Slow case. We fall through here if we multiplied a negative number
8362 // with 0, because that would mean we should produce -0.
8363 __ bind(&slow);
8364 }
8365 HandleBinaryOpSlowCases(masm, &not_smi, lhs, rhs, Builtins::MUL);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008366 break;
8367 }
8368
8369 case Token::DIV:
8370 case Token::MOD: {
8371 Label not_smi;
ager@chromium.org357bf652010-04-12 11:30:10 +00008372 if (ShouldGenerateSmiCode() && specialized_on_rhs_) {
lrn@chromium.org32d961d2010-06-30 09:09:34 +00008373 Label lhs_is_unsuitable;
ager@chromium.org357bf652010-04-12 11:30:10 +00008374 __ BranchOnNotSmi(lhs, &not_smi);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008375 if (IsPowerOf2(constant_rhs_)) {
8376 if (op_ == Token::MOD) {
ager@chromium.org357bf652010-04-12 11:30:10 +00008377 __ and_(rhs,
8378 lhs,
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008379 Operand(0x80000000u | ((constant_rhs_ << kSmiTagSize) - 1)),
8380 SetCC);
8381 // We now have the answer, but if the input was negative we also
8382 // have the sign bit. Our work is done if the result is
8383 // positive or zero:
ager@chromium.org357bf652010-04-12 11:30:10 +00008384 if (!rhs.is(r0)) {
8385 __ mov(r0, rhs, LeaveCC, pl);
8386 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008387 __ Ret(pl);
8388 // A mod of a negative left hand side must return a negative number.
8389 // Unfortunately if the answer is 0 then we must return -0. And we
ager@chromium.org357bf652010-04-12 11:30:10 +00008390 // already optimistically trashed rhs so we may need to restore it.
8391 __ eor(rhs, rhs, Operand(0x80000000u), SetCC);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008392 // Next two instructions are conditional on the answer being -0.
ager@chromium.org357bf652010-04-12 11:30:10 +00008393 __ mov(rhs, Operand(Smi::FromInt(constant_rhs_)), LeaveCC, eq);
lrn@chromium.org32d961d2010-06-30 09:09:34 +00008394 __ b(eq, &lhs_is_unsuitable);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008395 // We need to subtract the dividend. Eg. -3 % 4 == -3.
ager@chromium.org357bf652010-04-12 11:30:10 +00008396 __ sub(result, rhs, Operand(Smi::FromInt(constant_rhs_)));
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008397 } else {
8398 ASSERT(op_ == Token::DIV);
ager@chromium.org357bf652010-04-12 11:30:10 +00008399 __ tst(lhs,
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008400 Operand(0x80000000u | ((constant_rhs_ << kSmiTagSize) - 1)));
lrn@chromium.org32d961d2010-06-30 09:09:34 +00008401 __ b(ne, &lhs_is_unsuitable); // Go slow on negative or remainder.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008402 int shift = 0;
8403 int d = constant_rhs_;
8404 while ((d & 1) == 0) {
8405 d >>= 1;
8406 shift++;
8407 }
ager@chromium.org357bf652010-04-12 11:30:10 +00008408 __ mov(r0, Operand(lhs, LSR, shift));
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008409 __ bic(r0, r0, Operand(kSmiTagMask));
8410 }
8411 } else {
8412 // Not a power of 2.
ager@chromium.org357bf652010-04-12 11:30:10 +00008413 __ tst(lhs, Operand(0x80000000u));
lrn@chromium.org32d961d2010-06-30 09:09:34 +00008414 __ b(ne, &lhs_is_unsuitable);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008415 // Find a fixed point reciprocal of the divisor so we can divide by
8416 // multiplying.
8417 double divisor = 1.0 / constant_rhs_;
8418 int shift = 32;
8419 double scale = 4294967296.0; // 1 << 32.
8420 uint32_t mul;
8421 // Maximise the precision of the fixed point reciprocal.
8422 while (true) {
8423 mul = static_cast<uint32_t>(scale * divisor);
8424 if (mul >= 0x7fffffff) break;
8425 scale *= 2.0;
8426 shift++;
8427 }
8428 mul++;
ager@chromium.org357bf652010-04-12 11:30:10 +00008429 Register scratch2 = smi_test_reg;
8430 smi_test_reg = no_reg;
8431 __ mov(scratch2, Operand(mul));
8432 __ umull(scratch, scratch2, scratch2, lhs);
8433 __ mov(scratch2, Operand(scratch2, LSR, shift - 31));
8434 // scratch2 is lhs / rhs. scratch2 is not Smi tagged.
8435 // rhs is still the known rhs. rhs is Smi tagged.
8436 // lhs is still the unkown lhs. lhs is Smi tagged.
8437 int required_scratch_shift = 0; // Including the Smi tag shift of 1.
8438 // scratch = scratch2 * rhs.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008439 MultiplyByKnownInt2(masm,
ager@chromium.org357bf652010-04-12 11:30:10 +00008440 scratch,
8441 scratch2,
8442 rhs,
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008443 constant_rhs_,
ager@chromium.org357bf652010-04-12 11:30:10 +00008444 &required_scratch_shift);
8445 // scratch << required_scratch_shift is now the Smi tagged rhs *
8446 // (lhs / rhs) where / indicates integer division.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008447 if (op_ == Token::DIV) {
ager@chromium.org357bf652010-04-12 11:30:10 +00008448 __ cmp(lhs, Operand(scratch, LSL, required_scratch_shift));
lrn@chromium.org32d961d2010-06-30 09:09:34 +00008449 __ b(ne, &lhs_is_unsuitable); // There was a remainder.
ager@chromium.org357bf652010-04-12 11:30:10 +00008450 __ mov(result, Operand(scratch2, LSL, kSmiTagSize));
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008451 } else {
8452 ASSERT(op_ == Token::MOD);
ager@chromium.org357bf652010-04-12 11:30:10 +00008453 __ sub(result, lhs, Operand(scratch, LSL, required_scratch_shift));
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008454 }
8455 }
8456 __ Ret();
lrn@chromium.org32d961d2010-06-30 09:09:34 +00008457 __ bind(&lhs_is_unsuitable);
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00008458 } else if (op_ == Token::MOD &&
8459 runtime_operands_type_ != BinaryOpIC::HEAP_NUMBERS &&
8460 runtime_operands_type_ != BinaryOpIC::STRINGS) {
8461 // Do generate a bit of smi code for modulus even though the default for
8462 // modulus is not to do it, but as the ARM processor has no coprocessor
lrn@chromium.org32d961d2010-06-30 09:09:34 +00008463 // support for modulus checking for smis makes sense. We can handle
8464 // 1 to 25 times any power of 2. This covers over half the numbers from
8465 // 1 to 100 including all of the first 25. (Actually the constants < 10
8466 // are handled above by reciprocal multiplication. We only get here for
8467 // those cases if the right hand side is not a constant or for cases
8468 // like 192 which is 3*2^6 and ends up in the 3 case in the integer mod
8469 // stub.)
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00008470 Label slow;
lrn@chromium.org32d961d2010-06-30 09:09:34 +00008471 Label not_power_of_2;
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00008472 ASSERT(!ShouldGenerateSmiCode());
8473 ASSERT(kSmiTag == 0); // Adjust code below.
8474 // Check for two positive smis.
8475 __ orr(smi_test_reg, lhs, Operand(rhs));
8476 __ tst(smi_test_reg, Operand(0x80000000u | kSmiTagMask));
8477 __ b(ne, &slow);
8478 // Check that rhs is a power of two and not zero.
lrn@chromium.org32d961d2010-06-30 09:09:34 +00008479 Register mask_bits = r3;
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00008480 __ sub(scratch, rhs, Operand(1), SetCC);
8481 __ b(mi, &slow);
lrn@chromium.org32d961d2010-06-30 09:09:34 +00008482 __ and_(mask_bits, rhs, Operand(scratch), SetCC);
8483 __ b(ne, &not_power_of_2);
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00008484 // Calculate power of two modulus.
8485 __ and_(result, lhs, Operand(scratch));
8486 __ Ret();
lrn@chromium.org32d961d2010-06-30 09:09:34 +00008487
8488 __ bind(&not_power_of_2);
8489 __ eor(scratch, scratch, Operand(mask_bits));
8490 // At least two bits are set in the modulus. The high one(s) are in
8491 // mask_bits and the low one is scratch + 1.
8492 __ and_(mask_bits, scratch, Operand(lhs));
8493 Register shift_distance = scratch;
8494 scratch = no_reg;
8495
8496 // The rhs consists of a power of 2 multiplied by some odd number.
8497 // The power-of-2 part we handle by putting the corresponding bits
8498 // from the lhs in the mask_bits register, and the power in the
8499 // shift_distance register. Shift distance is never 0 due to Smi
8500 // tagging.
8501 __ CountLeadingZeros(r4, shift_distance, shift_distance);
8502 __ rsb(shift_distance, r4, Operand(32));
8503
8504 // Now we need to find out what the odd number is. The last bit is
8505 // always 1.
8506 Register odd_number = r4;
8507 __ mov(odd_number, Operand(rhs, LSR, shift_distance));
8508 __ cmp(odd_number, Operand(25));
8509 __ b(gt, &slow);
8510
8511 IntegerModStub stub(
8512 result, shift_distance, odd_number, mask_bits, lhs, r5);
8513 __ Jump(stub.GetCode(), RelocInfo::CODE_TARGET); // Tail call.
8514
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00008515 __ bind(&slow);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008516 }
ager@chromium.org357bf652010-04-12 11:30:10 +00008517 HandleBinaryOpSlowCases(
8518 masm,
8519 &not_smi,
8520 lhs,
8521 rhs,
8522 op_ == Token::MOD ? Builtins::MOD : Builtins::DIV);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008523 break;
8524 }
8525
8526 case Token::BIT_OR:
8527 case Token::BIT_AND:
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008528 case Token::BIT_XOR:
8529 case Token::SAR:
8530 case Token::SHR:
8531 case Token::SHL: {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008532 Label slow;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008533 ASSERT(kSmiTag == 0); // adjust code below
ager@chromium.org357bf652010-04-12 11:30:10 +00008534 __ tst(smi_test_reg, Operand(kSmiTagMask));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008535 __ b(ne, &slow);
ager@chromium.org357bf652010-04-12 11:30:10 +00008536 Register scratch2 = smi_test_reg;
8537 smi_test_reg = no_reg;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008538 switch (op_) {
ager@chromium.org357bf652010-04-12 11:30:10 +00008539 case Token::BIT_OR: __ orr(result, rhs, Operand(lhs)); break;
8540 case Token::BIT_AND: __ and_(result, rhs, Operand(lhs)); break;
8541 case Token::BIT_XOR: __ eor(result, rhs, Operand(lhs)); break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008542 case Token::SAR:
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008543 // Remove tags from right operand.
ager@chromium.org357bf652010-04-12 11:30:10 +00008544 __ GetLeastBitsFromSmi(scratch2, rhs, 5);
8545 __ mov(result, Operand(lhs, ASR, scratch2));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008546 // Smi tag result.
ager@chromium.org357bf652010-04-12 11:30:10 +00008547 __ bic(result, result, Operand(kSmiTagMask));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008548 break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008549 case Token::SHR:
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008550 // Remove tags from operands. We can't do this on a 31 bit number
8551 // because then the 0s get shifted into bit 30 instead of bit 31.
ager@chromium.org357bf652010-04-12 11:30:10 +00008552 __ mov(scratch, Operand(lhs, ASR, kSmiTagSize)); // x
8553 __ GetLeastBitsFromSmi(scratch2, rhs, 5);
8554 __ mov(scratch, Operand(scratch, LSR, scratch2));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008555 // Unsigned shift is not allowed to produce a negative number, so
8556 // check the sign bit and the sign bit after Smi tagging.
ager@chromium.org357bf652010-04-12 11:30:10 +00008557 __ tst(scratch, Operand(0xc0000000));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008558 __ b(ne, &slow);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008559 // Smi tag result.
ager@chromium.org357bf652010-04-12 11:30:10 +00008560 __ mov(result, Operand(scratch, LSL, kSmiTagSize));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008561 break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008562 case Token::SHL:
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008563 // Remove tags from operands.
ager@chromium.org357bf652010-04-12 11:30:10 +00008564 __ mov(scratch, Operand(lhs, ASR, kSmiTagSize)); // x
8565 __ GetLeastBitsFromSmi(scratch2, rhs, 5);
8566 __ mov(scratch, Operand(scratch, LSL, scratch2));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008567 // Check that the signed result fits in a Smi.
ager@chromium.org357bf652010-04-12 11:30:10 +00008568 __ add(scratch2, scratch, Operand(0x40000000), SetCC);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008569 __ b(mi, &slow);
ager@chromium.org357bf652010-04-12 11:30:10 +00008570 __ mov(result, Operand(scratch, LSL, kSmiTagSize));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008571 break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008572 default: UNREACHABLE();
8573 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008574 __ Ret();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008575 __ bind(&slow);
ager@chromium.org357bf652010-04-12 11:30:10 +00008576 HandleNonSmiBitwiseOp(masm, lhs, rhs);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008577 break;
8578 }
8579
8580 default: UNREACHABLE();
8581 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008582 // This code should be unreachable.
8583 __ stop("Unreachable");
ager@chromium.org357bf652010-04-12 11:30:10 +00008584
8585 // Generate an unreachable reference to the DEFAULT stub so that it can be
8586 // found at the end of this stub when clearing ICs at GC.
8587 // TODO(kaznacheev): Check performance impact and get rid of this.
8588 if (runtime_operands_type_ != BinaryOpIC::DEFAULT) {
8589 GenericBinaryOpStub uninit(MinorKey(), BinaryOpIC::DEFAULT);
8590 __ CallStub(&uninit);
8591 }
8592}
8593
8594
8595void GenericBinaryOpStub::GenerateTypeTransition(MacroAssembler* masm) {
8596 Label get_result;
8597
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00008598 __ Push(r1, r0);
ager@chromium.org357bf652010-04-12 11:30:10 +00008599
8600 // Internal frame is necessary to handle exceptions properly.
8601 __ EnterInternalFrame();
8602 // Call the stub proper to get the result in r0.
8603 __ Call(&get_result);
8604 __ LeaveInternalFrame();
8605
8606 __ push(r0);
8607
8608 __ mov(r0, Operand(Smi::FromInt(MinorKey())));
8609 __ push(r0);
8610 __ mov(r0, Operand(Smi::FromInt(op_)));
8611 __ push(r0);
8612 __ mov(r0, Operand(Smi::FromInt(runtime_operands_type_)));
8613 __ push(r0);
8614
8615 __ TailCallExternalReference(
8616 ExternalReference(IC_Utility(IC::kBinaryOp_Patch)),
8617 6,
8618 1);
8619
8620 // The entry point for the result calculation is assumed to be immediately
8621 // after this sequence.
8622 __ bind(&get_result);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008623}
8624
8625
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00008626Handle<Code> GetBinaryOpStub(int key, BinaryOpIC::TypeInfo type_info) {
ager@chromium.org357bf652010-04-12 11:30:10 +00008627 GenericBinaryOpStub stub(key, type_info);
8628 return stub.GetCode();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00008629}
8630
8631
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008632void TranscendentalCacheStub::Generate(MacroAssembler* masm) {
8633 // Argument is a number and is on stack and in r0.
8634 Label runtime_call;
8635 Label input_not_smi;
8636 Label loaded;
8637
8638 if (CpuFeatures::IsSupported(VFP3)) {
8639 // Load argument and check if it is a smi.
8640 __ BranchOnNotSmi(r0, &input_not_smi);
8641
8642 CpuFeatures::Scope scope(VFP3);
8643 // Input is a smi. Convert to double and load the low and high words
8644 // of the double into r2, r3.
8645 __ IntegerToDoubleConversionWithVFP3(r0, r3, r2);
8646 __ b(&loaded);
8647
8648 __ bind(&input_not_smi);
8649 // Check if input is a HeapNumber.
8650 __ CheckMap(r0,
8651 r1,
8652 Heap::kHeapNumberMapRootIndex,
8653 &runtime_call,
8654 true);
8655 // Input is a HeapNumber. Load it to a double register and store the
8656 // low and high words into r2, r3.
8657 __ Ldrd(r2, r3, FieldMemOperand(r0, HeapNumber::kValueOffset));
8658
8659 __ bind(&loaded);
8660 // r2 = low 32 bits of double value
8661 // r3 = high 32 bits of double value
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00008662 // Compute hash (the shifts are arithmetic):
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008663 // h = (low ^ high); h ^= h >> 16; h ^= h >> 8; h = h & (cacheSize - 1);
8664 __ eor(r1, r2, Operand(r3));
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00008665 __ eor(r1, r1, Operand(r1, ASR, 16));
8666 __ eor(r1, r1, Operand(r1, ASR, 8));
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008667 ASSERT(IsPowerOf2(TranscendentalCache::kCacheSize));
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00008668 __ And(r1, r1, Operand(TranscendentalCache::kCacheSize - 1));
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008669
8670 // r2 = low 32 bits of double value.
8671 // r3 = high 32 bits of double value.
8672 // r1 = TranscendentalCache::hash(double value).
8673 __ mov(r0,
8674 Operand(ExternalReference::transcendental_cache_array_address()));
8675 // r0 points to cache array.
8676 __ ldr(r0, MemOperand(r0, type_ * sizeof(TranscendentalCache::caches_[0])));
8677 // r0 points to the cache for the type type_.
8678 // If NULL, the cache hasn't been initialized yet, so go through runtime.
8679 __ cmp(r0, Operand(0));
8680 __ b(eq, &runtime_call);
8681
8682#ifdef DEBUG
8683 // Check that the layout of cache elements match expectations.
8684 { TranscendentalCache::Element test_elem[2];
8685 char* elem_start = reinterpret_cast<char*>(&test_elem[0]);
8686 char* elem2_start = reinterpret_cast<char*>(&test_elem[1]);
8687 char* elem_in0 = reinterpret_cast<char*>(&(test_elem[0].in[0]));
8688 char* elem_in1 = reinterpret_cast<char*>(&(test_elem[0].in[1]));
8689 char* elem_out = reinterpret_cast<char*>(&(test_elem[0].output));
8690 CHECK_EQ(12, elem2_start - elem_start); // Two uint_32's and a pointer.
8691 CHECK_EQ(0, elem_in0 - elem_start);
8692 CHECK_EQ(kIntSize, elem_in1 - elem_start);
8693 CHECK_EQ(2 * kIntSize, elem_out - elem_start);
8694 }
8695#endif
8696
8697 // Find the address of the r1'st entry in the cache, i.e., &r0[r1*12].
8698 __ add(r1, r1, Operand(r1, LSL, 1));
8699 __ add(r0, r0, Operand(r1, LSL, 2));
8700 // Check if cache matches: Double value is stored in uint32_t[2] array.
8701 __ ldm(ia, r0, r4.bit()| r5.bit() | r6.bit());
8702 __ cmp(r2, r4);
8703 __ b(ne, &runtime_call);
8704 __ cmp(r3, r5);
8705 __ b(ne, &runtime_call);
8706 // Cache hit. Load result, pop argument and return.
8707 __ mov(r0, Operand(r6));
8708 __ pop();
8709 __ Ret();
8710 }
8711
8712 __ bind(&runtime_call);
8713 __ TailCallExternalReference(ExternalReference(RuntimeFunction()), 1, 1);
8714}
8715
8716
8717Runtime::FunctionId TranscendentalCacheStub::RuntimeFunction() {
8718 switch (type_) {
8719 // Add more cases when necessary.
8720 case TranscendentalCache::SIN: return Runtime::kMath_sin;
8721 case TranscendentalCache::COS: return Runtime::kMath_cos;
8722 default:
8723 UNIMPLEMENTED();
8724 return Runtime::kAbort;
8725 }
8726}
8727
8728
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008729void StackCheckStub::Generate(MacroAssembler* masm) {
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00008730 // Do tail-call to runtime routine. Runtime routines expect at least one
8731 // argument, so give it a Smi.
8732 __ mov(r0, Operand(Smi::FromInt(0)));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008733 __ push(r0);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00008734 __ TailCallRuntime(Runtime::kStackGuard, 1, 1);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008735
8736 __ StubReturn(1);
8737}
8738
8739
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00008740void GenericUnaryOpStub::Generate(MacroAssembler* masm) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008741 Label slow, done;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00008742
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00008743 Register heap_number_map = r6;
8744 __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
8745
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008746 if (op_ == Token::SUB) {
8747 // Check whether the value is a smi.
8748 Label try_float;
8749 __ tst(r0, Operand(kSmiTagMask));
8750 __ b(ne, &try_float);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008751
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008752 // Go slow case if the value of the expression is zero
8753 // to make sure that we switch between 0 and -0.
8754 __ cmp(r0, Operand(0));
8755 __ b(eq, &slow);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008756
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008757 // The value of the expression is a smi that is not zero. Try
8758 // optimistic subtraction '0 - value'.
8759 __ rsb(r1, r0, Operand(0), SetCC);
8760 __ b(vs, &slow);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008761
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008762 __ mov(r0, Operand(r1)); // Set r0 to result.
8763 __ b(&done);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008764
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008765 __ bind(&try_float);
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00008766 __ ldr(r1, FieldMemOperand(r0, HeapObject::kMapOffset));
8767 __ AssertRegisterIsRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
8768 __ cmp(r1, heap_number_map);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008769 __ b(ne, &slow);
8770 // r0 is a heap number. Get a new heap number in r1.
8771 if (overwrite_) {
8772 __ ldr(r2, FieldMemOperand(r0, HeapNumber::kExponentOffset));
8773 __ eor(r2, r2, Operand(HeapNumber::kSignMask)); // Flip sign.
8774 __ str(r2, FieldMemOperand(r0, HeapNumber::kExponentOffset));
8775 } else {
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00008776 __ AllocateHeapNumber(r1, r2, r3, r6, &slow);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008777 __ ldr(r3, FieldMemOperand(r0, HeapNumber::kMantissaOffset));
8778 __ ldr(r2, FieldMemOperand(r0, HeapNumber::kExponentOffset));
8779 __ str(r3, FieldMemOperand(r1, HeapNumber::kMantissaOffset));
8780 __ eor(r2, r2, Operand(HeapNumber::kSignMask)); // Flip sign.
8781 __ str(r2, FieldMemOperand(r1, HeapNumber::kExponentOffset));
8782 __ mov(r0, Operand(r1));
8783 }
8784 } else if (op_ == Token::BIT_NOT) {
8785 // Check if the operand is a heap number.
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00008786 __ ldr(r1, FieldMemOperand(r0, HeapObject::kMapOffset));
8787 __ AssertRegisterIsRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
8788 __ cmp(r1, heap_number_map);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008789 __ b(ne, &slow);
8790
8791 // Convert the heap number is r0 to an untagged integer in r1.
8792 GetInt32(masm, r0, r1, r2, r3, &slow);
8793
8794 // Do the bitwise operation (move negated) and check if the result
8795 // fits in a smi.
8796 Label try_float;
8797 __ mvn(r1, Operand(r1));
8798 __ add(r2, r1, Operand(0x40000000), SetCC);
8799 __ b(mi, &try_float);
8800 __ mov(r0, Operand(r1, LSL, kSmiTagSize));
8801 __ b(&done);
8802
8803 __ bind(&try_float);
8804 if (!overwrite_) {
8805 // Allocate a fresh heap number, but don't overwrite r0 until
8806 // we're sure we can do it without going through the slow case
8807 // that needs the value in r0.
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00008808 __ AllocateHeapNumber(r2, r3, r4, r6, &slow);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008809 __ mov(r0, Operand(r2));
8810 }
8811
8812 // WriteInt32ToHeapNumberStub does not trigger GC, so we do not
8813 // have to set up a frame.
8814 WriteInt32ToHeapNumberStub stub(r1, r0, r2);
8815 __ push(lr);
8816 __ Call(stub.GetCode(), RelocInfo::CODE_TARGET);
8817 __ pop(lr);
8818 } else {
8819 UNIMPLEMENTED();
8820 }
8821
8822 __ bind(&done);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008823 __ StubReturn(1);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008824
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008825 // Handle the slow case by jumping to the JavaScript builtin.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008826 __ bind(&slow);
8827 __ push(r0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008828 switch (op_) {
8829 case Token::SUB:
8830 __ InvokeBuiltin(Builtins::UNARY_MINUS, JUMP_JS);
8831 break;
8832 case Token::BIT_NOT:
8833 __ InvokeBuiltin(Builtins::BIT_NOT, JUMP_JS);
8834 break;
8835 default:
8836 UNREACHABLE();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008837 }
ager@chromium.orga1645e22009-09-09 19:27:10 +00008838}
8839
8840
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008841void CEntryStub::GenerateThrowTOS(MacroAssembler* masm) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008842 // r0 holds the exception.
8843
8844 // Adjust this code if not the case.
8845 ASSERT(StackHandlerConstants::kSize == 4 * kPointerSize);
8846
8847 // Drop the sp to the top of the handler.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008848 __ mov(r3, Operand(ExternalReference(Top::k_handler_address)));
8849 __ ldr(sp, MemOperand(r3));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008850
8851 // Restore the next handler and frame pointer, discard handler state.
8852 ASSERT(StackHandlerConstants::kNextOffset == 0);
8853 __ pop(r2);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008854 __ str(r2, MemOperand(r3));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008855 ASSERT(StackHandlerConstants::kFPOffset == 2 * kPointerSize);
8856 __ ldm(ia_w, sp, r3.bit() | fp.bit()); // r3: discarded state.
8857
8858 // Before returning we restore the context from the frame pointer if
8859 // not NULL. The frame pointer is NULL in the exception handler of a
8860 // JS entry frame.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008861 __ cmp(fp, Operand(0));
8862 // Set cp to NULL if fp is NULL.
8863 __ mov(cp, Operand(0), LeaveCC, eq);
8864 // Restore cp otherwise.
8865 __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset), ne);
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008866#ifdef DEBUG
8867 if (FLAG_debug_code) {
8868 __ mov(lr, Operand(pc));
8869 }
8870#endif
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008871 ASSERT(StackHandlerConstants::kPCOffset == 3 * kPointerSize);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008872 __ pop(pc);
8873}
8874
8875
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00008876void CEntryStub::GenerateThrowUncatchable(MacroAssembler* masm,
8877 UncatchableExceptionType type) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008878 // Adjust this code if not the case.
8879 ASSERT(StackHandlerConstants::kSize == 4 * kPointerSize);
8880
8881 // Drop sp to the top stack handler.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008882 __ mov(r3, Operand(ExternalReference(Top::k_handler_address)));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008883 __ ldr(sp, MemOperand(r3));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008884
8885 // Unwind the handlers until the ENTRY handler is found.
8886 Label loop, done;
8887 __ bind(&loop);
8888 // Load the type of the current stack handler.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008889 const int kStateOffset = StackHandlerConstants::kStateOffset;
8890 __ ldr(r2, MemOperand(sp, kStateOffset));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008891 __ cmp(r2, Operand(StackHandler::ENTRY));
8892 __ b(eq, &done);
8893 // Fetch the next handler in the list.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008894 const int kNextOffset = StackHandlerConstants::kNextOffset;
8895 __ ldr(sp, MemOperand(sp, kNextOffset));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008896 __ jmp(&loop);
8897 __ bind(&done);
8898
8899 // Set the top handler address to next handler past the current ENTRY handler.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008900 ASSERT(StackHandlerConstants::kNextOffset == 0);
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00008901 __ pop(r2);
8902 __ str(r2, MemOperand(r3));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008903
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00008904 if (type == OUT_OF_MEMORY) {
8905 // Set external caught exception to false.
8906 ExternalReference external_caught(Top::k_external_caught_exception_address);
8907 __ mov(r0, Operand(false));
8908 __ mov(r2, Operand(external_caught));
8909 __ str(r0, MemOperand(r2));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008910
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00008911 // Set pending exception and r0 to out of memory exception.
8912 Failure* out_of_memory = Failure::OutOfMemoryException();
8913 __ mov(r0, Operand(reinterpret_cast<int32_t>(out_of_memory)));
8914 __ mov(r2, Operand(ExternalReference(Top::k_pending_exception_address)));
8915 __ str(r0, MemOperand(r2));
8916 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008917
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008918 // Stack layout at this point. See also StackHandlerConstants.
8919 // sp -> state (ENTRY)
8920 // fp
8921 // lr
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008922
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008923 // Discard handler state (r2 is not used) and restore frame pointer.
8924 ASSERT(StackHandlerConstants::kFPOffset == 2 * kPointerSize);
8925 __ ldm(ia_w, sp, r2.bit() | fp.bit()); // r2: discarded state.
8926 // Before returning we restore the context from the frame pointer if
8927 // not NULL. The frame pointer is NULL in the exception handler of a
8928 // JS entry frame.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008929 __ cmp(fp, Operand(0));
8930 // Set cp to NULL if fp is NULL.
8931 __ mov(cp, Operand(0), LeaveCC, eq);
8932 // Restore cp otherwise.
8933 __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset), ne);
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008934#ifdef DEBUG
8935 if (FLAG_debug_code) {
8936 __ mov(lr, Operand(pc));
8937 }
8938#endif
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008939 ASSERT(StackHandlerConstants::kPCOffset == 3 * kPointerSize);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008940 __ pop(pc);
8941}
8942
8943
8944void CEntryStub::GenerateCore(MacroAssembler* masm,
8945 Label* throw_normal_exception,
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00008946 Label* throw_termination_exception,
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008947 Label* throw_out_of_memory_exception,
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00008948 bool do_gc,
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00008949 bool always_allocate,
8950 int frame_alignment_skew) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008951 // r0: result parameter for PerformGC, if any
8952 // r4: number of arguments including receiver (C callee-saved)
8953 // r5: pointer to builtin function (C callee-saved)
8954 // r6: pointer to the first argument (C callee-saved)
8955
8956 if (do_gc) {
8957 // Passing r0.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00008958 __ PrepareCallCFunction(1, r1);
8959 __ CallCFunction(ExternalReference::perform_gc_function(), 1);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008960 }
8961
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00008962 ExternalReference scope_depth =
8963 ExternalReference::heap_always_allocate_scope_depth();
8964 if (always_allocate) {
8965 __ mov(r0, Operand(scope_depth));
8966 __ ldr(r1, MemOperand(r0));
8967 __ add(r1, r1, Operand(1));
8968 __ str(r1, MemOperand(r0));
8969 }
8970
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008971 // Call C built-in.
8972 // r0 = argc, r1 = argv
8973 __ mov(r0, Operand(r4));
8974 __ mov(r1, Operand(r6));
8975
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00008976 int frame_alignment = MacroAssembler::ActivationFrameAlignment();
8977 int frame_alignment_mask = frame_alignment - 1;
8978#if defined(V8_HOST_ARCH_ARM)
8979 if (FLAG_debug_code) {
8980 if (frame_alignment > kPointerSize) {
8981 Label alignment_as_expected;
8982 ASSERT(IsPowerOf2(frame_alignment));
8983 __ sub(r2, sp, Operand(frame_alignment_skew));
8984 __ tst(r2, Operand(frame_alignment_mask));
8985 __ b(eq, &alignment_as_expected);
8986 // Don't use Check here, as it will call Runtime_Abort re-entering here.
8987 __ stop("Unexpected alignment");
8988 __ bind(&alignment_as_expected);
8989 }
8990 }
8991#endif
8992
8993 // Just before the call (jump) below lr is pushed, so the actual alignment is
8994 // adding one to the current skew.
8995 int alignment_before_call =
8996 (frame_alignment_skew + kPointerSize) & frame_alignment_mask;
8997 if (alignment_before_call > 0) {
8998 // Push until the alignment before the call is met.
8999 __ mov(r2, Operand(0));
9000 for (int i = alignment_before_call;
9001 (i & frame_alignment_mask) != 0;
9002 i += kPointerSize) {
9003 __ push(r2);
9004 }
9005 }
9006
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009007 // TODO(1242173): To let the GC traverse the return address of the exit
9008 // frames, we need to know where the return address is. Right now,
9009 // we push it on the stack to be able to find it again, but we never
9010 // restore from it in case of changes, which makes it impossible to
9011 // support moving the C entry code stub. This should be fixed, but currently
9012 // this is OK because the CEntryStub gets generated so early in the V8 boot
9013 // sequence that it is not moving ever.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009014 masm->add(lr, pc, Operand(4)); // Compute return address: (pc + 8) + 4
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009015 masm->push(lr);
9016 masm->Jump(r5);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00009017
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009018 // Restore sp back to before aligning the stack.
9019 if (alignment_before_call > 0) {
9020 __ add(sp, sp, Operand(alignment_before_call));
9021 }
9022
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00009023 if (always_allocate) {
9024 // It's okay to clobber r2 and r3 here. Don't mess with r0 and r1
9025 // though (contain the result).
9026 __ mov(r2, Operand(scope_depth));
9027 __ ldr(r3, MemOperand(r2));
9028 __ sub(r3, r3, Operand(1));
9029 __ str(r3, MemOperand(r2));
9030 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009031
9032 // check for failure result
9033 Label failure_returned;
9034 ASSERT(((kFailureTag + 1) & kFailureTagMask) == 0);
9035 // Lower 2 bits of r2 are 0 iff r0 has failure tag.
9036 __ add(r2, r0, Operand(1));
9037 __ tst(r2, Operand(kFailureTagMask));
9038 __ b(eq, &failure_returned);
9039
9040 // Exit C frame and return.
9041 // r0:r1: result
9042 // sp: stack pointer
9043 // fp: frame pointer
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009044 __ LeaveExitFrame(mode_);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009045
9046 // check if we should retry or throw exception
9047 Label retry;
9048 __ bind(&failure_returned);
9049 ASSERT(Failure::RETRY_AFTER_GC == 0);
9050 __ tst(r0, Operand(((1 << kFailureTypeTagSize) - 1) << kFailureTagSize));
9051 __ b(eq, &retry);
9052
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00009053 // Special handling of out of memory exceptions.
9054 Failure* out_of_memory = Failure::OutOfMemoryException();
9055 __ cmp(r0, Operand(reinterpret_cast<int32_t>(out_of_memory)));
9056 __ b(eq, throw_out_of_memory_exception);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009057
9058 // Retrieve the pending exception and clear the variable.
ager@chromium.org32912102009-01-16 10:38:43 +00009059 __ mov(ip, Operand(ExternalReference::the_hole_value_location()));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009060 __ ldr(r3, MemOperand(ip));
ager@chromium.org32912102009-01-16 10:38:43 +00009061 __ mov(ip, Operand(ExternalReference(Top::k_pending_exception_address)));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009062 __ ldr(r0, MemOperand(ip));
9063 __ str(r3, MemOperand(ip));
9064
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00009065 // Special handling of termination exceptions which are uncatchable
9066 // by javascript code.
9067 __ cmp(r0, Operand(Factory::termination_exception()));
9068 __ b(eq, throw_termination_exception);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009069
9070 // Handle normal exception.
9071 __ jmp(throw_normal_exception);
9072
9073 __ bind(&retry); // pass last failure (r0) as parameter (r0) when retrying
9074}
9075
9076
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009077void CEntryStub::Generate(MacroAssembler* masm) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009078 // Called from JavaScript; parameters are on stack as if calling JS function
9079 // r0: number of arguments including receiver
9080 // r1: pointer to builtin function
9081 // fp: frame pointer (restored after C call)
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009082 // sp: stack pointer (restored as callee's sp after C call)
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009083 // cp: current context (C callee-saved)
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009084
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009085 // Result returned in r0 or r0+r1 by default.
9086
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009087 // NOTE: Invocations of builtins may return failure objects
9088 // instead of a proper result. The builtin entry handles
9089 // this by performing a garbage collection and retrying the
9090 // builtin once.
9091
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009092 // Enter the exit frame that transitions from JavaScript to C++.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009093 __ EnterExitFrame(mode_);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009094
9095 // r4: number of arguments (C callee-saved)
9096 // r5: pointer to builtin function (C callee-saved)
9097 // r6: pointer to first argument (C callee-saved)
9098
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009099 Label throw_normal_exception;
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00009100 Label throw_termination_exception;
9101 Label throw_out_of_memory_exception;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009102
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00009103 // Call into the runtime system.
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00009104 GenerateCore(masm,
9105 &throw_normal_exception,
9106 &throw_termination_exception,
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009107 &throw_out_of_memory_exception,
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00009108 false,
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009109 false,
9110 -kPointerSize);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00009111
9112 // Do space-specific GC and retry runtime call.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009113 GenerateCore(masm,
9114 &throw_normal_exception,
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00009115 &throw_termination_exception,
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009116 &throw_out_of_memory_exception,
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00009117 true,
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009118 false,
9119 0);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00009120
9121 // Do full GC and retry runtime call one final time.
9122 Failure* failure = Failure::InternalError();
9123 __ mov(r0, Operand(reinterpret_cast<int32_t>(failure)));
9124 GenerateCore(masm,
9125 &throw_normal_exception,
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00009126 &throw_termination_exception,
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00009127 &throw_out_of_memory_exception,
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00009128 true,
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009129 true,
9130 kPointerSize);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009131
9132 __ bind(&throw_out_of_memory_exception);
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00009133 GenerateThrowUncatchable(masm, OUT_OF_MEMORY);
9134
9135 __ bind(&throw_termination_exception);
9136 GenerateThrowUncatchable(masm, TERMINATION);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009137
9138 __ bind(&throw_normal_exception);
9139 GenerateThrowTOS(masm);
9140}
9141
9142
9143void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) {
9144 // r0: code entry
9145 // r1: function
9146 // r2: receiver
9147 // r3: argc
9148 // [sp+0]: argv
9149
9150 Label invoke, exit;
9151
9152 // Called from C, so do not pop argc and args on exit (preserve sp)
9153 // No need to save register-passed args
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009154 // Save callee-saved registers (incl. cp and fp), sp, and lr
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009155 __ stm(db_w, sp, kCalleeSaved | lr.bit());
9156
9157 // Get address of argv, see stm above.
9158 // r0: code entry
9159 // r1: function
9160 // r2: receiver
9161 // r3: argc
ager@chromium.org5c838252010-02-19 08:53:10 +00009162 __ ldr(r4, MemOperand(sp, (kNumCalleeSaved + 1) * kPointerSize)); // argv
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009163
9164 // Push a frame with special values setup to mark it as an entry frame.
9165 // r0: code entry
9166 // r1: function
9167 // r2: receiver
9168 // r3: argc
9169 // r4: argv
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009170 __ mov(r8, Operand(-1)); // Push a bad frame pointer to fail if it is used.
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009171 int marker = is_construct ? StackFrame::ENTRY_CONSTRUCT : StackFrame::ENTRY;
9172 __ mov(r7, Operand(Smi::FromInt(marker)));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009173 __ mov(r6, Operand(Smi::FromInt(marker)));
9174 __ mov(r5, Operand(ExternalReference(Top::k_c_entry_fp_address)));
9175 __ ldr(r5, MemOperand(r5));
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00009176 __ Push(r8, r7, r6, r5);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009177
9178 // Setup frame pointer for the frame to be pushed.
9179 __ add(fp, sp, Operand(-EntryFrameConstants::kCallerFPOffset));
9180
9181 // Call a faked try-block that does the invoke.
9182 __ bl(&invoke);
9183
9184 // Caught exception: Store result (exception) in the pending
9185 // exception field in the JSEnv and return a failure sentinel.
9186 // Coming in here the fp will be invalid because the PushTryHandler below
9187 // sets it to 0 to signal the existence of the JSEntry frame.
ager@chromium.org32912102009-01-16 10:38:43 +00009188 __ mov(ip, Operand(ExternalReference(Top::k_pending_exception_address)));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009189 __ str(r0, MemOperand(ip));
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009190 __ mov(r0, Operand(reinterpret_cast<int32_t>(Failure::Exception())));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009191 __ b(&exit);
9192
9193 // Invoke: Link this frame into the handler chain.
9194 __ bind(&invoke);
9195 // Must preserve r0-r4, r5-r7 are available.
9196 __ PushTryHandler(IN_JS_ENTRY, JS_ENTRY_HANDLER);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009197 // If an exception not caught by another handler occurs, this handler
9198 // returns control to the code after the bl(&invoke) above, which
9199 // restores all kCalleeSaved registers (including cp and fp) to their
9200 // saved values before returning a failure to C.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009201
9202 // Clear any pending exceptions.
9203 __ mov(ip, Operand(ExternalReference::the_hole_value_location()));
9204 __ ldr(r5, MemOperand(ip));
ager@chromium.org32912102009-01-16 10:38:43 +00009205 __ mov(ip, Operand(ExternalReference(Top::k_pending_exception_address)));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009206 __ str(r5, MemOperand(ip));
9207
9208 // Invoke the function by calling through JS entry trampoline builtin.
9209 // Notice that we cannot store a reference to the trampoline code directly in
9210 // this stub, because runtime stubs are not traversed when doing GC.
9211
9212 // Expected registers by Builtins::JSEntryTrampoline
9213 // r0: code entry
9214 // r1: function
9215 // r2: receiver
9216 // r3: argc
9217 // r4: argv
9218 if (is_construct) {
9219 ExternalReference construct_entry(Builtins::JSConstructEntryTrampoline);
9220 __ mov(ip, Operand(construct_entry));
9221 } else {
9222 ExternalReference entry(Builtins::JSEntryTrampoline);
9223 __ mov(ip, Operand(entry));
9224 }
9225 __ ldr(ip, MemOperand(ip)); // deref address
9226
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009227 // Branch and link to JSEntryTrampoline. We don't use the double underscore
9228 // macro for the add instruction because we don't want the coverage tool
9229 // inserting instructions here after we read the pc.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009230 __ mov(lr, Operand(pc));
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009231 masm->add(pc, ip, Operand(Code::kHeaderSize - kHeapObjectTag));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009232
9233 // Unlink this frame from the handler chain. When reading the
9234 // address of the next handler, there is no need to use the address
9235 // displacement since the current stack pointer (sp) points directly
9236 // to the stack handler.
9237 __ ldr(r3, MemOperand(sp, StackHandlerConstants::kNextOffset));
9238 __ mov(ip, Operand(ExternalReference(Top::k_handler_address)));
9239 __ str(r3, MemOperand(ip));
9240 // No need to restore registers
9241 __ add(sp, sp, Operand(StackHandlerConstants::kSize));
9242
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009243
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009244 __ bind(&exit); // r0 holds result
9245 // Restore the top frame descriptors from the stack.
9246 __ pop(r3);
9247 __ mov(ip, Operand(ExternalReference(Top::k_c_entry_fp_address)));
9248 __ str(r3, MemOperand(ip));
9249
9250 // Reset the stack to the callee saved registers.
9251 __ add(sp, sp, Operand(-EntryFrameConstants::kCallerFPOffset));
9252
9253 // Restore callee-saved registers and return.
9254#ifdef DEBUG
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009255 if (FLAG_debug_code) {
9256 __ mov(lr, Operand(pc));
9257 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009258#endif
9259 __ ldm(ia_w, sp, kCalleeSaved | pc.bit());
9260}
9261
9262
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009263// This stub performs an instanceof, calling the builtin function if
9264// necessary. Uses r1 for the object, r0 for the function that it may
9265// be an instance of (these are fetched from the stack).
9266void InstanceofStub::Generate(MacroAssembler* masm) {
9267 // Get the object - slow case for smis (we may need to throw an exception
9268 // depending on the rhs).
9269 Label slow, loop, is_instance, is_not_instance;
9270 __ ldr(r0, MemOperand(sp, 1 * kPointerSize));
9271 __ BranchOnSmi(r0, &slow);
9272
9273 // Check that the left hand is a JS object and put map in r3.
9274 __ CompareObjectType(r0, r3, r2, FIRST_JS_OBJECT_TYPE);
9275 __ b(lt, &slow);
9276 __ cmp(r2, Operand(LAST_JS_OBJECT_TYPE));
9277 __ b(gt, &slow);
9278
9279 // Get the prototype of the function (r4 is result, r2 is scratch).
ager@chromium.org5c838252010-02-19 08:53:10 +00009280 __ ldr(r1, MemOperand(sp, 0));
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00009281 // r1 is function, r3 is map.
9282
9283 // Look up the function and the map in the instanceof cache.
9284 Label miss;
9285 __ LoadRoot(ip, Heap::kInstanceofCacheFunctionRootIndex);
9286 __ cmp(r1, ip);
9287 __ b(ne, &miss);
9288 __ LoadRoot(ip, Heap::kInstanceofCacheMapRootIndex);
9289 __ cmp(r3, ip);
9290 __ b(ne, &miss);
9291 __ LoadRoot(r0, Heap::kInstanceofCacheAnswerRootIndex);
9292 __ pop();
9293 __ pop();
9294 __ mov(pc, Operand(lr));
9295
9296 __ bind(&miss);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009297 __ TryGetFunctionPrototype(r1, r4, r2, &slow);
9298
9299 // Check that the function prototype is a JS object.
9300 __ BranchOnSmi(r4, &slow);
9301 __ CompareObjectType(r4, r5, r5, FIRST_JS_OBJECT_TYPE);
9302 __ b(lt, &slow);
9303 __ cmp(r5, Operand(LAST_JS_OBJECT_TYPE));
9304 __ b(gt, &slow);
9305
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00009306 __ StoreRoot(r1, Heap::kInstanceofCacheFunctionRootIndex);
9307 __ StoreRoot(r3, Heap::kInstanceofCacheMapRootIndex);
9308
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009309 // Register mapping: r3 is object map and r4 is function prototype.
9310 // Get prototype of object into r2.
9311 __ ldr(r2, FieldMemOperand(r3, Map::kPrototypeOffset));
9312
9313 // Loop through the prototype chain looking for the function prototype.
9314 __ bind(&loop);
9315 __ cmp(r2, Operand(r4));
9316 __ b(eq, &is_instance);
ager@chromium.orgab99eea2009-08-25 07:05:41 +00009317 __ LoadRoot(ip, Heap::kNullValueRootIndex);
9318 __ cmp(r2, ip);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009319 __ b(eq, &is_not_instance);
9320 __ ldr(r2, FieldMemOperand(r2, HeapObject::kMapOffset));
9321 __ ldr(r2, FieldMemOperand(r2, Map::kPrototypeOffset));
9322 __ jmp(&loop);
9323
9324 __ bind(&is_instance);
9325 __ mov(r0, Operand(Smi::FromInt(0)));
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00009326 __ StoreRoot(r0, Heap::kInstanceofCacheAnswerRootIndex);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009327 __ pop();
9328 __ pop();
9329 __ mov(pc, Operand(lr)); // Return.
9330
9331 __ bind(&is_not_instance);
9332 __ mov(r0, Operand(Smi::FromInt(1)));
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00009333 __ StoreRoot(r0, Heap::kInstanceofCacheAnswerRootIndex);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009334 __ pop();
9335 __ pop();
9336 __ mov(pc, Operand(lr)); // Return.
9337
9338 // Slow-case. Tail call builtin.
9339 __ bind(&slow);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009340 __ InvokeBuiltin(Builtins::INSTANCE_OF, JUMP_JS);
9341}
9342
9343
ager@chromium.org7c537e22008-10-16 08:43:32 +00009344void ArgumentsAccessStub::GenerateReadElement(MacroAssembler* masm) {
9345 // The displacement is the offset of the last parameter (if any)
9346 // relative to the frame pointer.
9347 static const int kDisplacement =
9348 StandardFrameConstants::kCallerSPOffset - kPointerSize;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009349
ager@chromium.org7c537e22008-10-16 08:43:32 +00009350 // Check that the key is a smi.
9351 Label slow;
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009352 __ BranchOnNotSmi(r1, &slow);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009353
ager@chromium.org7c537e22008-10-16 08:43:32 +00009354 // Check if the calling frame is an arguments adaptor frame.
9355 Label adaptor;
9356 __ ldr(r2, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
9357 __ ldr(r3, MemOperand(r2, StandardFrameConstants::kContextOffset));
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009358 __ cmp(r3, Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
ager@chromium.org7c537e22008-10-16 08:43:32 +00009359 __ b(eq, &adaptor);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009360
ager@chromium.org7c537e22008-10-16 08:43:32 +00009361 // Check index against formal parameters count limit passed in
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009362 // through register r0. Use unsigned comparison to get negative
ager@chromium.org7c537e22008-10-16 08:43:32 +00009363 // check for free.
9364 __ cmp(r1, r0);
9365 __ b(cs, &slow);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009366
ager@chromium.org7c537e22008-10-16 08:43:32 +00009367 // Read the argument from the stack and return it.
9368 __ sub(r3, r0, r1);
9369 __ add(r3, fp, Operand(r3, LSL, kPointerSizeLog2 - kSmiTagSize));
9370 __ ldr(r0, MemOperand(r3, kDisplacement));
ager@chromium.org9085a012009-05-11 19:22:57 +00009371 __ Jump(lr);
ager@chromium.org7c537e22008-10-16 08:43:32 +00009372
9373 // Arguments adaptor case: Check index against actual arguments
9374 // limit found in the arguments adaptor frame. Use unsigned
9375 // comparison to get negative check for free.
9376 __ bind(&adaptor);
9377 __ ldr(r0, MemOperand(r2, ArgumentsAdaptorFrameConstants::kLengthOffset));
9378 __ cmp(r1, r0);
9379 __ b(cs, &slow);
9380
9381 // Read the argument from the adaptor frame and return it.
9382 __ sub(r3, r0, r1);
9383 __ add(r3, r2, Operand(r3, LSL, kPointerSizeLog2 - kSmiTagSize));
9384 __ ldr(r0, MemOperand(r3, kDisplacement));
ager@chromium.org9085a012009-05-11 19:22:57 +00009385 __ Jump(lr);
ager@chromium.org7c537e22008-10-16 08:43:32 +00009386
9387 // Slow-case: Handle non-smi or out-of-bounds access to arguments
9388 // by calling the runtime system.
9389 __ bind(&slow);
9390 __ push(r1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009391 __ TailCallRuntime(Runtime::kGetArgumentsProperty, 1, 1);
ager@chromium.org7c537e22008-10-16 08:43:32 +00009392}
9393
9394
9395void ArgumentsAccessStub::GenerateNewObject(MacroAssembler* masm) {
ager@chromium.org5c838252010-02-19 08:53:10 +00009396 // sp[0] : number of parameters
9397 // sp[4] : receiver displacement
9398 // sp[8] : function
9399
ager@chromium.org7c537e22008-10-16 08:43:32 +00009400 // Check if the calling frame is an arguments adaptor frame.
ager@chromium.org5c838252010-02-19 08:53:10 +00009401 Label adaptor_frame, try_allocate, runtime;
ager@chromium.org7c537e22008-10-16 08:43:32 +00009402 __ ldr(r2, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
9403 __ ldr(r3, MemOperand(r2, StandardFrameConstants::kContextOffset));
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009404 __ cmp(r3, Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
ager@chromium.org5c838252010-02-19 08:53:10 +00009405 __ b(eq, &adaptor_frame);
9406
9407 // Get the length from the frame.
9408 __ ldr(r1, MemOperand(sp, 0));
9409 __ b(&try_allocate);
ager@chromium.org7c537e22008-10-16 08:43:32 +00009410
9411 // Patch the arguments.length and the parameters pointer.
ager@chromium.org5c838252010-02-19 08:53:10 +00009412 __ bind(&adaptor_frame);
9413 __ ldr(r1, MemOperand(r2, ArgumentsAdaptorFrameConstants::kLengthOffset));
9414 __ str(r1, MemOperand(sp, 0));
9415 __ add(r3, r2, Operand(r1, LSL, kPointerSizeLog2 - kSmiTagSize));
ager@chromium.org7c537e22008-10-16 08:43:32 +00009416 __ add(r3, r3, Operand(StandardFrameConstants::kCallerSPOffset));
9417 __ str(r3, MemOperand(sp, 1 * kPointerSize));
9418
ager@chromium.org5c838252010-02-19 08:53:10 +00009419 // Try the new space allocation. Start out with computing the size
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00009420 // of the arguments object and the elements array in words.
ager@chromium.org5c838252010-02-19 08:53:10 +00009421 Label add_arguments_object;
9422 __ bind(&try_allocate);
9423 __ cmp(r1, Operand(0));
9424 __ b(eq, &add_arguments_object);
9425 __ mov(r1, Operand(r1, LSR, kSmiTagSize));
9426 __ add(r1, r1, Operand(FixedArray::kHeaderSize / kPointerSize));
9427 __ bind(&add_arguments_object);
9428 __ add(r1, r1, Operand(Heap::kArgumentsObjectSize / kPointerSize));
9429
9430 // Do the allocation of both objects in one go.
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00009431 __ AllocateInNewSpace(
9432 r1,
9433 r0,
9434 r2,
9435 r3,
9436 &runtime,
9437 static_cast<AllocationFlags>(TAG_OBJECT | SIZE_IN_WORDS));
ager@chromium.org5c838252010-02-19 08:53:10 +00009438
9439 // Get the arguments boilerplate from the current (global) context.
9440 int offset = Context::SlotOffset(Context::ARGUMENTS_BOILERPLATE_INDEX);
9441 __ ldr(r4, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_INDEX)));
9442 __ ldr(r4, FieldMemOperand(r4, GlobalObject::kGlobalContextOffset));
9443 __ ldr(r4, MemOperand(r4, offset));
9444
9445 // Copy the JS object part.
9446 for (int i = 0; i < JSObject::kHeaderSize; i += kPointerSize) {
9447 __ ldr(r3, FieldMemOperand(r4, i));
9448 __ str(r3, FieldMemOperand(r0, i));
9449 }
9450
9451 // Setup the callee in-object property.
9452 ASSERT(Heap::arguments_callee_index == 0);
9453 __ ldr(r3, MemOperand(sp, 2 * kPointerSize));
9454 __ str(r3, FieldMemOperand(r0, JSObject::kHeaderSize));
9455
9456 // Get the length (smi tagged) and set that as an in-object property too.
9457 ASSERT(Heap::arguments_length_index == 1);
9458 __ ldr(r1, MemOperand(sp, 0 * kPointerSize));
9459 __ str(r1, FieldMemOperand(r0, JSObject::kHeaderSize + kPointerSize));
9460
9461 // If there are no actual arguments, we're done.
9462 Label done;
9463 __ cmp(r1, Operand(0));
9464 __ b(eq, &done);
9465
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009466 // Get the parameters pointer from the stack.
ager@chromium.org5c838252010-02-19 08:53:10 +00009467 __ ldr(r2, MemOperand(sp, 1 * kPointerSize));
ager@chromium.org5c838252010-02-19 08:53:10 +00009468
9469 // Setup the elements pointer in the allocated arguments object and
9470 // initialize the header in the elements fixed array.
9471 __ add(r4, r0, Operand(Heap::kArgumentsObjectSize));
9472 __ str(r4, FieldMemOperand(r0, JSObject::kElementsOffset));
9473 __ LoadRoot(r3, Heap::kFixedArrayMapRootIndex);
9474 __ str(r3, FieldMemOperand(r4, FixedArray::kMapOffset));
9475 __ str(r1, FieldMemOperand(r4, FixedArray::kLengthOffset));
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009476 __ mov(r1, Operand(r1, LSR, kSmiTagSize)); // Untag the length for the loop.
ager@chromium.org5c838252010-02-19 08:53:10 +00009477
9478 // Copy the fixed array slots.
9479 Label loop;
9480 // Setup r4 to point to the first array slot.
9481 __ add(r4, r4, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
9482 __ bind(&loop);
9483 // Pre-decrement r2 with kPointerSize on each iteration.
9484 // Pre-decrement in order to skip receiver.
9485 __ ldr(r3, MemOperand(r2, kPointerSize, NegPreIndex));
9486 // Post-increment r4 with kPointerSize on each iteration.
9487 __ str(r3, MemOperand(r4, kPointerSize, PostIndex));
9488 __ sub(r1, r1, Operand(1));
9489 __ cmp(r1, Operand(0));
9490 __ b(ne, &loop);
9491
9492 // Return and remove the on-stack parameters.
9493 __ bind(&done);
9494 __ add(sp, sp, Operand(3 * kPointerSize));
9495 __ Ret();
9496
ager@chromium.org7c537e22008-10-16 08:43:32 +00009497 // Do the runtime call to allocate the arguments object.
9498 __ bind(&runtime);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009499 __ TailCallRuntime(Runtime::kNewArgumentsFast, 3, 1);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009500}
9501
9502
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009503void RegExpExecStub::Generate(MacroAssembler* masm) {
9504 // Just jump directly to runtime if native RegExp is not selected at compile
9505 // time or if regexp entry in generated code is turned off runtime switch or
9506 // at compilation.
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00009507#ifdef V8_INTERPRETED_REGEXP
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009508 __ TailCallRuntime(Runtime::kRegExpExec, 4, 1);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00009509#else // V8_INTERPRETED_REGEXP
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009510 if (!FLAG_regexp_entry_native) {
9511 __ TailCallRuntime(Runtime::kRegExpExec, 4, 1);
9512 return;
9513 }
9514
9515 // Stack frame on entry.
9516 // sp[0]: last_match_info (expected JSArray)
9517 // sp[4]: previous index
9518 // sp[8]: subject string
9519 // sp[12]: JSRegExp object
9520
9521 static const int kLastMatchInfoOffset = 0 * kPointerSize;
9522 static const int kPreviousIndexOffset = 1 * kPointerSize;
9523 static const int kSubjectOffset = 2 * kPointerSize;
9524 static const int kJSRegExpOffset = 3 * kPointerSize;
9525
9526 Label runtime, invoke_regexp;
9527
9528 // Allocation of registers for this function. These are in callee save
9529 // registers and will be preserved by the call to the native RegExp code, as
9530 // this code is called using the normal C calling convention. When calling
9531 // directly from generated code the native RegExp code will not do a GC and
9532 // therefore the content of these registers are safe to use after the call.
9533 Register subject = r4;
9534 Register regexp_data = r5;
9535 Register last_match_info_elements = r6;
9536
9537 // Ensure that a RegExp stack is allocated.
9538 ExternalReference address_of_regexp_stack_memory_address =
9539 ExternalReference::address_of_regexp_stack_memory_address();
9540 ExternalReference address_of_regexp_stack_memory_size =
9541 ExternalReference::address_of_regexp_stack_memory_size();
9542 __ mov(r0, Operand(address_of_regexp_stack_memory_size));
9543 __ ldr(r0, MemOperand(r0, 0));
9544 __ tst(r0, Operand(r0));
9545 __ b(eq, &runtime);
9546
9547 // Check that the first argument is a JSRegExp object.
9548 __ ldr(r0, MemOperand(sp, kJSRegExpOffset));
9549 ASSERT_EQ(0, kSmiTag);
9550 __ tst(r0, Operand(kSmiTagMask));
9551 __ b(eq, &runtime);
9552 __ CompareObjectType(r0, r1, r1, JS_REGEXP_TYPE);
9553 __ b(ne, &runtime);
9554
9555 // Check that the RegExp has been compiled (data contains a fixed array).
9556 __ ldr(regexp_data, FieldMemOperand(r0, JSRegExp::kDataOffset));
9557 if (FLAG_debug_code) {
9558 __ tst(regexp_data, Operand(kSmiTagMask));
9559 __ Check(nz, "Unexpected type for RegExp data, FixedArray expected");
9560 __ CompareObjectType(regexp_data, r0, r0, FIXED_ARRAY_TYPE);
9561 __ Check(eq, "Unexpected type for RegExp data, FixedArray expected");
9562 }
9563
9564 // regexp_data: RegExp data (FixedArray)
9565 // Check the type of the RegExp. Only continue if type is JSRegExp::IRREGEXP.
9566 __ ldr(r0, FieldMemOperand(regexp_data, JSRegExp::kDataTagOffset));
9567 __ cmp(r0, Operand(Smi::FromInt(JSRegExp::IRREGEXP)));
9568 __ b(ne, &runtime);
9569
9570 // regexp_data: RegExp data (FixedArray)
9571 // Check that the number of captures fit in the static offsets vector buffer.
9572 __ ldr(r2,
9573 FieldMemOperand(regexp_data, JSRegExp::kIrregexpCaptureCountOffset));
9574 // Calculate number of capture registers (number_of_captures + 1) * 2. This
9575 // uses the asumption that smis are 2 * their untagged value.
9576 ASSERT_EQ(0, kSmiTag);
9577 ASSERT_EQ(1, kSmiTagSize + kSmiShiftSize);
9578 __ add(r2, r2, Operand(2)); // r2 was a smi.
9579 // Check that the static offsets vector buffer is large enough.
9580 __ cmp(r2, Operand(OffsetsVector::kStaticOffsetsVectorSize));
9581 __ b(hi, &runtime);
9582
9583 // r2: Number of capture registers
9584 // regexp_data: RegExp data (FixedArray)
9585 // Check that the second argument is a string.
9586 __ ldr(subject, MemOperand(sp, kSubjectOffset));
9587 __ tst(subject, Operand(kSmiTagMask));
9588 __ b(eq, &runtime);
9589 Condition is_string = masm->IsObjectStringType(subject, r0);
9590 __ b(NegateCondition(is_string), &runtime);
9591 // Get the length of the string to r3.
9592 __ ldr(r3, FieldMemOperand(subject, String::kLengthOffset));
9593
9594 // r2: Number of capture registers
ager@chromium.orgac091b72010-05-05 07:34:42 +00009595 // r3: Length of subject string as a smi
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009596 // subject: Subject string
9597 // regexp_data: RegExp data (FixedArray)
9598 // Check that the third argument is a positive smi less than the subject
9599 // string length. A negative value will be greater (unsigned comparison).
9600 __ ldr(r0, MemOperand(sp, kPreviousIndexOffset));
ager@chromium.orgac091b72010-05-05 07:34:42 +00009601 __ tst(r0, Operand(kSmiTagMask));
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00009602 __ b(ne, &runtime);
ager@chromium.orgac091b72010-05-05 07:34:42 +00009603 __ cmp(r3, Operand(r0));
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00009604 __ b(ls, &runtime);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009605
9606 // r2: Number of capture registers
9607 // subject: Subject string
9608 // regexp_data: RegExp data (FixedArray)
9609 // Check that the fourth object is a JSArray object.
9610 __ ldr(r0, MemOperand(sp, kLastMatchInfoOffset));
9611 __ tst(r0, Operand(kSmiTagMask));
9612 __ b(eq, &runtime);
9613 __ CompareObjectType(r0, r1, r1, JS_ARRAY_TYPE);
9614 __ b(ne, &runtime);
9615 // Check that the JSArray is in fast case.
9616 __ ldr(last_match_info_elements,
9617 FieldMemOperand(r0, JSArray::kElementsOffset));
9618 __ ldr(r0, FieldMemOperand(last_match_info_elements, HeapObject::kMapOffset));
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00009619 __ LoadRoot(ip, Heap::kFixedArrayMapRootIndex);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00009620 __ cmp(r0, ip);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009621 __ b(ne, &runtime);
9622 // Check that the last match info has space for the capture registers and the
9623 // additional information.
9624 __ ldr(r0,
9625 FieldMemOperand(last_match_info_elements, FixedArray::kLengthOffset));
9626 __ add(r2, r2, Operand(RegExpImpl::kLastMatchOverhead));
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009627 __ cmp(r2, Operand(r0, ASR, kSmiTagSize));
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009628 __ b(gt, &runtime);
9629
9630 // subject: Subject string
9631 // regexp_data: RegExp data (FixedArray)
9632 // Check the representation and encoding of the subject string.
9633 Label seq_string;
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009634 __ ldr(r0, FieldMemOperand(subject, HeapObject::kMapOffset));
9635 __ ldrb(r0, FieldMemOperand(r0, Map::kInstanceTypeOffset));
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00009636 // First check for flat string.
9637 __ tst(r0, Operand(kIsNotStringMask | kStringRepresentationMask));
9638 ASSERT_EQ(0, kStringTag | kSeqStringTag);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009639 __ b(eq, &seq_string);
9640
9641 // subject: Subject string
9642 // regexp_data: RegExp data (FixedArray)
9643 // Check for flat cons string.
9644 // A flat cons string is a cons string where the second part is the empty
9645 // string. In that case the subject string is just the first part of the cons
9646 // string. Also in this case the first part of the cons string is known to be
9647 // a sequential string or an external string.
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00009648 ASSERT(kExternalStringTag !=0);
9649 ASSERT_EQ(0, kConsStringTag & kExternalStringTag);
9650 __ tst(r0, Operand(kIsNotStringMask | kExternalStringTag));
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009651 __ b(ne, &runtime);
9652 __ ldr(r0, FieldMemOperand(subject, ConsString::kSecondOffset));
9653 __ LoadRoot(r1, Heap::kEmptyStringRootIndex);
9654 __ cmp(r0, r1);
9655 __ b(ne, &runtime);
9656 __ ldr(subject, FieldMemOperand(subject, ConsString::kFirstOffset));
9657 __ ldr(r0, FieldMemOperand(subject, HeapObject::kMapOffset));
9658 __ ldrb(r0, FieldMemOperand(r0, Map::kInstanceTypeOffset));
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00009659 // Is first part a flat string?
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009660 ASSERT_EQ(0, kSeqStringTag);
9661 __ tst(r0, Operand(kStringRepresentationMask));
9662 __ b(nz, &runtime);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009663
9664 __ bind(&seq_string);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009665 // subject: Subject string
9666 // regexp_data: RegExp data (FixedArray)
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00009667 // r0: Instance type of subject string
9668 ASSERT_EQ(4, kAsciiStringTag);
9669 ASSERT_EQ(0, kTwoByteStringTag);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009670 // Find the code object based on the assumptions above.
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00009671 __ and_(r0, r0, Operand(kStringEncodingMask));
9672 __ mov(r3, Operand(r0, ASR, 2), SetCC);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009673 __ ldr(r7, FieldMemOperand(regexp_data, JSRegExp::kDataAsciiCodeOffset), ne);
9674 __ ldr(r7, FieldMemOperand(regexp_data, JSRegExp::kDataUC16CodeOffset), eq);
9675
9676 // Check that the irregexp code has been generated for the actual string
9677 // encoding. If it has, the field contains a code object otherwise it contains
9678 // the hole.
9679 __ CompareObjectType(r7, r0, r0, CODE_TYPE);
9680 __ b(ne, &runtime);
9681
9682 // r3: encoding of subject string (1 if ascii, 0 if two_byte);
9683 // r7: code
9684 // subject: Subject string
9685 // regexp_data: RegExp data (FixedArray)
9686 // Load used arguments before starting to push arguments for call to native
9687 // RegExp code to avoid handling changing stack height.
9688 __ ldr(r1, MemOperand(sp, kPreviousIndexOffset));
9689 __ mov(r1, Operand(r1, ASR, kSmiTagSize));
9690
9691 // r1: previous index
9692 // r3: encoding of subject string (1 if ascii, 0 if two_byte);
9693 // r7: code
9694 // subject: Subject string
9695 // regexp_data: RegExp data (FixedArray)
9696 // All checks done. Now push arguments for native regexp code.
9697 __ IncrementCounter(&Counters::regexp_entry_native, 1, r0, r2);
9698
9699 static const int kRegExpExecuteArguments = 7;
9700 __ push(lr);
9701 __ PrepareCallCFunction(kRegExpExecuteArguments, r0);
9702
9703 // Argument 7 (sp[8]): Indicate that this is a direct call from JavaScript.
9704 __ mov(r0, Operand(1));
9705 __ str(r0, MemOperand(sp, 2 * kPointerSize));
9706
9707 // Argument 6 (sp[4]): Start (high end) of backtracking stack memory area.
9708 __ mov(r0, Operand(address_of_regexp_stack_memory_address));
9709 __ ldr(r0, MemOperand(r0, 0));
9710 __ mov(r2, Operand(address_of_regexp_stack_memory_size));
9711 __ ldr(r2, MemOperand(r2, 0));
9712 __ add(r0, r0, Operand(r2));
9713 __ str(r0, MemOperand(sp, 1 * kPointerSize));
9714
9715 // Argument 5 (sp[0]): static offsets vector buffer.
9716 __ mov(r0, Operand(ExternalReference::address_of_static_offsets_vector()));
9717 __ str(r0, MemOperand(sp, 0 * kPointerSize));
9718
9719 // For arguments 4 and 3 get string length, calculate start of string data and
9720 // calculate the shift of the index (0 for ASCII and 1 for two byte).
9721 __ ldr(r0, FieldMemOperand(subject, String::kLengthOffset));
ager@chromium.orgac091b72010-05-05 07:34:42 +00009722 __ mov(r0, Operand(r0, ASR, kSmiTagSize));
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009723 ASSERT_EQ(SeqAsciiString::kHeaderSize, SeqTwoByteString::kHeaderSize);
9724 __ add(r9, subject, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
9725 __ eor(r3, r3, Operand(1));
9726 // Argument 4 (r3): End of string data
9727 // Argument 3 (r2): Start of string data
9728 __ add(r2, r9, Operand(r1, LSL, r3));
9729 __ add(r3, r9, Operand(r0, LSL, r3));
9730
9731 // Argument 2 (r1): Previous index.
9732 // Already there
9733
9734 // Argument 1 (r0): Subject string.
9735 __ mov(r0, subject);
9736
9737 // Locate the code entry and call it.
9738 __ add(r7, r7, Operand(Code::kHeaderSize - kHeapObjectTag));
9739 __ CallCFunction(r7, kRegExpExecuteArguments);
9740 __ pop(lr);
9741
9742 // r0: result
9743 // subject: subject string (callee saved)
9744 // regexp_data: RegExp data (callee saved)
9745 // last_match_info_elements: Last match info elements (callee saved)
9746
9747 // Check the result.
9748 Label success;
9749 __ cmp(r0, Operand(NativeRegExpMacroAssembler::SUCCESS));
9750 __ b(eq, &success);
9751 Label failure;
9752 __ cmp(r0, Operand(NativeRegExpMacroAssembler::FAILURE));
9753 __ b(eq, &failure);
9754 __ cmp(r0, Operand(NativeRegExpMacroAssembler::EXCEPTION));
9755 // If not exception it can only be retry. Handle that in the runtime system.
9756 __ b(ne, &runtime);
9757 // Result must now be exception. If there is no pending exception already a
9758 // stack overflow (on the backtrack stack) was detected in RegExp code but
9759 // haven't created the exception yet. Handle that in the runtime system.
9760 // TODO(592): Rerunning the RegExp to get the stack overflow exception.
9761 __ mov(r0, Operand(ExternalReference::the_hole_value_location()));
9762 __ ldr(r0, MemOperand(r0, 0));
9763 __ mov(r1, Operand(ExternalReference(Top::k_pending_exception_address)));
9764 __ ldr(r1, MemOperand(r1, 0));
9765 __ cmp(r0, r1);
9766 __ b(eq, &runtime);
9767 __ bind(&failure);
9768 // For failure and exception return null.
9769 __ mov(r0, Operand(Factory::null_value()));
9770 __ add(sp, sp, Operand(4 * kPointerSize));
9771 __ Ret();
9772
9773 // Process the result from the native regexp code.
9774 __ bind(&success);
9775 __ ldr(r1,
9776 FieldMemOperand(regexp_data, JSRegExp::kIrregexpCaptureCountOffset));
9777 // Calculate number of capture registers (number_of_captures + 1) * 2.
9778 ASSERT_EQ(0, kSmiTag);
9779 ASSERT_EQ(1, kSmiTagSize + kSmiShiftSize);
9780 __ add(r1, r1, Operand(2)); // r1 was a smi.
9781
9782 // r1: number of capture registers
9783 // r4: subject string
9784 // Store the capture count.
9785 __ mov(r2, Operand(r1, LSL, kSmiTagSize + kSmiShiftSize)); // To smi.
9786 __ str(r2, FieldMemOperand(last_match_info_elements,
9787 RegExpImpl::kLastCaptureCountOffset));
9788 // Store last subject and last input.
9789 __ mov(r3, last_match_info_elements); // Moved up to reduce latency.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009790 __ str(subject,
9791 FieldMemOperand(last_match_info_elements,
9792 RegExpImpl::kLastSubjectOffset));
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00009793 __ RecordWrite(r3, Operand(RegExpImpl::kLastSubjectOffset), r2, r7);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009794 __ str(subject,
9795 FieldMemOperand(last_match_info_elements,
9796 RegExpImpl::kLastInputOffset));
9797 __ mov(r3, last_match_info_elements);
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00009798 __ RecordWrite(r3, Operand(RegExpImpl::kLastInputOffset), r2, r7);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009799
9800 // Get the static offsets vector filled by the native regexp code.
9801 ExternalReference address_of_static_offsets_vector =
9802 ExternalReference::address_of_static_offsets_vector();
9803 __ mov(r2, Operand(address_of_static_offsets_vector));
9804
9805 // r1: number of capture registers
9806 // r2: offsets vector
9807 Label next_capture, done;
9808 // Capture register counter starts from number of capture registers and
9809 // counts down until wraping after zero.
9810 __ add(r0,
9811 last_match_info_elements,
9812 Operand(RegExpImpl::kFirstCaptureOffset - kHeapObjectTag));
9813 __ bind(&next_capture);
9814 __ sub(r1, r1, Operand(1), SetCC);
9815 __ b(mi, &done);
9816 // Read the value from the static offsets vector buffer.
9817 __ ldr(r3, MemOperand(r2, kPointerSize, PostIndex));
9818 // Store the smi value in the last match info.
9819 __ mov(r3, Operand(r3, LSL, kSmiTagSize));
9820 __ str(r3, MemOperand(r0, kPointerSize, PostIndex));
9821 __ jmp(&next_capture);
9822 __ bind(&done);
9823
9824 // Return last match info.
9825 __ ldr(r0, MemOperand(sp, kLastMatchInfoOffset));
9826 __ add(sp, sp, Operand(4 * kPointerSize));
9827 __ Ret();
9828
9829 // Do the runtime call to execute the regexp.
9830 __ bind(&runtime);
9831 __ TailCallRuntime(Runtime::kRegExpExec, 4, 1);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00009832#endif // V8_INTERPRETED_REGEXP
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009833}
9834
9835
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009836void CallFunctionStub::Generate(MacroAssembler* masm) {
9837 Label slow;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009838
9839 // If the receiver might be a value (string, number or boolean) check for this
9840 // and box it if it is.
9841 if (ReceiverMightBeValue()) {
9842 // Get the receiver from the stack.
9843 // function, receiver [, arguments]
9844 Label receiver_is_value, receiver_is_js_object;
9845 __ ldr(r1, MemOperand(sp, argc_ * kPointerSize));
9846
9847 // Check if receiver is a smi (which is a number value).
9848 __ BranchOnSmi(r1, &receiver_is_value);
9849
9850 // Check if the receiver is a valid JS object.
9851 __ CompareObjectType(r1, r2, r2, FIRST_JS_OBJECT_TYPE);
9852 __ b(ge, &receiver_is_js_object);
9853
9854 // Call the runtime to box the value.
9855 __ bind(&receiver_is_value);
9856 __ EnterInternalFrame();
9857 __ push(r1);
9858 __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_JS);
9859 __ LeaveInternalFrame();
9860 __ str(r0, MemOperand(sp, argc_ * kPointerSize));
9861
9862 __ bind(&receiver_is_js_object);
9863 }
9864
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009865 // Get the function to call from the stack.
9866 // function, receiver [, arguments]
9867 __ ldr(r1, MemOperand(sp, (argc_ + 1) * kPointerSize));
9868
9869 // Check that the function is really a JavaScript function.
9870 // r1: pushed function (to be verified)
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009871 __ BranchOnSmi(r1, &slow);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009872 // Get the map of the function object.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009873 __ CompareObjectType(r1, r2, r2, JS_FUNCTION_TYPE);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009874 __ b(ne, &slow);
9875
9876 // Fast-case: Invoke the function now.
9877 // r1: pushed function
9878 ParameterCount actual(argc_);
9879 __ InvokeFunction(r1, actual, JUMP_FUNCTION);
9880
9881 // Slow-case: Non-function called.
9882 __ bind(&slow);
ager@chromium.org5c838252010-02-19 08:53:10 +00009883 // CALL_NON_FUNCTION expects the non-function callee as receiver (instead
9884 // of the original receiver from the call site).
9885 __ str(r1, MemOperand(sp, argc_ * kPointerSize));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009886 __ mov(r0, Operand(argc_)); // Setup the number of arguments.
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009887 __ mov(r2, Operand(0));
9888 __ GetBuiltinEntry(r3, Builtins::CALL_NON_FUNCTION);
9889 __ Jump(Handle<Code>(Builtins::builtin(Builtins::ArgumentsAdaptorTrampoline)),
9890 RelocInfo::CODE_TARGET);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009891}
9892
9893
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00009894// Unfortunately you have to run without snapshots to see most of these
9895// names in the profile since most compare stubs end up in the snapshot.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00009896const char* CompareStub::GetName() {
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00009897 if (name_ != NULL) return name_;
9898 const int kMaxNameLength = 100;
9899 name_ = Bootstrapper::AllocateAutoDeletedArray(kMaxNameLength);
9900 if (name_ == NULL) return "OOM";
9901
9902 const char* cc_name;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00009903 switch (cc_) {
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00009904 case lt: cc_name = "LT"; break;
9905 case gt: cc_name = "GT"; break;
9906 case le: cc_name = "LE"; break;
9907 case ge: cc_name = "GE"; break;
9908 case eq: cc_name = "EQ"; break;
9909 case ne: cc_name = "NE"; break;
9910 default: cc_name = "UnknownCondition"; break;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00009911 }
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00009912
9913 const char* strict_name = "";
9914 if (strict_ && (cc_ == eq || cc_ == ne)) {
9915 strict_name = "_STRICT";
9916 }
9917
9918 const char* never_nan_nan_name = "";
9919 if (never_nan_nan_ && (cc_ == eq || cc_ == ne)) {
9920 never_nan_nan_name = "_NO_NAN";
9921 }
9922
9923 const char* include_number_compare_name = "";
9924 if (!include_number_compare_) {
9925 include_number_compare_name = "_NO_NUMBER";
9926 }
9927
9928 OS::SNPrintF(Vector<char>(name_, kMaxNameLength),
9929 "CompareStub_%s%s%s%s",
9930 cc_name,
9931 strict_name,
9932 never_nan_nan_name,
9933 include_number_compare_name);
9934 return name_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00009935}
9936
9937
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009938int CompareStub::MinorKey() {
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009939 // Encode the three parameters in a unique 16 bit value. To avoid duplicate
9940 // stubs the never NaN NaN condition is only taken into account if the
9941 // condition is equals.
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00009942 ASSERT((static_cast<unsigned>(cc_) >> 28) < (1 << 13));
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009943 return ConditionField::encode(static_cast<unsigned>(cc_) >> 28)
9944 | StrictField::encode(strict_)
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00009945 | NeverNanNanField::encode(cc_ == eq ? never_nan_nan_ : false)
9946 | IncludeNumberCompareField::encode(include_number_compare_);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009947}
9948
9949
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009950// StringCharCodeAtGenerator
9951
9952void StringCharCodeAtGenerator::GenerateFast(MacroAssembler* masm) {
9953 Label flat_string;
ager@chromium.orgac091b72010-05-05 07:34:42 +00009954 Label ascii_string;
9955 Label got_char_code;
9956
9957 // If the receiver is a smi trigger the non-string case.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009958 __ BranchOnSmi(object_, receiver_not_string_);
ager@chromium.orgac091b72010-05-05 07:34:42 +00009959
9960 // Fetch the instance type of the receiver into result register.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009961 __ ldr(result_, FieldMemOperand(object_, HeapObject::kMapOffset));
9962 __ ldrb(result_, FieldMemOperand(result_, Map::kInstanceTypeOffset));
ager@chromium.orgac091b72010-05-05 07:34:42 +00009963 // If the receiver is not a string trigger the non-string case.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009964 __ tst(result_, Operand(kIsNotStringMask));
9965 __ b(ne, receiver_not_string_);
ager@chromium.orgac091b72010-05-05 07:34:42 +00009966
9967 // If the index is non-smi trigger the non-smi case.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009968 __ BranchOnNotSmi(index_, &index_not_smi_);
9969
9970 // Put smi-tagged index into scratch register.
9971 __ mov(scratch_, index_);
9972 __ bind(&got_smi_index_);
ager@chromium.orgac091b72010-05-05 07:34:42 +00009973
9974 // Check for index out of range.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009975 __ ldr(ip, FieldMemOperand(object_, String::kLengthOffset));
9976 __ cmp(ip, Operand(scratch_));
9977 __ b(ls, index_out_of_range_);
ager@chromium.orgac091b72010-05-05 07:34:42 +00009978
9979 // We need special handling for non-flat strings.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009980 ASSERT(kSeqStringTag == 0);
9981 __ tst(result_, Operand(kStringRepresentationMask));
9982 __ b(eq, &flat_string);
ager@chromium.orgac091b72010-05-05 07:34:42 +00009983
9984 // Handle non-flat strings.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009985 __ tst(result_, Operand(kIsConsStringMask));
9986 __ b(eq, &call_runtime_);
ager@chromium.orgac091b72010-05-05 07:34:42 +00009987
9988 // ConsString.
9989 // Check whether the right hand side is the empty string (i.e. if
9990 // this is really a flat string in a cons string). If that is not
9991 // the case we would rather go to the runtime system now to flatten
9992 // the string.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009993 __ ldr(result_, FieldMemOperand(object_, ConsString::kSecondOffset));
9994 __ LoadRoot(ip, Heap::kEmptyStringRootIndex);
9995 __ cmp(result_, Operand(ip));
9996 __ b(ne, &call_runtime_);
ager@chromium.orgac091b72010-05-05 07:34:42 +00009997 // Get the first of the two strings and load its instance type.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009998 __ ldr(object_, FieldMemOperand(object_, ConsString::kFirstOffset));
9999 __ ldr(result_, FieldMemOperand(object_, HeapObject::kMapOffset));
10000 __ ldrb(result_, FieldMemOperand(result_, Map::kInstanceTypeOffset));
10001 // If the first cons component is also non-flat, then go to runtime.
10002 ASSERT(kSeqStringTag == 0);
10003 __ tst(result_, Operand(kStringRepresentationMask));
10004 __ b(nz, &call_runtime_);
10005
10006 // Check for 1-byte or 2-byte string.
10007 __ bind(&flat_string);
10008 ASSERT(kAsciiStringTag != 0);
10009 __ tst(result_, Operand(kStringEncodingMask));
10010 __ b(nz, &ascii_string);
10011
10012 // 2-byte string.
10013 // Load the 2-byte character code into the result register. We can
10014 // add without shifting since the smi tag size is the log2 of the
10015 // number of bytes in a two-byte character.
10016 ASSERT(kSmiTag == 0 && kSmiTagSize == 1 && kSmiShiftSize == 0);
10017 __ add(scratch_, object_, Operand(scratch_));
10018 __ ldrh(result_, FieldMemOperand(scratch_, SeqTwoByteString::kHeaderSize));
10019 __ jmp(&got_char_code);
ager@chromium.orgac091b72010-05-05 07:34:42 +000010020
10021 // ASCII string.
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010022 // Load the byte into the result register.
ager@chromium.orgac091b72010-05-05 07:34:42 +000010023 __ bind(&ascii_string);
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010024 __ add(scratch_, object_, Operand(scratch_, LSR, kSmiTagSize));
10025 __ ldrb(result_, FieldMemOperand(scratch_, SeqAsciiString::kHeaderSize));
ager@chromium.orgac091b72010-05-05 07:34:42 +000010026
10027 __ bind(&got_char_code);
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010028 __ mov(result_, Operand(result_, LSL, kSmiTagSize));
10029 __ bind(&exit_);
ager@chromium.orgac091b72010-05-05 07:34:42 +000010030}
10031
10032
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010033void StringCharCodeAtGenerator::GenerateSlow(
10034 MacroAssembler* masm, const RuntimeCallHelper& call_helper) {
10035 __ Abort("Unexpected fallthrough to CharCodeAt slow case");
ager@chromium.orgac091b72010-05-05 07:34:42 +000010036
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010037 // Index is not a smi.
10038 __ bind(&index_not_smi_);
10039 // If index is a heap number, try converting it to an integer.
ager@chromium.org2cc82ae2010-06-14 07:35:38 +000010040 __ CheckMap(index_,
10041 scratch_,
10042 Heap::kHeapNumberMapRootIndex,
10043 index_not_number_,
10044 true);
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010045 call_helper.BeforeCall(masm);
sgjesse@chromium.org82dbbab2010-06-02 08:57:44 +000010046 __ Push(object_, index_);
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010047 __ push(index_); // Consumed by runtime conversion function.
10048 if (index_flags_ == STRING_INDEX_IS_NUMBER) {
10049 __ CallRuntime(Runtime::kNumberToIntegerMapMinusZero, 1);
10050 } else {
10051 ASSERT(index_flags_ == STRING_INDEX_IS_ARRAY_INDEX);
10052 // NumberToSmi discards numbers that are not exact integers.
10053 __ CallRuntime(Runtime::kNumberToSmi, 1);
10054 }
10055 if (!scratch_.is(r0)) {
10056 // Save the conversion result before the pop instructions below
10057 // have a chance to overwrite it.
10058 __ mov(scratch_, r0);
10059 }
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010060 __ pop(index_);
10061 __ pop(object_);
sgjesse@chromium.org82dbbab2010-06-02 08:57:44 +000010062 // Reload the instance type.
10063 __ ldr(result_, FieldMemOperand(object_, HeapObject::kMapOffset));
10064 __ ldrb(result_, FieldMemOperand(result_, Map::kInstanceTypeOffset));
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010065 call_helper.AfterCall(masm);
10066 // If index is still not a smi, it must be out of range.
10067 __ BranchOnNotSmi(scratch_, index_out_of_range_);
10068 // Otherwise, return to the fast path.
10069 __ jmp(&got_smi_index_);
ager@chromium.orgac091b72010-05-05 07:34:42 +000010070
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010071 // Call runtime. We get here when the receiver is a string and the
10072 // index is a number, but the code of getting the actual character
10073 // is too complex (e.g., when the string needs to be flattened).
10074 __ bind(&call_runtime_);
10075 call_helper.BeforeCall(masm);
10076 __ Push(object_, index_);
10077 __ CallRuntime(Runtime::kStringCharCodeAt, 2);
10078 if (!result_.is(r0)) {
10079 __ mov(result_, r0);
10080 }
10081 call_helper.AfterCall(masm);
10082 __ jmp(&exit_);
10083
10084 __ Abort("Unexpected fallthrough from CharCodeAt slow case");
10085}
10086
10087
10088// -------------------------------------------------------------------------
10089// StringCharFromCodeGenerator
10090
10091void StringCharFromCodeGenerator::GenerateFast(MacroAssembler* masm) {
ager@chromium.orgac091b72010-05-05 07:34:42 +000010092 // Fast case of Heap::LookupSingleCharacterStringFromCode.
10093 ASSERT(kSmiTag == 0);
10094 ASSERT(kSmiShiftSize == 0);
10095 ASSERT(IsPowerOf2(String::kMaxAsciiCharCode + 1));
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010096 __ tst(code_,
10097 Operand(kSmiTagMask |
10098 ((~String::kMaxAsciiCharCode) << kSmiTagSize)));
10099 __ b(nz, &slow_case_);
ager@chromium.orgac091b72010-05-05 07:34:42 +000010100
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010101 __ LoadRoot(result_, Heap::kSingleCharacterStringCacheRootIndex);
10102 // At this point code register contains smi tagged ascii char code.
ager@chromium.orgac091b72010-05-05 07:34:42 +000010103 ASSERT(kSmiTag == 0);
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010104 __ add(result_, result_, Operand(code_, LSL, kPointerSizeLog2 - kSmiTagSize));
10105 __ ldr(result_, FieldMemOperand(result_, FixedArray::kHeaderSize));
10106 __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
10107 __ cmp(result_, Operand(ip));
10108 __ b(eq, &slow_case_);
10109 __ bind(&exit_);
10110}
ager@chromium.orgac091b72010-05-05 07:34:42 +000010111
ager@chromium.orgac091b72010-05-05 07:34:42 +000010112
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010113void StringCharFromCodeGenerator::GenerateSlow(
10114 MacroAssembler* masm, const RuntimeCallHelper& call_helper) {
10115 __ Abort("Unexpected fallthrough to CharFromCode slow case");
10116
10117 __ bind(&slow_case_);
10118 call_helper.BeforeCall(masm);
10119 __ push(code_);
10120 __ CallRuntime(Runtime::kCharFromCode, 1);
10121 if (!result_.is(r0)) {
10122 __ mov(result_, r0);
ager@chromium.orgac091b72010-05-05 07:34:42 +000010123 }
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010124 call_helper.AfterCall(masm);
10125 __ jmp(&exit_);
10126
10127 __ Abort("Unexpected fallthrough from CharFromCode slow case");
10128}
10129
10130
10131// -------------------------------------------------------------------------
10132// StringCharAtGenerator
10133
10134void StringCharAtGenerator::GenerateFast(MacroAssembler* masm) {
10135 char_code_at_generator_.GenerateFast(masm);
10136 char_from_code_generator_.GenerateFast(masm);
10137}
10138
10139
10140void StringCharAtGenerator::GenerateSlow(
10141 MacroAssembler* masm, const RuntimeCallHelper& call_helper) {
10142 char_code_at_generator_.GenerateSlow(masm, call_helper);
10143 char_from_code_generator_.GenerateSlow(masm, call_helper);
ager@chromium.orgac091b72010-05-05 07:34:42 +000010144}
10145
10146
10147void StringHelper::GenerateCopyCharacters(MacroAssembler* masm,
10148 Register dest,
10149 Register src,
10150 Register count,
10151 Register scratch,
10152 bool ascii) {
ager@chromium.org5c838252010-02-19 08:53:10 +000010153 Label loop;
10154 Label done;
10155 // This loop just copies one character at a time, as it is only used for very
10156 // short strings.
10157 if (!ascii) {
10158 __ add(count, count, Operand(count), SetCC);
10159 } else {
10160 __ cmp(count, Operand(0));
10161 }
10162 __ b(eq, &done);
10163
10164 __ bind(&loop);
10165 __ ldrb(scratch, MemOperand(src, 1, PostIndex));
10166 // Perform sub between load and dependent store to get the load time to
10167 // complete.
10168 __ sub(count, count, Operand(1), SetCC);
10169 __ strb(scratch, MemOperand(dest, 1, PostIndex));
10170 // last iteration.
10171 __ b(gt, &loop);
10172
10173 __ bind(&done);
10174}
10175
10176
10177enum CopyCharactersFlags {
10178 COPY_ASCII = 1,
10179 DEST_ALWAYS_ALIGNED = 2
10180};
10181
10182
ager@chromium.orgac091b72010-05-05 07:34:42 +000010183void StringHelper::GenerateCopyCharactersLong(MacroAssembler* masm,
10184 Register dest,
10185 Register src,
10186 Register count,
10187 Register scratch1,
10188 Register scratch2,
10189 Register scratch3,
10190 Register scratch4,
10191 Register scratch5,
10192 int flags) {
ager@chromium.org5c838252010-02-19 08:53:10 +000010193 bool ascii = (flags & COPY_ASCII) != 0;
10194 bool dest_always_aligned = (flags & DEST_ALWAYS_ALIGNED) != 0;
10195
10196 if (dest_always_aligned && FLAG_debug_code) {
10197 // Check that destination is actually word aligned if the flag says
10198 // that it is.
10199 __ tst(dest, Operand(kPointerAlignmentMask));
10200 __ Check(eq, "Destination of copy not aligned.");
10201 }
10202
10203 const int kReadAlignment = 4;
10204 const int kReadAlignmentMask = kReadAlignment - 1;
10205 // Ensure that reading an entire aligned word containing the last character
10206 // of a string will not read outside the allocated area (because we pad up
10207 // to kObjectAlignment).
10208 ASSERT(kObjectAlignment >= kReadAlignment);
10209 // Assumes word reads and writes are little endian.
10210 // Nothing to do for zero characters.
10211 Label done;
10212 if (!ascii) {
10213 __ add(count, count, Operand(count), SetCC);
10214 } else {
10215 __ cmp(count, Operand(0));
10216 }
10217 __ b(eq, &done);
10218
10219 // Assume that you cannot read (or write) unaligned.
10220 Label byte_loop;
10221 // Must copy at least eight bytes, otherwise just do it one byte at a time.
10222 __ cmp(count, Operand(8));
10223 __ add(count, dest, Operand(count));
10224 Register limit = count; // Read until src equals this.
10225 __ b(lt, &byte_loop);
10226
10227 if (!dest_always_aligned) {
10228 // Align dest by byte copying. Copies between zero and three bytes.
10229 __ and_(scratch4, dest, Operand(kReadAlignmentMask), SetCC);
10230 Label dest_aligned;
10231 __ b(eq, &dest_aligned);
10232 __ cmp(scratch4, Operand(2));
10233 __ ldrb(scratch1, MemOperand(src, 1, PostIndex));
10234 __ ldrb(scratch2, MemOperand(src, 1, PostIndex), le);
10235 __ ldrb(scratch3, MemOperand(src, 1, PostIndex), lt);
10236 __ strb(scratch1, MemOperand(dest, 1, PostIndex));
10237 __ strb(scratch2, MemOperand(dest, 1, PostIndex), le);
10238 __ strb(scratch3, MemOperand(dest, 1, PostIndex), lt);
10239 __ bind(&dest_aligned);
10240 }
10241
10242 Label simple_loop;
10243
10244 __ sub(scratch4, dest, Operand(src));
10245 __ and_(scratch4, scratch4, Operand(0x03), SetCC);
10246 __ b(eq, &simple_loop);
10247 // Shift register is number of bits in a source word that
10248 // must be combined with bits in the next source word in order
10249 // to create a destination word.
10250
10251 // Complex loop for src/dst that are not aligned the same way.
10252 {
10253 Label loop;
10254 __ mov(scratch4, Operand(scratch4, LSL, 3));
10255 Register left_shift = scratch4;
10256 __ and_(src, src, Operand(~3)); // Round down to load previous word.
10257 __ ldr(scratch1, MemOperand(src, 4, PostIndex));
10258 // Store the "shift" most significant bits of scratch in the least
10259 // signficant bits (i.e., shift down by (32-shift)).
10260 __ rsb(scratch2, left_shift, Operand(32));
10261 Register right_shift = scratch2;
10262 __ mov(scratch1, Operand(scratch1, LSR, right_shift));
10263
10264 __ bind(&loop);
10265 __ ldr(scratch3, MemOperand(src, 4, PostIndex));
10266 __ sub(scratch5, limit, Operand(dest));
10267 __ orr(scratch1, scratch1, Operand(scratch3, LSL, left_shift));
10268 __ str(scratch1, MemOperand(dest, 4, PostIndex));
10269 __ mov(scratch1, Operand(scratch3, LSR, right_shift));
10270 // Loop if four or more bytes left to copy.
10271 // Compare to eight, because we did the subtract before increasing dst.
10272 __ sub(scratch5, scratch5, Operand(8), SetCC);
10273 __ b(ge, &loop);
10274 }
10275 // There is now between zero and three bytes left to copy (negative that
10276 // number is in scratch5), and between one and three bytes already read into
10277 // scratch1 (eight times that number in scratch4). We may have read past
10278 // the end of the string, but because objects are aligned, we have not read
10279 // past the end of the object.
10280 // Find the minimum of remaining characters to move and preloaded characters
10281 // and write those as bytes.
10282 __ add(scratch5, scratch5, Operand(4), SetCC);
10283 __ b(eq, &done);
10284 __ cmp(scratch4, Operand(scratch5, LSL, 3), ne);
10285 // Move minimum of bytes read and bytes left to copy to scratch4.
10286 __ mov(scratch5, Operand(scratch4, LSR, 3), LeaveCC, lt);
10287 // Between one and three (value in scratch5) characters already read into
10288 // scratch ready to write.
10289 __ cmp(scratch5, Operand(2));
10290 __ strb(scratch1, MemOperand(dest, 1, PostIndex));
10291 __ mov(scratch1, Operand(scratch1, LSR, 8), LeaveCC, ge);
10292 __ strb(scratch1, MemOperand(dest, 1, PostIndex), ge);
10293 __ mov(scratch1, Operand(scratch1, LSR, 8), LeaveCC, gt);
10294 __ strb(scratch1, MemOperand(dest, 1, PostIndex), gt);
10295 // Copy any remaining bytes.
10296 __ b(&byte_loop);
10297
10298 // Simple loop.
10299 // Copy words from src to dst, until less than four bytes left.
10300 // Both src and dest are word aligned.
10301 __ bind(&simple_loop);
10302 {
10303 Label loop;
10304 __ bind(&loop);
10305 __ ldr(scratch1, MemOperand(src, 4, PostIndex));
10306 __ sub(scratch3, limit, Operand(dest));
10307 __ str(scratch1, MemOperand(dest, 4, PostIndex));
10308 // Compare to 8, not 4, because we do the substraction before increasing
10309 // dest.
10310 __ cmp(scratch3, Operand(8));
10311 __ b(ge, &loop);
10312 }
10313
10314 // Copy bytes from src to dst until dst hits limit.
10315 __ bind(&byte_loop);
10316 __ cmp(dest, Operand(limit));
10317 __ ldrb(scratch1, MemOperand(src, 1, PostIndex), lt);
10318 __ b(ge, &done);
10319 __ strb(scratch1, MemOperand(dest, 1, PostIndex));
10320 __ b(&byte_loop);
10321
10322 __ bind(&done);
10323}
10324
10325
ager@chromium.orgac091b72010-05-05 07:34:42 +000010326void StringHelper::GenerateTwoCharacterSymbolTableProbe(MacroAssembler* masm,
10327 Register c1,
10328 Register c2,
10329 Register scratch1,
10330 Register scratch2,
10331 Register scratch3,
10332 Register scratch4,
10333 Register scratch5,
10334 Label* not_found) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010335 // Register scratch3 is the general scratch register in this function.
10336 Register scratch = scratch3;
10337
10338 // Make sure that both characters are not digits as such strings has a
10339 // different hash algorithm. Don't try to look for these in the symbol table.
10340 Label not_array_index;
10341 __ sub(scratch, c1, Operand(static_cast<int>('0')));
10342 __ cmp(scratch, Operand(static_cast<int>('9' - '0')));
10343 __ b(hi, &not_array_index);
10344 __ sub(scratch, c2, Operand(static_cast<int>('0')));
10345 __ cmp(scratch, Operand(static_cast<int>('9' - '0')));
10346
10347 // If check failed combine both characters into single halfword.
10348 // This is required by the contract of the method: code at the
10349 // not_found branch expects this combination in c1 register
10350 __ orr(c1, c1, Operand(c2, LSL, kBitsPerByte), LeaveCC, ls);
10351 __ b(ls, not_found);
10352
10353 __ bind(&not_array_index);
10354 // Calculate the two character string hash.
10355 Register hash = scratch1;
ager@chromium.orgac091b72010-05-05 07:34:42 +000010356 StringHelper::GenerateHashInit(masm, hash, c1);
10357 StringHelper::GenerateHashAddCharacter(masm, hash, c2);
10358 StringHelper::GenerateHashGetHash(masm, hash);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010359
10360 // Collect the two characters in a register.
10361 Register chars = c1;
10362 __ orr(chars, chars, Operand(c2, LSL, kBitsPerByte));
10363
10364 // chars: two character string, char 1 in byte 0 and char 2 in byte 1.
10365 // hash: hash of two character string.
10366
10367 // Load symbol table
10368 // Load address of first element of the symbol table.
10369 Register symbol_table = c2;
10370 __ LoadRoot(symbol_table, Heap::kSymbolTableRootIndex);
10371
10372 // Load undefined value
10373 Register undefined = scratch4;
10374 __ LoadRoot(undefined, Heap::kUndefinedValueRootIndex);
10375
10376 // Calculate capacity mask from the symbol table capacity.
10377 Register mask = scratch2;
10378 __ ldr(mask, FieldMemOperand(symbol_table, SymbolTable::kCapacityOffset));
10379 __ mov(mask, Operand(mask, ASR, 1));
10380 __ sub(mask, mask, Operand(1));
10381
10382 // Calculate untagged address of the first element of the symbol table.
10383 Register first_symbol_table_element = symbol_table;
10384 __ add(first_symbol_table_element, symbol_table,
10385 Operand(SymbolTable::kElementsStartOffset - kHeapObjectTag));
10386
10387 // Registers
10388 // chars: two character string, char 1 in byte 0 and char 2 in byte 1.
10389 // hash: hash of two character string
10390 // mask: capacity mask
10391 // first_symbol_table_element: address of the first element of
10392 // the symbol table
10393 // scratch: -
10394
10395 // Perform a number of probes in the symbol table.
10396 static const int kProbes = 4;
10397 Label found_in_symbol_table;
10398 Label next_probe[kProbes];
10399 for (int i = 0; i < kProbes; i++) {
10400 Register candidate = scratch5; // Scratch register contains candidate.
10401
10402 // Calculate entry in symbol table.
10403 if (i > 0) {
10404 __ add(candidate, hash, Operand(SymbolTable::GetProbeOffset(i)));
10405 } else {
10406 __ mov(candidate, hash);
10407 }
10408
10409 __ and_(candidate, candidate, Operand(mask));
10410
10411 // Load the entry from the symble table.
10412 ASSERT_EQ(1, SymbolTable::kEntrySize);
10413 __ ldr(candidate,
10414 MemOperand(first_symbol_table_element,
10415 candidate,
10416 LSL,
10417 kPointerSizeLog2));
10418
10419 // If entry is undefined no string with this hash can be found.
10420 __ cmp(candidate, undefined);
10421 __ b(eq, not_found);
10422
10423 // If length is not 2 the string is not a candidate.
10424 __ ldr(scratch, FieldMemOperand(candidate, String::kLengthOffset));
ager@chromium.orgac091b72010-05-05 07:34:42 +000010425 __ cmp(scratch, Operand(Smi::FromInt(2)));
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010426 __ b(ne, &next_probe[i]);
10427
10428 // Check that the candidate is a non-external ascii string.
10429 __ ldr(scratch, FieldMemOperand(candidate, HeapObject::kMapOffset));
10430 __ ldrb(scratch, FieldMemOperand(scratch, Map::kInstanceTypeOffset));
10431 __ JumpIfInstanceTypeIsNotSequentialAscii(scratch, scratch,
10432 &next_probe[i]);
10433
10434 // Check if the two characters match.
10435 // Assumes that word load is little endian.
10436 __ ldrh(scratch, FieldMemOperand(candidate, SeqAsciiString::kHeaderSize));
10437 __ cmp(chars, scratch);
10438 __ b(eq, &found_in_symbol_table);
10439 __ bind(&next_probe[i]);
10440 }
10441
10442 // No matching 2 character string found by probing.
10443 __ jmp(not_found);
10444
10445 // Scratch register contains result when we fall through to here.
10446 Register result = scratch;
10447 __ bind(&found_in_symbol_table);
ager@chromium.org357bf652010-04-12 11:30:10 +000010448 __ Move(r0, result);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010449}
10450
10451
ager@chromium.orgac091b72010-05-05 07:34:42 +000010452void StringHelper::GenerateHashInit(MacroAssembler* masm,
10453 Register hash,
10454 Register character) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010455 // hash = character + (character << 10);
10456 __ add(hash, character, Operand(character, LSL, 10));
10457 // hash ^= hash >> 6;
10458 __ eor(hash, hash, Operand(hash, ASR, 6));
10459}
10460
10461
ager@chromium.orgac091b72010-05-05 07:34:42 +000010462void StringHelper::GenerateHashAddCharacter(MacroAssembler* masm,
10463 Register hash,
10464 Register character) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010465 // hash += character;
10466 __ add(hash, hash, Operand(character));
10467 // hash += hash << 10;
10468 __ add(hash, hash, Operand(hash, LSL, 10));
10469 // hash ^= hash >> 6;
10470 __ eor(hash, hash, Operand(hash, ASR, 6));
10471}
10472
10473
ager@chromium.orgac091b72010-05-05 07:34:42 +000010474void StringHelper::GenerateHashGetHash(MacroAssembler* masm,
10475 Register hash) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010476 // hash += hash << 3;
10477 __ add(hash, hash, Operand(hash, LSL, 3));
10478 // hash ^= hash >> 11;
10479 __ eor(hash, hash, Operand(hash, ASR, 11));
10480 // hash += hash << 15;
10481 __ add(hash, hash, Operand(hash, LSL, 15), SetCC);
10482
10483 // if (hash == 0) hash = 27;
10484 __ mov(hash, Operand(27), LeaveCC, nz);
10485}
10486
10487
ager@chromium.org5c838252010-02-19 08:53:10 +000010488void SubStringStub::Generate(MacroAssembler* masm) {
10489 Label runtime;
10490
10491 // Stack frame on entry.
10492 // lr: return address
10493 // sp[0]: to
10494 // sp[4]: from
10495 // sp[8]: string
10496
10497 // This stub is called from the native-call %_SubString(...), so
10498 // nothing can be assumed about the arguments. It is tested that:
10499 // "string" is a sequential string,
10500 // both "from" and "to" are smis, and
10501 // 0 <= from <= to <= string.length.
10502 // If any of these assumptions fail, we call the runtime system.
10503
10504 static const int kToOffset = 0 * kPointerSize;
10505 static const int kFromOffset = 1 * kPointerSize;
10506 static const int kStringOffset = 2 * kPointerSize;
10507
10508
10509 // Check bounds and smi-ness.
10510 __ ldr(r7, MemOperand(sp, kToOffset));
10511 __ ldr(r6, MemOperand(sp, kFromOffset));
10512 ASSERT_EQ(0, kSmiTag);
10513 ASSERT_EQ(1, kSmiTagSize + kSmiShiftSize);
10514 // I.e., arithmetic shift right by one un-smi-tags.
10515 __ mov(r2, Operand(r7, ASR, 1), SetCC);
10516 __ mov(r3, Operand(r6, ASR, 1), SetCC, cc);
10517 // If either r2 or r6 had the smi tag bit set, then carry is set now.
10518 __ b(cs, &runtime); // Either "from" or "to" is not a smi.
10519 __ b(mi, &runtime); // From is negative.
10520
10521 __ sub(r2, r2, Operand(r3), SetCC);
10522 __ b(mi, &runtime); // Fail if from > to.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010523 // Special handling of sub-strings of length 1 and 2. One character strings
10524 // are handled in the runtime system (looked up in the single character
10525 // cache). Two character strings are looked for in the symbol cache.
ager@chromium.org5c838252010-02-19 08:53:10 +000010526 __ cmp(r2, Operand(2));
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010527 __ b(lt, &runtime);
ager@chromium.org5c838252010-02-19 08:53:10 +000010528
10529 // r2: length
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010530 // r3: from index (untaged smi)
ager@chromium.org5c838252010-02-19 08:53:10 +000010531 // r6: from (smi)
10532 // r7: to (smi)
10533
10534 // Make sure first argument is a sequential (or flat) string.
10535 __ ldr(r5, MemOperand(sp, kStringOffset));
10536 ASSERT_EQ(0, kSmiTag);
10537 __ tst(r5, Operand(kSmiTagMask));
10538 __ b(eq, &runtime);
10539 Condition is_string = masm->IsObjectStringType(r5, r1);
10540 __ b(NegateCondition(is_string), &runtime);
10541
10542 // r1: instance type
10543 // r2: length
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010544 // r3: from index (untaged smi)
ager@chromium.org5c838252010-02-19 08:53:10 +000010545 // r5: string
10546 // r6: from (smi)
10547 // r7: to (smi)
10548 Label seq_string;
10549 __ and_(r4, r1, Operand(kStringRepresentationMask));
10550 ASSERT(kSeqStringTag < kConsStringTag);
10551 ASSERT(kExternalStringTag > kConsStringTag);
10552 __ cmp(r4, Operand(kConsStringTag));
10553 __ b(gt, &runtime); // External strings go to runtime.
10554 __ b(lt, &seq_string); // Sequential strings are handled directly.
10555
10556 // Cons string. Try to recurse (once) on the first substring.
10557 // (This adds a little more generality than necessary to handle flattened
10558 // cons strings, but not much).
10559 __ ldr(r5, FieldMemOperand(r5, ConsString::kFirstOffset));
10560 __ ldr(r4, FieldMemOperand(r5, HeapObject::kMapOffset));
10561 __ ldrb(r1, FieldMemOperand(r4, Map::kInstanceTypeOffset));
10562 __ tst(r1, Operand(kStringRepresentationMask));
10563 ASSERT_EQ(0, kSeqStringTag);
10564 __ b(ne, &runtime); // Cons and External strings go to runtime.
10565
10566 // Definitly a sequential string.
10567 __ bind(&seq_string);
10568
10569 // r1: instance type.
10570 // r2: length
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010571 // r3: from index (untaged smi)
ager@chromium.org5c838252010-02-19 08:53:10 +000010572 // r5: string
10573 // r6: from (smi)
10574 // r7: to (smi)
10575 __ ldr(r4, FieldMemOperand(r5, String::kLengthOffset));
ager@chromium.orgac091b72010-05-05 07:34:42 +000010576 __ cmp(r4, Operand(r7));
ager@chromium.org5c838252010-02-19 08:53:10 +000010577 __ b(lt, &runtime); // Fail if to > length.
10578
10579 // r1: instance type.
10580 // r2: result string length.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010581 // r3: from index (untaged smi)
ager@chromium.org5c838252010-02-19 08:53:10 +000010582 // r5: string.
10583 // r6: from offset (smi)
10584 // Check for flat ascii string.
10585 Label non_ascii_flat;
10586 __ tst(r1, Operand(kStringEncodingMask));
10587 ASSERT_EQ(0, kTwoByteStringTag);
10588 __ b(eq, &non_ascii_flat);
10589
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010590 Label result_longer_than_two;
10591 __ cmp(r2, Operand(2));
10592 __ b(gt, &result_longer_than_two);
10593
10594 // Sub string of length 2 requested.
10595 // Get the two characters forming the sub string.
10596 __ add(r5, r5, Operand(r3));
10597 __ ldrb(r3, FieldMemOperand(r5, SeqAsciiString::kHeaderSize));
10598 __ ldrb(r4, FieldMemOperand(r5, SeqAsciiString::kHeaderSize + 1));
10599
10600 // Try to lookup two character string in symbol table.
10601 Label make_two_character_string;
ager@chromium.orgac091b72010-05-05 07:34:42 +000010602 StringHelper::GenerateTwoCharacterSymbolTableProbe(
10603 masm, r3, r4, r1, r5, r6, r7, r9, &make_two_character_string);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010604 __ IncrementCounter(&Counters::sub_string_native, 1, r3, r4);
10605 __ add(sp, sp, Operand(3 * kPointerSize));
10606 __ Ret();
10607
10608 // r2: result string length.
10609 // r3: two characters combined into halfword in little endian byte order.
10610 __ bind(&make_two_character_string);
10611 __ AllocateAsciiString(r0, r2, r4, r5, r9, &runtime);
10612 __ strh(r3, FieldMemOperand(r0, SeqAsciiString::kHeaderSize));
10613 __ IncrementCounter(&Counters::sub_string_native, 1, r3, r4);
10614 __ add(sp, sp, Operand(3 * kPointerSize));
10615 __ Ret();
10616
10617 __ bind(&result_longer_than_two);
10618
ager@chromium.org5c838252010-02-19 08:53:10 +000010619 // Allocate the result.
10620 __ AllocateAsciiString(r0, r2, r3, r4, r1, &runtime);
10621
10622 // r0: result string.
10623 // r2: result string length.
10624 // r5: string.
10625 // r6: from offset (smi)
10626 // Locate first character of result.
10627 __ add(r1, r0, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
10628 // Locate 'from' character of string.
10629 __ add(r5, r5, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
10630 __ add(r5, r5, Operand(r6, ASR, 1));
10631
10632 // r0: result string.
10633 // r1: first character of result string.
10634 // r2: result string length.
10635 // r5: first character of sub string to copy.
10636 ASSERT_EQ(0, SeqAsciiString::kHeaderSize & kObjectAlignmentMask);
ager@chromium.orgac091b72010-05-05 07:34:42 +000010637 StringHelper::GenerateCopyCharactersLong(masm, r1, r5, r2, r3, r4, r6, r7, r9,
10638 COPY_ASCII | DEST_ALWAYS_ALIGNED);
ager@chromium.org5c838252010-02-19 08:53:10 +000010639 __ IncrementCounter(&Counters::sub_string_native, 1, r3, r4);
10640 __ add(sp, sp, Operand(3 * kPointerSize));
10641 __ Ret();
10642
10643 __ bind(&non_ascii_flat);
10644 // r2: result string length.
10645 // r5: string.
10646 // r6: from offset (smi)
10647 // Check for flat two byte string.
10648
10649 // Allocate the result.
10650 __ AllocateTwoByteString(r0, r2, r1, r3, r4, &runtime);
10651
10652 // r0: result string.
10653 // r2: result string length.
10654 // r5: string.
10655 // Locate first character of result.
10656 __ add(r1, r0, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
10657 // Locate 'from' character of string.
10658 __ add(r5, r5, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
10659 // As "from" is a smi it is 2 times the value which matches the size of a two
10660 // byte character.
10661 __ add(r5, r5, Operand(r6));
10662
10663 // r0: result string.
10664 // r1: first character of result.
10665 // r2: result length.
10666 // r5: first character of string to copy.
10667 ASSERT_EQ(0, SeqTwoByteString::kHeaderSize & kObjectAlignmentMask);
ager@chromium.orgac091b72010-05-05 07:34:42 +000010668 StringHelper::GenerateCopyCharactersLong(masm, r1, r5, r2, r3, r4, r6, r7, r9,
10669 DEST_ALWAYS_ALIGNED);
ager@chromium.org5c838252010-02-19 08:53:10 +000010670 __ IncrementCounter(&Counters::sub_string_native, 1, r3, r4);
10671 __ add(sp, sp, Operand(3 * kPointerSize));
10672 __ Ret();
10673
10674 // Just jump to runtime to create the sub string.
10675 __ bind(&runtime);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010676 __ TailCallRuntime(Runtime::kSubString, 3, 1);
ager@chromium.org5c838252010-02-19 08:53:10 +000010677}
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010678
10679
10680void StringCompareStub::GenerateCompareFlatAsciiStrings(MacroAssembler* masm,
10681 Register left,
10682 Register right,
10683 Register scratch1,
10684 Register scratch2,
10685 Register scratch3,
10686 Register scratch4) {
10687 Label compare_lengths;
10688 // Find minimum length and length difference.
10689 __ ldr(scratch1, FieldMemOperand(left, String::kLengthOffset));
10690 __ ldr(scratch2, FieldMemOperand(right, String::kLengthOffset));
10691 __ sub(scratch3, scratch1, Operand(scratch2), SetCC);
10692 Register length_delta = scratch3;
10693 __ mov(scratch1, scratch2, LeaveCC, gt);
10694 Register min_length = scratch1;
ager@chromium.orgac091b72010-05-05 07:34:42 +000010695 ASSERT(kSmiTag == 0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010696 __ tst(min_length, Operand(min_length));
10697 __ b(eq, &compare_lengths);
10698
ager@chromium.orgac091b72010-05-05 07:34:42 +000010699 // Untag smi.
10700 __ mov(min_length, Operand(min_length, ASR, kSmiTagSize));
10701
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010702 // Setup registers so that we only need to increment one register
10703 // in the loop.
10704 __ add(scratch2, min_length,
10705 Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
10706 __ add(left, left, Operand(scratch2));
10707 __ add(right, right, Operand(scratch2));
10708 // Registers left and right points to the min_length character of strings.
10709 __ rsb(min_length, min_length, Operand(-1));
10710 Register index = min_length;
10711 // Index starts at -min_length.
10712
10713 {
10714 // Compare loop.
10715 Label loop;
10716 __ bind(&loop);
10717 // Compare characters.
10718 __ add(index, index, Operand(1), SetCC);
10719 __ ldrb(scratch2, MemOperand(left, index), ne);
10720 __ ldrb(scratch4, MemOperand(right, index), ne);
10721 // Skip to compare lengths with eq condition true.
10722 __ b(eq, &compare_lengths);
10723 __ cmp(scratch2, scratch4);
10724 __ b(eq, &loop);
10725 // Fallthrough with eq condition false.
10726 }
10727 // Compare lengths - strings up to min-length are equal.
10728 __ bind(&compare_lengths);
10729 ASSERT(Smi::FromInt(EQUAL) == static_cast<Smi*>(0));
10730 // Use zero length_delta as result.
10731 __ mov(r0, Operand(length_delta), SetCC, eq);
10732 // Fall through to here if characters compare not-equal.
10733 __ mov(r0, Operand(Smi::FromInt(GREATER)), LeaveCC, gt);
10734 __ mov(r0, Operand(Smi::FromInt(LESS)), LeaveCC, lt);
10735 __ Ret();
10736}
10737
10738
10739void StringCompareStub::Generate(MacroAssembler* masm) {
10740 Label runtime;
10741
10742 // Stack frame on entry.
ager@chromium.org5c838252010-02-19 08:53:10 +000010743 // sp[0]: right string
10744 // sp[4]: left string
10745 __ ldr(r0, MemOperand(sp, 1 * kPointerSize)); // left
10746 __ ldr(r1, MemOperand(sp, 0 * kPointerSize)); // right
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010747
10748 Label not_same;
10749 __ cmp(r0, r1);
10750 __ b(ne, &not_same);
10751 ASSERT_EQ(0, EQUAL);
10752 ASSERT_EQ(0, kSmiTag);
10753 __ mov(r0, Operand(Smi::FromInt(EQUAL)));
10754 __ IncrementCounter(&Counters::string_compare_native, 1, r1, r2);
10755 __ add(sp, sp, Operand(2 * kPointerSize));
10756 __ Ret();
10757
10758 __ bind(&not_same);
10759
10760 // Check that both objects are sequential ascii strings.
10761 __ JumpIfNotBothSequentialAsciiStrings(r0, r1, r2, r3, &runtime);
10762
10763 // Compare flat ascii strings natively. Remove arguments from stack first.
10764 __ IncrementCounter(&Counters::string_compare_native, 1, r2, r3);
10765 __ add(sp, sp, Operand(2 * kPointerSize));
10766 GenerateCompareFlatAsciiStrings(masm, r0, r1, r2, r3, r4, r5);
10767
10768 // Call the runtime; it returns -1 (less), 0 (equal), or 1 (greater)
10769 // tagged as a small integer.
10770 __ bind(&runtime);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010771 __ TailCallRuntime(Runtime::kStringCompare, 2, 1);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010772}
10773
10774
ager@chromium.org5c838252010-02-19 08:53:10 +000010775void StringAddStub::Generate(MacroAssembler* masm) {
10776 Label string_add_runtime;
10777 // Stack on entry:
10778 // sp[0]: second argument.
10779 // sp[4]: first argument.
10780
10781 // Load the two arguments.
10782 __ ldr(r0, MemOperand(sp, 1 * kPointerSize)); // First argument.
10783 __ ldr(r1, MemOperand(sp, 0 * kPointerSize)); // Second argument.
10784
10785 // Make sure that both arguments are strings if not known in advance.
10786 if (string_check_) {
10787 ASSERT_EQ(0, kSmiTag);
10788 __ JumpIfEitherSmi(r0, r1, &string_add_runtime);
10789 // Load instance types.
10790 __ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset));
10791 __ ldr(r5, FieldMemOperand(r1, HeapObject::kMapOffset));
10792 __ ldrb(r4, FieldMemOperand(r4, Map::kInstanceTypeOffset));
10793 __ ldrb(r5, FieldMemOperand(r5, Map::kInstanceTypeOffset));
10794 ASSERT_EQ(0, kStringTag);
10795 // If either is not a string, go to runtime.
10796 __ tst(r4, Operand(kIsNotStringMask));
10797 __ tst(r5, Operand(kIsNotStringMask), eq);
10798 __ b(ne, &string_add_runtime);
10799 }
10800
10801 // Both arguments are strings.
10802 // r0: first string
10803 // r1: second string
10804 // r4: first string instance type (if string_check_)
10805 // r5: second string instance type (if string_check_)
10806 {
10807 Label strings_not_empty;
10808 // Check if either of the strings are empty. In that case return the other.
10809 __ ldr(r2, FieldMemOperand(r0, String::kLengthOffset));
10810 __ ldr(r3, FieldMemOperand(r1, String::kLengthOffset));
ager@chromium.orgac091b72010-05-05 07:34:42 +000010811 ASSERT(kSmiTag == 0);
10812 __ cmp(r2, Operand(Smi::FromInt(0))); // Test if first string is empty.
ager@chromium.org5c838252010-02-19 08:53:10 +000010813 __ mov(r0, Operand(r1), LeaveCC, eq); // If first is empty, return second.
ager@chromium.orgac091b72010-05-05 07:34:42 +000010814 ASSERT(kSmiTag == 0);
10815 // Else test if second string is empty.
10816 __ cmp(r3, Operand(Smi::FromInt(0)), ne);
ager@chromium.org5c838252010-02-19 08:53:10 +000010817 __ b(ne, &strings_not_empty); // If either string was empty, return r0.
10818
10819 __ IncrementCounter(&Counters::string_add_native, 1, r2, r3);
10820 __ add(sp, sp, Operand(2 * kPointerSize));
10821 __ Ret();
10822
10823 __ bind(&strings_not_empty);
10824 }
10825
ager@chromium.orgac091b72010-05-05 07:34:42 +000010826 __ mov(r2, Operand(r2, ASR, kSmiTagSize));
10827 __ mov(r3, Operand(r3, ASR, kSmiTagSize));
ager@chromium.org5c838252010-02-19 08:53:10 +000010828 // Both strings are non-empty.
10829 // r0: first string
10830 // r1: second string
10831 // r2: length of first string
10832 // r3: length of second string
10833 // r4: first string instance type (if string_check_)
10834 // r5: second string instance type (if string_check_)
10835 // Look at the length of the result of adding the two strings.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010836 Label string_add_flat_result, longer_than_two;
ager@chromium.org5c838252010-02-19 08:53:10 +000010837 // Adding two lengths can't overflow.
10838 ASSERT(String::kMaxLength * 2 > String::kMaxLength);
10839 __ add(r6, r2, Operand(r3));
10840 // Use the runtime system when adding two one character strings, as it
10841 // contains optimizations for this specific case using the symbol table.
10842 __ cmp(r6, Operand(2));
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010843 __ b(ne, &longer_than_two);
10844
10845 // Check that both strings are non-external ascii strings.
10846 if (!string_check_) {
10847 __ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset));
10848 __ ldr(r5, FieldMemOperand(r1, HeapObject::kMapOffset));
10849 __ ldrb(r4, FieldMemOperand(r4, Map::kInstanceTypeOffset));
10850 __ ldrb(r5, FieldMemOperand(r5, Map::kInstanceTypeOffset));
10851 }
10852 __ JumpIfBothInstanceTypesAreNotSequentialAscii(r4, r5, r6, r7,
10853 &string_add_runtime);
10854
10855 // Get the two characters forming the sub string.
10856 __ ldrb(r2, FieldMemOperand(r0, SeqAsciiString::kHeaderSize));
10857 __ ldrb(r3, FieldMemOperand(r1, SeqAsciiString::kHeaderSize));
10858
10859 // Try to lookup two character string in symbol table. If it is not found
10860 // just allocate a new one.
10861 Label make_two_character_string;
ager@chromium.orgac091b72010-05-05 07:34:42 +000010862 StringHelper::GenerateTwoCharacterSymbolTableProbe(
10863 masm, r2, r3, r6, r7, r4, r5, r9, &make_two_character_string);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010864 __ IncrementCounter(&Counters::string_add_native, 1, r2, r3);
10865 __ add(sp, sp, Operand(2 * kPointerSize));
10866 __ Ret();
10867
10868 __ bind(&make_two_character_string);
10869 // Resulting string has length 2 and first chars of two strings
10870 // are combined into single halfword in r2 register.
10871 // So we can fill resulting string without two loops by a single
10872 // halfword store instruction (which assumes that processor is
10873 // in a little endian mode)
10874 __ mov(r6, Operand(2));
10875 __ AllocateAsciiString(r0, r6, r4, r5, r9, &string_add_runtime);
10876 __ strh(r2, FieldMemOperand(r0, SeqAsciiString::kHeaderSize));
10877 __ IncrementCounter(&Counters::string_add_native, 1, r2, r3);
10878 __ add(sp, sp, Operand(2 * kPointerSize));
10879 __ Ret();
10880
10881 __ bind(&longer_than_two);
ager@chromium.org5c838252010-02-19 08:53:10 +000010882 // Check if resulting string will be flat.
10883 __ cmp(r6, Operand(String::kMinNonFlatLength));
10884 __ b(lt, &string_add_flat_result);
10885 // Handle exceptionally long strings in the runtime system.
10886 ASSERT((String::kMaxLength & 0x80000000) == 0);
10887 ASSERT(IsPowerOf2(String::kMaxLength + 1));
10888 // kMaxLength + 1 is representable as shifted literal, kMaxLength is not.
10889 __ cmp(r6, Operand(String::kMaxLength + 1));
10890 __ b(hs, &string_add_runtime);
10891
10892 // If result is not supposed to be flat, allocate a cons string object.
10893 // If both strings are ascii the result is an ascii cons string.
10894 if (!string_check_) {
10895 __ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset));
10896 __ ldr(r5, FieldMemOperand(r1, HeapObject::kMapOffset));
10897 __ ldrb(r4, FieldMemOperand(r4, Map::kInstanceTypeOffset));
10898 __ ldrb(r5, FieldMemOperand(r5, Map::kInstanceTypeOffset));
10899 }
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +000010900 Label non_ascii, allocated, ascii_data;
ager@chromium.org5c838252010-02-19 08:53:10 +000010901 ASSERT_EQ(0, kTwoByteStringTag);
10902 __ tst(r4, Operand(kStringEncodingMask));
10903 __ tst(r5, Operand(kStringEncodingMask), ne);
10904 __ b(eq, &non_ascii);
10905
10906 // Allocate an ASCII cons string.
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +000010907 __ bind(&ascii_data);
ager@chromium.org5c838252010-02-19 08:53:10 +000010908 __ AllocateAsciiConsString(r7, r6, r4, r5, &string_add_runtime);
10909 __ bind(&allocated);
10910 // Fill the fields of the cons string.
10911 __ str(r0, FieldMemOperand(r7, ConsString::kFirstOffset));
10912 __ str(r1, FieldMemOperand(r7, ConsString::kSecondOffset));
10913 __ mov(r0, Operand(r7));
10914 __ IncrementCounter(&Counters::string_add_native, 1, r2, r3);
10915 __ add(sp, sp, Operand(2 * kPointerSize));
10916 __ Ret();
10917
10918 __ bind(&non_ascii);
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +000010919 // At least one of the strings is two-byte. Check whether it happens
10920 // to contain only ascii characters.
10921 // r4: first instance type.
10922 // r5: second instance type.
10923 __ tst(r4, Operand(kAsciiDataHintMask));
10924 __ tst(r5, Operand(kAsciiDataHintMask), ne);
10925 __ b(ne, &ascii_data);
10926 __ eor(r4, r4, Operand(r5));
10927 ASSERT(kAsciiStringTag != 0 && kAsciiDataHintTag != 0);
10928 __ and_(r4, r4, Operand(kAsciiStringTag | kAsciiDataHintTag));
10929 __ cmp(r4, Operand(kAsciiStringTag | kAsciiDataHintTag));
10930 __ b(eq, &ascii_data);
10931
ager@chromium.org5c838252010-02-19 08:53:10 +000010932 // Allocate a two byte cons string.
10933 __ AllocateTwoByteConsString(r7, r6, r4, r5, &string_add_runtime);
10934 __ jmp(&allocated);
10935
10936 // Handle creating a flat result. First check that both strings are
10937 // sequential and that they have the same encoding.
10938 // r0: first string
10939 // r1: second string
10940 // r2: length of first string
10941 // r3: length of second string
10942 // r4: first string instance type (if string_check_)
10943 // r5: second string instance type (if string_check_)
10944 // r6: sum of lengths.
10945 __ bind(&string_add_flat_result);
10946 if (!string_check_) {
10947 __ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset));
10948 __ ldr(r5, FieldMemOperand(r1, HeapObject::kMapOffset));
10949 __ ldrb(r4, FieldMemOperand(r4, Map::kInstanceTypeOffset));
10950 __ ldrb(r5, FieldMemOperand(r5, Map::kInstanceTypeOffset));
10951 }
10952 // Check that both strings are sequential.
10953 ASSERT_EQ(0, kSeqStringTag);
10954 __ tst(r4, Operand(kStringRepresentationMask));
10955 __ tst(r5, Operand(kStringRepresentationMask), eq);
10956 __ b(ne, &string_add_runtime);
10957 // Now check if both strings have the same encoding (ASCII/Two-byte).
10958 // r0: first string.
10959 // r1: second string.
10960 // r2: length of first string.
10961 // r3: length of second string.
10962 // r6: sum of lengths..
10963 Label non_ascii_string_add_flat_result;
10964 ASSERT(IsPowerOf2(kStringEncodingMask)); // Just one bit to test.
10965 __ eor(r7, r4, Operand(r5));
10966 __ tst(r7, Operand(kStringEncodingMask));
10967 __ b(ne, &string_add_runtime);
10968 // And see if it's ASCII or two-byte.
10969 __ tst(r4, Operand(kStringEncodingMask));
10970 __ b(eq, &non_ascii_string_add_flat_result);
10971
10972 // Both strings are sequential ASCII strings. We also know that they are
10973 // short (since the sum of the lengths is less than kMinNonFlatLength).
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010974 // r6: length of resulting flat string
ager@chromium.org5c838252010-02-19 08:53:10 +000010975 __ AllocateAsciiString(r7, r6, r4, r5, r9, &string_add_runtime);
10976 // Locate first character of result.
10977 __ add(r6, r7, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
10978 // Locate first character of first argument.
10979 __ add(r0, r0, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
10980 // r0: first character of first string.
10981 // r1: second string.
10982 // r2: length of first string.
10983 // r3: length of second string.
10984 // r6: first character of result.
10985 // r7: result string.
ager@chromium.orgac091b72010-05-05 07:34:42 +000010986 StringHelper::GenerateCopyCharacters(masm, r6, r0, r2, r4, true);
ager@chromium.org5c838252010-02-19 08:53:10 +000010987
10988 // Load second argument and locate first character.
10989 __ add(r1, r1, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
10990 // r1: first character of second string.
10991 // r3: length of second string.
10992 // r6: next character of result.
10993 // r7: result string.
ager@chromium.orgac091b72010-05-05 07:34:42 +000010994 StringHelper::GenerateCopyCharacters(masm, r6, r1, r3, r4, true);
ager@chromium.org5c838252010-02-19 08:53:10 +000010995 __ mov(r0, Operand(r7));
10996 __ IncrementCounter(&Counters::string_add_native, 1, r2, r3);
10997 __ add(sp, sp, Operand(2 * kPointerSize));
10998 __ Ret();
10999
11000 __ bind(&non_ascii_string_add_flat_result);
11001 // Both strings are sequential two byte strings.
11002 // r0: first string.
11003 // r1: second string.
11004 // r2: length of first string.
11005 // r3: length of second string.
11006 // r6: sum of length of strings.
11007 __ AllocateTwoByteString(r7, r6, r4, r5, r9, &string_add_runtime);
11008 // r0: first string.
11009 // r1: second string.
11010 // r2: length of first string.
11011 // r3: length of second string.
11012 // r7: result string.
11013
11014 // Locate first character of result.
11015 __ add(r6, r7, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
11016 // Locate first character of first argument.
11017 __ add(r0, r0, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
11018
11019 // r0: first character of first string.
11020 // r1: second string.
11021 // r2: length of first string.
11022 // r3: length of second string.
11023 // r6: first character of result.
11024 // r7: result string.
ager@chromium.orgac091b72010-05-05 07:34:42 +000011025 StringHelper::GenerateCopyCharacters(masm, r6, r0, r2, r4, false);
ager@chromium.org5c838252010-02-19 08:53:10 +000011026
11027 // Locate first character of second argument.
11028 __ add(r1, r1, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
11029
11030 // r1: first character of second string.
11031 // r3: length of second string.
11032 // r6: next character of result (after copy of first string).
11033 // r7: result string.
ager@chromium.orgac091b72010-05-05 07:34:42 +000011034 StringHelper::GenerateCopyCharacters(masm, r6, r1, r3, r4, false);
ager@chromium.org5c838252010-02-19 08:53:10 +000011035
11036 __ mov(r0, Operand(r7));
11037 __ IncrementCounter(&Counters::string_add_native, 1, r2, r3);
11038 __ add(sp, sp, Operand(2 * kPointerSize));
11039 __ Ret();
11040
11041 // Just jump to runtime to add the two strings.
11042 __ bind(&string_add_runtime);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000011043 __ TailCallRuntime(Runtime::kStringAdd, 2, 1);
ager@chromium.org5c838252010-02-19 08:53:10 +000011044}
11045
11046
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000011047#undef __
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011048
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011049} } // namespace v8::internal
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +000011050
11051#endif // V8_TARGET_ARCH_ARM