blob: 92206dd185b6b500ebb6e01aeb4c8f6fdedd6725 [file] [log] [blame]
ager@chromium.org9258b6b2008-09-11 09:11:10 +00001// Copyright 2006-2008 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
30#include "bootstrapper.h"
31#include "codegen-inl.h"
32#include "debug.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000033#include "scopes.h"
34#include "runtime.h"
35
36namespace v8 { namespace internal {
37
kasperl@chromium.orgb9123622008-09-17 14:05:56 +000038// -------------------------------------------------------------------------
39// CodeGenState implementation.
40
ager@chromium.org7c537e22008-10-16 08:43:32 +000041CodeGenState::CodeGenState(CodeGenerator* owner)
kasperl@chromium.orgb9123622008-09-17 14:05:56 +000042 : owner_(owner),
ager@chromium.org7c537e22008-10-16 08:43:32 +000043 typeof_state_(NOT_INSIDE_TYPEOF),
kasperl@chromium.orgb9123622008-09-17 14:05:56 +000044 true_target_(NULL),
45 false_target_(NULL),
46 previous_(NULL) {
47 owner_->set_state(this);
48}
49
50
ager@chromium.org7c537e22008-10-16 08:43:32 +000051CodeGenState::CodeGenState(CodeGenerator* owner,
52 TypeofState typeof_state,
kasperl@chromium.orgb9123622008-09-17 14:05:56 +000053 Label* true_target,
54 Label* false_target)
55 : owner_(owner),
ager@chromium.org7c537e22008-10-16 08:43:32 +000056 typeof_state_(typeof_state),
kasperl@chromium.orgb9123622008-09-17 14:05:56 +000057 true_target_(true_target),
58 false_target_(false_target),
59 previous_(owner->state()) {
60 owner_->set_state(this);
61}
62
63
kasperl@chromium.orgb9123622008-09-17 14:05:56 +000064CodeGenState::~CodeGenState() {
65 ASSERT(owner_->state() == this);
66 owner_->set_state(previous_);
67}
68
69
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000070// -----------------------------------------------------------------------------
ager@chromium.org7c537e22008-10-16 08:43:32 +000071// CodeGenerator implementation
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000072
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000073#define __ masm_->
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000074
ager@chromium.org7c537e22008-10-16 08:43:32 +000075CodeGenerator::CodeGenerator(int buffer_size, Handle<Script> script,
76 bool is_eval)
77 : is_eval_(is_eval),
78 script_(script),
79 deferred_(8),
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000080 masm_(new MacroAssembler(NULL, buffer_size)),
81 scope_(NULL),
82 cc_reg_(al),
83 state_(NULL),
84 break_stack_height_(0) {
85}
86
87
88// Calling conventions:
89
mads.s.ager31e71382008-08-13 09:32:07 +000090// r0: the number of arguments
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000091// fp: frame pointer
92// sp: stack pointer
93// pp: caller's parameter pointer
94// cp: callee's context
95
ager@chromium.org7c537e22008-10-16 08:43:32 +000096void CodeGenerator::GenCode(FunctionLiteral* fun) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000097 Scope* scope = fun->scope();
98 ZoneList<Statement*>* body = fun->body();
99
100 // Initialize state.
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000101 { CodeGenState state(this);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000102 scope_ = scope;
103 cc_reg_ = al;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000104
105 // Entry
106 // stack: function, receiver, arguments, return address
107 // r0: number of arguments
108 // sp: stack pointer
109 // fp: frame pointer
110 // pp: caller's parameter pointer
111 // cp: callee's context
112
113 { Comment cmnt(masm_, "[ enter JS frame");
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000114 EnterJSFrame();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000115 }
116 // tos: code slot
117#ifdef DEBUG
118 if (strlen(FLAG_stop_at) > 0 &&
119 fun->name()->IsEqualTo(CStrVector(FLAG_stop_at))) {
kasper.lund7276f142008-07-30 08:49:36 +0000120 __ stop("stop-at");
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000121 }
122#endif
123
124 // Allocate space for locals and initialize them.
kasper.lund7276f142008-07-30 08:49:36 +0000125 if (scope->num_stack_slots() > 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000126 Comment cmnt(masm_, "[ allocate space for locals");
mads.s.ager31e71382008-08-13 09:32:07 +0000127 // Initialize stack slots with 'undefined' value.
128 __ mov(ip, Operand(Factory::undefined_value()));
129 for (int i = 0; i < scope->num_stack_slots(); i++) {
130 __ push(ip);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000131 }
132 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000133
134 if (scope->num_heap_slots() > 0) {
135 // Allocate local context.
136 // Get outer context and create a new context based on it.
mads.s.ager31e71382008-08-13 09:32:07 +0000137 __ ldr(r0, FunctionOperand());
138 __ push(r0);
kasper.lund7276f142008-07-30 08:49:36 +0000139 __ CallRuntime(Runtime::kNewContext, 1); // r0 holds the result
140
141 if (kDebug) {
142 Label verified_true;
143 __ cmp(r0, Operand(cp));
144 __ b(eq, &verified_true);
145 __ stop("NewContext: r0 is expected to be the same as cp");
146 __ bind(&verified_true);
147 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000148 // Update context local.
149 __ str(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
150 }
151
152 // TODO(1241774): Improve this code!!!
153 // 1) only needed if we have a context
154 // 2) no need to recompute context ptr every single time
155 // 3) don't copy parameter operand code from SlotOperand!
156 {
157 Comment cmnt2(masm_, "[ copy context parameters into .context");
158
159 // Note that iteration order is relevant here! If we have the same
160 // parameter twice (e.g., function (x, y, x)), and that parameter
161 // needs to be copied into the context, it must be the last argument
162 // passed to the parameter that needs to be copied. This is a rare
163 // case so we don't check for it, instead we rely on the copying
164 // order: such a parameter is copied repeatedly into the same
165 // context location and thus the last value is what is seen inside
166 // the function.
167 for (int i = 0; i < scope->num_parameters(); i++) {
168 Variable* par = scope->parameter(i);
169 Slot* slot = par->slot();
170 if (slot != NULL && slot->type() == Slot::CONTEXT) {
171 ASSERT(!scope->is_global_scope()); // no parameters in global scope
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000172 __ ldr(r1, ParameterOperand(i));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000173 // Loads r2 with context; used below in RecordWrite.
174 __ str(r1, SlotOperand(slot, r2));
175 // Load the offset into r3.
176 int slot_offset =
177 FixedArray::kHeaderSize + slot->index() * kPointerSize;
178 __ mov(r3, Operand(slot_offset));
179 __ RecordWrite(r2, r3, r1);
180 }
181 }
182 }
183
184 // Store the arguments object.
185 // This must happen after context initialization because
186 // the arguments array may be stored in the context!
187 if (scope->arguments() != NULL) {
188 ASSERT(scope->arguments_shadow() != NULL);
189 Comment cmnt(masm_, "[ allocate arguments object");
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000190 { Reference shadow_ref(this, scope->arguments_shadow());
191 { Reference arguments_ref(this, scope->arguments());
192 ArgumentsAccessStub stub(ArgumentsAccessStub::NEW_OBJECT);
193 __ ldr(r2, FunctionOperand());
194 // The receiver is below the arguments, the return address,
195 // and the frame pointer on the stack.
196 const int kReceiverDisplacement = 2 + scope->num_parameters();
197 __ add(r1, fp, Operand(kReceiverDisplacement * kPointerSize));
198 __ mov(r0, Operand(Smi::FromInt(scope->num_parameters())));
199 __ stm(db_w, sp, r0.bit() | r1.bit() | r2.bit());
200 __ CallStub(&stub);
201 __ push(r0);
ager@chromium.org7c537e22008-10-16 08:43:32 +0000202 arguments_ref.SetValue(NOT_CONST_INIT);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000203 }
ager@chromium.org7c537e22008-10-16 08:43:32 +0000204 shadow_ref.SetValue(NOT_CONST_INIT);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000205 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000206 __ pop(r0); // Value is no longer needed.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000207 }
208
209 // Generate code to 'execute' declarations and initialize
210 // functions (source elements). In case of an illegal
211 // redeclaration we need to handle that instead of processing the
212 // declarations.
213 if (scope->HasIllegalRedeclaration()) {
214 Comment cmnt(masm_, "[ illegal redeclarations");
215 scope->VisitIllegalRedeclaration(this);
216 } else {
217 Comment cmnt(masm_, "[ declarations");
mads.s.ager31e71382008-08-13 09:32:07 +0000218 // ProcessDeclarations calls DeclareGlobals indirectly
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000219 ProcessDeclarations(scope->declarations());
mads.s.ager31e71382008-08-13 09:32:07 +0000220
v8.team.kasperl727e9952008-09-02 14:56:44 +0000221 // Bail out if a stack-overflow exception occurred when
kasper.lund212ac232008-07-16 07:07:30 +0000222 // processing declarations.
223 if (HasStackOverflow()) return;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000224 }
225
mads.s.ager31e71382008-08-13 09:32:07 +0000226 if (FLAG_trace) {
227 // Push a valid value as the parameter. The runtime call only uses
228 // it as the return value to indicate non-failure.
229 __ mov(r0, Operand(Smi::FromInt(0)));
230 __ push(r0);
231 __ CallRuntime(Runtime::kTraceEnter, 1);
232 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000233 CheckStack();
234
235 // Compile the body of the function in a vanilla state. Don't
236 // bother compiling all the code if the scope has an illegal
237 // redeclaration.
238 if (!scope->HasIllegalRedeclaration()) {
239 Comment cmnt(masm_, "[ function body");
240#ifdef DEBUG
241 bool is_builtin = Bootstrapper::IsActive();
242 bool should_trace =
243 is_builtin ? FLAG_trace_builtin_calls : FLAG_trace_calls;
mads.s.ager31e71382008-08-13 09:32:07 +0000244 if (should_trace) {
245 // Push a valid value as the parameter. The runtime call only uses
246 // it as the return value to indicate non-failure.
247 __ mov(r0, Operand(Smi::FromInt(0)));
248 __ push(r0);
249 __ CallRuntime(Runtime::kDebugTrace, 1);
250 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000251#endif
252 VisitStatements(body);
253 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000254 }
255
256 // exit
257 // r0: result
258 // sp: stack pointer
259 // fp: frame pointer
260 // pp: parameter pointer
261 // cp: callee's context
mads.s.ager31e71382008-08-13 09:32:07 +0000262 __ mov(r0, Operand(Factory::undefined_value()));
263
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000264 __ bind(&function_return_);
mads.s.ager31e71382008-08-13 09:32:07 +0000265 if (FLAG_trace) {
266 // Push the return value on the stack as the parameter.
267 // Runtime::TraceExit returns the parameter as it is.
268 __ push(r0);
269 __ CallRuntime(Runtime::kTraceExit, 1);
270 }
271
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000272 // Tear down the frame which will restore the caller's frame pointer and the
273 // link register.
kasper.lund7276f142008-07-30 08:49:36 +0000274 ExitJSFrame();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000275
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000276 __ add(sp, sp, Operand((scope_->num_parameters() + 1) * kPointerSize));
277 __ mov(pc, lr);
278
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000279 // Code generation state must be reset.
280 scope_ = NULL;
281 ASSERT(!has_cc());
282 ASSERT(state_ == NULL);
283}
284
285
ager@chromium.org7c537e22008-10-16 08:43:32 +0000286MemOperand CodeGenerator::SlotOperand(Slot* slot, Register tmp) {
287 // Currently, this assertion will fail if we try to assign to
288 // a constant variable that is constant because it is read-only
289 // (such as the variable referring to a named function expression).
290 // We need to implement assignments to read-only variables.
291 // Ideally, we should do this during AST generation (by converting
292 // such assignments into expression statements); however, in general
293 // we may not be able to make the decision until past AST generation,
294 // that is when the entire program is known.
295 ASSERT(slot != NULL);
296 int index = slot->index();
297 switch (slot->type()) {
298 case Slot::PARAMETER:
299 return ParameterOperand(index);
300
301 case Slot::LOCAL: {
302 ASSERT(0 <= index && index < scope()->num_stack_slots());
303 const int kLocalOffset = JavaScriptFrameConstants::kLocal0Offset;
304 return MemOperand(fp, kLocalOffset - index * kPointerSize);
305 }
306
307 case Slot::CONTEXT: {
308 // Follow the context chain if necessary.
309 ASSERT(!tmp.is(cp)); // do not overwrite context register
310 Register context = cp;
311 int chain_length = scope()->ContextChainLength(slot->var()->scope());
312 for (int i = chain_length; i-- > 0;) {
313 // Load the closure.
314 // (All contexts, even 'with' contexts, have a closure,
315 // and it is the same for all contexts inside a function.
316 // There is no need to go to the function context first.)
317 __ ldr(tmp, ContextOperand(context, Context::CLOSURE_INDEX));
318 // Load the function context (which is the incoming, outer context).
319 __ ldr(tmp, FieldMemOperand(tmp, JSFunction::kContextOffset));
320 context = tmp;
321 }
322 // We may have a 'with' context now. Get the function context.
323 // (In fact this mov may never be the needed, since the scope analysis
324 // may not permit a direct context access in this case and thus we are
325 // always at a function context. However it is safe to dereference be-
326 // cause the function context of a function context is itself. Before
327 // deleting this mov we should try to create a counter-example first,
328 // though...)
329 __ ldr(tmp, ContextOperand(context, Context::FCONTEXT_INDEX));
330 return ContextOperand(tmp, index);
331 }
332
333 default:
334 UNREACHABLE();
335 return MemOperand(r0, 0);
336 }
337}
338
339
mads.s.ager31e71382008-08-13 09:32:07 +0000340// Loads a value on the stack. If it is a boolean value, the result may have
341// been (partially) translated into branches, or it may have set the condition
342// code register. If force_cc is set, the value is forced to set the condition
343// code register and no value is pushed. If the condition code register was set,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000344// has_cc() is true and cc_reg_ contains the condition to test for 'true'.
ager@chromium.org7c537e22008-10-16 08:43:32 +0000345void CodeGenerator::LoadCondition(Expression* x,
346 TypeofState typeof_state,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000347 Label* true_target,
348 Label* false_target,
349 bool force_cc) {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000350 ASSERT(!has_cc());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000351
ager@chromium.org7c537e22008-10-16 08:43:32 +0000352 { CodeGenState new_state(this, typeof_state, true_target, false_target);
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000353 Visit(x);
354 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000355 if (force_cc && !has_cc()) {
mads.s.ager31e71382008-08-13 09:32:07 +0000356 // Convert the TOS value to a boolean in the condition code register.
357 ToBoolean(true_target, false_target);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000358 }
359 ASSERT(has_cc() || !force_cc);
360}
361
362
ager@chromium.org7c537e22008-10-16 08:43:32 +0000363void CodeGenerator::Load(Expression* x, TypeofState typeof_state) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000364 Label true_target;
365 Label false_target;
ager@chromium.org7c537e22008-10-16 08:43:32 +0000366 LoadCondition(x, typeof_state, &true_target, &false_target, false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000367
368 if (has_cc()) {
369 // convert cc_reg_ into a bool
370 Label loaded, materialize_true;
371 __ b(cc_reg_, &materialize_true);
mads.s.ager31e71382008-08-13 09:32:07 +0000372 __ mov(r0, Operand(Factory::false_value()));
373 __ push(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000374 __ b(&loaded);
375 __ bind(&materialize_true);
mads.s.ager31e71382008-08-13 09:32:07 +0000376 __ mov(r0, Operand(Factory::true_value()));
377 __ push(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000378 __ bind(&loaded);
379 cc_reg_ = al;
380 }
381
382 if (true_target.is_linked() || false_target.is_linked()) {
383 // we have at least one condition value
384 // that has been "translated" into a branch,
385 // thus it needs to be loaded explicitly again
386 Label loaded;
387 __ b(&loaded); // don't lose current TOS
388 bool both = true_target.is_linked() && false_target.is_linked();
389 // reincarnate "true", if necessary
390 if (true_target.is_linked()) {
391 __ bind(&true_target);
mads.s.ager31e71382008-08-13 09:32:07 +0000392 __ mov(r0, Operand(Factory::true_value()));
393 __ push(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000394 }
395 // if both "true" and "false" need to be reincarnated,
396 // jump across code for "false"
397 if (both)
398 __ b(&loaded);
399 // reincarnate "false", if necessary
400 if (false_target.is_linked()) {
401 __ bind(&false_target);
mads.s.ager31e71382008-08-13 09:32:07 +0000402 __ mov(r0, Operand(Factory::false_value()));
403 __ push(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000404 }
405 // everything is loaded at this point
406 __ bind(&loaded);
407 }
408 ASSERT(!has_cc());
409}
410
411
ager@chromium.org7c537e22008-10-16 08:43:32 +0000412void CodeGenerator::LoadGlobal() {
mads.s.ager31e71382008-08-13 09:32:07 +0000413 __ ldr(r0, GlobalObject());
414 __ push(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000415}
416
417
418// TODO(1241834): Get rid of this function in favor of just using Load, now
ager@chromium.org7c537e22008-10-16 08:43:32 +0000419// that we have the INSIDE_TYPEOF typeof state. => Need to handle global
420// variables w/o reference errors elsewhere.
421void CodeGenerator::LoadTypeofExpression(Expression* x) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000422 Variable* variable = x->AsVariableProxy()->AsVariable();
423 if (variable != NULL && !variable->is_this() && variable->is_global()) {
424 // NOTE: This is somewhat nasty. We force the compiler to load
425 // the variable as if through '<global>.<variable>' to make sure we
426 // do not get reference errors.
427 Slot global(variable, Slot::CONTEXT, Context::GLOBAL_INDEX);
428 Literal key(variable->name());
429 // TODO(1241834): Fetch the position from the variable instead of using
430 // no position.
ager@chromium.org236ad962008-09-25 09:45:57 +0000431 Property property(&global, &key, RelocInfo::kNoPosition);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000432 Load(&property);
433 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000434 Load(x, INSIDE_TYPEOF);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000435 }
436}
437
438
ager@chromium.org7c537e22008-10-16 08:43:32 +0000439Reference::Reference(CodeGenerator* cgen, Expression* expression)
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000440 : cgen_(cgen), expression_(expression), type_(ILLEGAL) {
441 cgen->LoadReference(this);
442}
443
444
445Reference::~Reference() {
446 cgen_->UnloadReference(this);
447}
448
449
ager@chromium.org7c537e22008-10-16 08:43:32 +0000450void CodeGenerator::LoadReference(Reference* ref) {
451 Comment cmnt(masm_, "[ LoadReference");
452
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000453 Expression* e = ref->expression();
454 Property* property = e->AsProperty();
455 Variable* var = e->AsVariableProxy()->AsVariable();
456
457 if (property != NULL) {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000458 // The expression is either a property or a variable proxy that rewrites
459 // to a property.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000460 Load(property->obj());
ager@chromium.org7c537e22008-10-16 08:43:32 +0000461 // We use a named reference if the key is a literal symbol, unless it is
462 // a string that can be legally parsed as an integer. This is because
463 // otherwise we will not get into the slow case code that handles [] on
464 // String objects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000465 Literal* literal = property->key()->AsLiteral();
466 uint32_t dummy;
ager@chromium.org7c537e22008-10-16 08:43:32 +0000467 if (literal != NULL &&
468 literal->handle()->IsSymbol() &&
469 !String::cast(*(literal->handle()))->AsArrayIndex(&dummy)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000470 ref->set_type(Reference::NAMED);
471 } else {
472 Load(property->key());
473 ref->set_type(Reference::KEYED);
474 }
475 } else if (var != NULL) {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000476 // The expression is a variable proxy that does not rewrite to a
477 // property. Global variables are treated as named property references.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000478 if (var->is_global()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000479 LoadGlobal();
480 ref->set_type(Reference::NAMED);
481 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000482 ASSERT(var->slot() != NULL);
483 ref->set_type(Reference::SLOT);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000484 }
485 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000486 // Anything else is a runtime error.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000487 Load(e);
488 __ CallRuntime(Runtime::kThrowReferenceError, 1);
489 }
490}
491
492
ager@chromium.org7c537e22008-10-16 08:43:32 +0000493void CodeGenerator::UnloadReference(Reference* ref) {
494 Comment cmnt(masm_, "[ UnloadReference");
495
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000496 int size = ref->size();
497 if (size <= 0) {
498 // Do nothing. No popping is necessary.
499 } else {
mads.s.ager31e71382008-08-13 09:32:07 +0000500 __ pop(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000501 __ add(sp, sp, Operand(size * kPointerSize));
mads.s.ager31e71382008-08-13 09:32:07 +0000502 __ push(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000503 }
504}
505
506
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000507// ECMA-262, section 9.2, page 30: ToBoolean(). Convert the given
508// register to a boolean in the condition code register. The code
509// may jump to 'false_target' in case the register converts to 'false'.
ager@chromium.org7c537e22008-10-16 08:43:32 +0000510void CodeGenerator::ToBoolean(Label* true_target,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000511 Label* false_target) {
mads.s.ager31e71382008-08-13 09:32:07 +0000512 // Note: The generated code snippet does not change stack variables.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000513 // Only the condition code should be set.
mads.s.ager31e71382008-08-13 09:32:07 +0000514 __ pop(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000515
516 // Fast case checks
517
mads.s.ager31e71382008-08-13 09:32:07 +0000518 // Check if the value is 'false'.
519 __ cmp(r0, Operand(Factory::false_value()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000520 __ b(eq, false_target);
521
mads.s.ager31e71382008-08-13 09:32:07 +0000522 // Check if the value is 'true'.
523 __ cmp(r0, Operand(Factory::true_value()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000524 __ b(eq, true_target);
525
mads.s.ager31e71382008-08-13 09:32:07 +0000526 // Check if the value is 'undefined'.
527 __ cmp(r0, Operand(Factory::undefined_value()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000528 __ b(eq, false_target);
529
mads.s.ager31e71382008-08-13 09:32:07 +0000530 // Check if the value is a smi.
531 __ cmp(r0, Operand(Smi::FromInt(0)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000532 __ b(eq, false_target);
mads.s.ager31e71382008-08-13 09:32:07 +0000533 __ tst(r0, Operand(kSmiTagMask));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000534 __ b(eq, true_target);
535
536 // Slow case: call the runtime.
537 __ push(r0);
mads.s.ager31e71382008-08-13 09:32:07 +0000538 __ CallRuntime(Runtime::kToBool, 1);
539
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000540 // Convert result (r0) to condition code
541 __ cmp(r0, Operand(Factory::false_value()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000542
543 cc_reg_ = ne;
544}
545
546
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000547class GetPropertyStub : public CodeStub {
548 public:
549 GetPropertyStub() { }
550
551 private:
552 Major MajorKey() { return GetProperty; }
553 int MinorKey() { return 0; }
554 void Generate(MacroAssembler* masm);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000555};
556
557
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000558class SetPropertyStub : public CodeStub {
559 public:
560 SetPropertyStub() { }
561
562 private:
563 Major MajorKey() { return SetProperty; }
564 int MinorKey() { return 0; }
565 void Generate(MacroAssembler* masm);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000566};
567
568
kasper.lund7276f142008-07-30 08:49:36 +0000569class GenericBinaryOpStub : public CodeStub {
570 public:
571 explicit GenericBinaryOpStub(Token::Value op) : op_(op) { }
572
573 private:
574 Token::Value op_;
575
576 Major MajorKey() { return GenericBinaryOp; }
577 int MinorKey() { return static_cast<int>(op_); }
578 void Generate(MacroAssembler* masm);
579
580 const char* GetName() {
581 switch (op_) {
582 case Token::ADD: return "GenericBinaryOpStub_ADD";
583 case Token::SUB: return "GenericBinaryOpStub_SUB";
584 case Token::MUL: return "GenericBinaryOpStub_MUL";
585 case Token::DIV: return "GenericBinaryOpStub_DIV";
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000586 case Token::BIT_OR: return "GenericBinaryOpStub_BIT_OR";
587 case Token::BIT_AND: return "GenericBinaryOpStub_BIT_AND";
588 case Token::BIT_XOR: return "GenericBinaryOpStub_BIT_XOR";
589 case Token::SAR: return "GenericBinaryOpStub_SAR";
590 case Token::SHL: return "GenericBinaryOpStub_SHL";
591 case Token::SHR: return "GenericBinaryOpStub_SHR";
kasper.lund7276f142008-07-30 08:49:36 +0000592 default: return "GenericBinaryOpStub";
593 }
594 }
595
596#ifdef DEBUG
597 void Print() { PrintF("GenericBinaryOpStub (%s)\n", Token::String(op_)); }
598#endif
599};
600
601
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000602class InvokeBuiltinStub : public CodeStub {
603 public:
604 enum Kind { Inc, Dec, ToNumber };
605 InvokeBuiltinStub(Kind kind, int argc) : kind_(kind), argc_(argc) { }
606
607 private:
608 Kind kind_;
609 int argc_;
610
611 Major MajorKey() { return InvokeBuiltin; }
612 int MinorKey() { return (argc_ << 3) | static_cast<int>(kind_); }
613 void Generate(MacroAssembler* masm);
614
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000615#ifdef DEBUG
616 void Print() {
617 PrintF("InvokeBuiltinStub (kind %d, argc, %d)\n",
618 static_cast<int>(kind_),
619 argc_);
620 }
621#endif
622};
623
624
ager@chromium.org7c537e22008-10-16 08:43:32 +0000625void CodeGenerator::GenericBinaryOperation(Token::Value op) {
mads.s.ager31e71382008-08-13 09:32:07 +0000626 // sp[0] : y
627 // sp[1] : x
628 // result : r0
629
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000630 // Stub is entered with a call: 'return address' is in lr.
631 switch (op) {
632 case Token::ADD: // fall through.
633 case Token::SUB: // fall through.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000634 case Token::MUL:
635 case Token::BIT_OR:
636 case Token::BIT_AND:
637 case Token::BIT_XOR:
638 case Token::SHL:
639 case Token::SHR:
640 case Token::SAR: {
mads.s.ager31e71382008-08-13 09:32:07 +0000641 __ pop(r0); // r0 : y
642 __ pop(r1); // r1 : x
kasper.lund7276f142008-07-30 08:49:36 +0000643 GenericBinaryOpStub stub(op);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000644 __ CallStub(&stub);
645 break;
646 }
647
648 case Token::DIV: {
mads.s.ager31e71382008-08-13 09:32:07 +0000649 __ mov(r0, Operand(1));
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000650 __ InvokeBuiltin(Builtins::DIV, CALL_JS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000651 break;
652 }
653
654 case Token::MOD: {
mads.s.ager31e71382008-08-13 09:32:07 +0000655 __ mov(r0, Operand(1));
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000656 __ InvokeBuiltin(Builtins::MOD, CALL_JS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000657 break;
658 }
659
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000660 case Token::COMMA:
mads.s.ager31e71382008-08-13 09:32:07 +0000661 __ pop(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000662 // simply discard left value
mads.s.ager31e71382008-08-13 09:32:07 +0000663 __ pop();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000664 break;
665
666 default:
667 // Other cases should have been handled before this point.
668 UNREACHABLE();
669 break;
670 }
671}
672
673
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000674class DeferredInlinedSmiOperation: public DeferredCode {
675 public:
676 DeferredInlinedSmiOperation(CodeGenerator* generator, Token::Value op,
677 int value, bool reversed) :
678 DeferredCode(generator), op_(op), value_(value), reversed_(reversed) {
679 set_comment("[ DeferredInlinedSmiOperation");
680 }
681
682 virtual void Generate() {
683 switch (op_) {
684 case Token::ADD: {
685 if (reversed_) {
686 // revert optimistic add
687 __ sub(r0, r0, Operand(Smi::FromInt(value_)));
688 __ mov(r1, Operand(Smi::FromInt(value_))); // x
689 } else {
690 // revert optimistic add
691 __ sub(r1, r0, Operand(Smi::FromInt(value_)));
692 __ mov(r0, Operand(Smi::FromInt(value_)));
693 }
694 break;
695 }
696
697 case Token::SUB: {
698 if (reversed_) {
699 // revert optimistic sub
700 __ rsb(r0, r0, Operand(Smi::FromInt(value_)));
701 __ mov(r1, Operand(Smi::FromInt(value_)));
702 } else {
703 __ add(r1, r0, Operand(Smi::FromInt(value_)));
704 __ mov(r0, Operand(Smi::FromInt(value_)));
705 }
706 break;
707 }
708
709 case Token::BIT_OR:
710 case Token::BIT_XOR:
711 case Token::BIT_AND: {
712 if (reversed_) {
713 __ mov(r1, Operand(Smi::FromInt(value_)));
714 } else {
715 __ mov(r1, Operand(r0));
716 __ mov(r0, Operand(Smi::FromInt(value_)));
717 }
718 break;
719 }
720
721 case Token::SHL:
722 case Token::SHR:
723 case Token::SAR: {
724 if (!reversed_) {
725 __ mov(r1, Operand(r0));
726 __ mov(r0, Operand(Smi::FromInt(value_)));
727 } else {
728 UNREACHABLE(); // should have been handled in SmiOperation
729 }
730 break;
731 }
732
733 default:
734 // other cases should have been handled before this point.
735 UNREACHABLE();
736 break;
737 }
738
739 GenericBinaryOpStub igostub(op_);
740 __ CallStub(&igostub);
741 }
742
743 private:
744 Token::Value op_;
745 int value_;
746 bool reversed_;
747};
748
749
ager@chromium.org7c537e22008-10-16 08:43:32 +0000750void CodeGenerator::SmiOperation(Token::Value op,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000751 Handle<Object> value,
752 bool reversed) {
753 // NOTE: This is an attempt to inline (a bit) more of the code for
754 // some possible smi operations (like + and -) when (at least) one
755 // of the operands is a literal smi. With this optimization, the
756 // performance of the system is increased by ~15%, and the generated
757 // code size is increased by ~1% (measured on a combination of
758 // different benchmarks).
759
mads.s.ager31e71382008-08-13 09:32:07 +0000760 // sp[0] : operand
761
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000762 int int_value = Smi::cast(*value)->value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000763
764 Label exit;
mads.s.ager31e71382008-08-13 09:32:07 +0000765 __ pop(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000766
767 switch (op) {
768 case Token::ADD: {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000769 DeferredCode* deferred =
770 new DeferredInlinedSmiOperation(this, op, int_value, reversed);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000771
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000772 __ add(r0, r0, Operand(value), SetCC);
773 __ b(vs, deferred->enter());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000774 __ tst(r0, Operand(kSmiTagMask));
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000775 __ b(ne, deferred->enter());
776 __ bind(deferred->exit());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000777 break;
778 }
779
780 case Token::SUB: {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000781 DeferredCode* deferred =
782 new DeferredInlinedSmiOperation(this, op, int_value, reversed);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000783
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000784 if (!reversed) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000785 __ sub(r0, r0, Operand(value), SetCC);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000786 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000787 __ rsb(r0, r0, Operand(value), SetCC);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000788 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000789 __ b(vs, deferred->enter());
790 __ tst(r0, Operand(kSmiTagMask));
791 __ b(ne, deferred->enter());
792 __ bind(deferred->exit());
793 break;
794 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000795
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000796 case Token::BIT_OR:
797 case Token::BIT_XOR:
798 case Token::BIT_AND: {
799 DeferredCode* deferred =
800 new DeferredInlinedSmiOperation(this, op, int_value, reversed);
801 __ tst(r0, Operand(kSmiTagMask));
802 __ b(ne, deferred->enter());
803 switch (op) {
804 case Token::BIT_OR: __ orr(r0, r0, Operand(value)); break;
805 case Token::BIT_XOR: __ eor(r0, r0, Operand(value)); break;
806 case Token::BIT_AND: __ and_(r0, r0, Operand(value)); break;
807 default: UNREACHABLE();
808 }
809 __ bind(deferred->exit());
810 break;
811 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000812
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000813 case Token::SHL:
814 case Token::SHR:
815 case Token::SAR: {
816 if (reversed) {
817 __ mov(ip, Operand(value));
818 __ push(ip);
819 __ push(r0);
820 GenericBinaryOperation(op);
821
822 } else {
823 int shift_value = int_value & 0x1f; // least significant 5 bits
824 DeferredCode* deferred =
825 new DeferredInlinedSmiOperation(this, op, shift_value, false);
826 __ tst(r0, Operand(kSmiTagMask));
827 __ b(ne, deferred->enter());
828 __ mov(r2, Operand(r0, ASR, kSmiTagSize)); // remove tags
829 switch (op) {
830 case Token::SHL: {
831 __ mov(r2, Operand(r2, LSL, shift_value));
832 // check that the *unsigned* result fits in a smi
833 __ add(r3, r2, Operand(0x40000000), SetCC);
834 __ b(mi, deferred->enter());
835 break;
836 }
837 case Token::SHR: {
838 // LSR by immediate 0 means shifting 32 bits.
839 if (shift_value != 0) {
840 __ mov(r2, Operand(r2, LSR, shift_value));
841 }
842 // check that the *unsigned* result fits in a smi
843 // neither of the two high-order bits can be set:
844 // - 0x80000000: high bit would be lost when smi tagging
845 // - 0x40000000: this number would convert to negative when
846 // smi tagging these two cases can only happen with shifts
847 // by 0 or 1 when handed a valid smi
848 __ and_(r3, r2, Operand(0xc0000000), SetCC);
849 __ b(ne, deferred->enter());
850 break;
851 }
852 case Token::SAR: {
853 if (shift_value != 0) {
854 // ASR by immediate 0 means shifting 32 bits.
855 __ mov(r2, Operand(r2, ASR, shift_value));
856 }
857 break;
858 }
859 default: UNREACHABLE();
860 }
861 __ mov(r0, Operand(r2, LSL, kSmiTagSize));
862 __ bind(deferred->exit());
863 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000864 break;
865 }
866
867 default:
868 if (!reversed) {
mads.s.ager31e71382008-08-13 09:32:07 +0000869 __ push(r0);
870 __ mov(r0, Operand(value));
871 __ push(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000872 } else {
873 __ mov(ip, Operand(value));
874 __ push(ip);
mads.s.ager31e71382008-08-13 09:32:07 +0000875 __ push(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000876 }
kasper.lund7276f142008-07-30 08:49:36 +0000877 GenericBinaryOperation(op);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000878 break;
879 }
880
881 __ bind(&exit);
882}
883
884
ager@chromium.org7c537e22008-10-16 08:43:32 +0000885void CodeGenerator::Comparison(Condition cc, bool strict) {
mads.s.ager31e71382008-08-13 09:32:07 +0000886 // sp[0] : y
887 // sp[1] : x
888 // result : cc register
889
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000890 // Strict only makes sense for equality comparisons.
891 ASSERT(!strict || cc == eq);
892
893 Label exit, smi;
mads.s.ager@gmail.com769cc962008-08-06 10:02:49 +0000894 // Implement '>' and '<=' by reversal to obtain ECMA-262 conversion order.
895 if (cc == gt || cc == le) {
896 cc = ReverseCondition(cc);
mads.s.ager31e71382008-08-13 09:32:07 +0000897 __ pop(r1);
mads.s.ager@gmail.com769cc962008-08-06 10:02:49 +0000898 __ pop(r0);
899 } else {
mads.s.ager31e71382008-08-13 09:32:07 +0000900 __ pop(r0);
mads.s.ager@gmail.com769cc962008-08-06 10:02:49 +0000901 __ pop(r1);
902 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000903 __ orr(r2, r0, Operand(r1));
904 __ tst(r2, Operand(kSmiTagMask));
905 __ b(eq, &smi);
906
907 // Perform non-smi comparison by runtime call.
908 __ push(r1);
909
910 // Figure out which native to call and setup the arguments.
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000911 Builtins::JavaScript native;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000912 int argc;
913 if (cc == eq) {
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000914 native = strict ? Builtins::STRICT_EQUALS : Builtins::EQUALS;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000915 argc = 1;
916 } else {
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000917 native = Builtins::COMPARE;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000918 int ncr; // NaN compare result
919 if (cc == lt || cc == le) {
920 ncr = GREATER;
921 } else {
922 ASSERT(cc == gt || cc == ge); // remaining cases
923 ncr = LESS;
924 }
mads.s.ager31e71382008-08-13 09:32:07 +0000925 __ push(r0);
926 __ mov(r0, Operand(Smi::FromInt(ncr)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000927 argc = 2;
928 }
929
930 // Call the native; it returns -1 (less), 0 (equal), or 1 (greater)
931 // tagged as a small integer.
mads.s.ager31e71382008-08-13 09:32:07 +0000932 __ push(r0);
933 __ mov(r0, Operand(argc));
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000934 __ InvokeBuiltin(native, CALL_JS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000935 __ cmp(r0, Operand(0));
936 __ b(&exit);
937
938 // test smi equality by pointer comparison.
939 __ bind(&smi);
940 __ cmp(r1, Operand(r0));
941
942 __ bind(&exit);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000943 cc_reg_ = cc;
944}
945
946
kasper.lund7276f142008-07-30 08:49:36 +0000947class CallFunctionStub: public CodeStub {
948 public:
949 explicit CallFunctionStub(int argc) : argc_(argc) {}
950
951 void Generate(MacroAssembler* masm);
952
953 private:
954 int argc_;
955
kasper.lund7276f142008-07-30 08:49:36 +0000956#if defined(DEBUG)
957 void Print() { PrintF("CallFunctionStub (argc %d)\n", argc_); }
958#endif // defined(DEBUG)
959
960 Major MajorKey() { return CallFunction; }
961 int MinorKey() { return argc_; }
962};
963
964
mads.s.ager31e71382008-08-13 09:32:07 +0000965// Call the function on the stack with the given arguments.
ager@chromium.org7c537e22008-10-16 08:43:32 +0000966void CodeGenerator::CallWithArguments(ZoneList<Expression*>* args,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000967 int position) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000968 // Push the arguments ("left-to-right") on the stack.
mads.s.ager31e71382008-08-13 09:32:07 +0000969 for (int i = 0; i < args->length(); i++) {
970 Load(args->at(i));
971 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000972
kasper.lund7276f142008-07-30 08:49:36 +0000973 // Record the position for debugging purposes.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000974 __ RecordPosition(position);
975
kasper.lund7276f142008-07-30 08:49:36 +0000976 // Use the shared code stub to call the function.
977 CallFunctionStub call_function(args->length());
978 __ CallStub(&call_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000979
980 // Restore context and pop function from the stack.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000981 __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
mads.s.ager31e71382008-08-13 09:32:07 +0000982 __ pop(); // discard the TOS
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000983}
984
985
ager@chromium.org7c537e22008-10-16 08:43:32 +0000986void CodeGenerator::Branch(bool if_true, Label* L) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000987 ASSERT(has_cc());
988 Condition cc = if_true ? cc_reg_ : NegateCondition(cc_reg_);
989 __ b(cc, L);
990 cc_reg_ = al;
991}
992
993
ager@chromium.org7c537e22008-10-16 08:43:32 +0000994void CodeGenerator::CheckStack() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000995 if (FLAG_check_stack) {
996 Comment cmnt(masm_, "[ check stack");
997 StackCheckStub stub;
998 __ CallStub(&stub);
999 }
1000}
1001
1002
ager@chromium.org7c537e22008-10-16 08:43:32 +00001003void CodeGenerator::VisitBlock(Block* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001004 Comment cmnt(masm_, "[ Block");
1005 if (FLAG_debug_info) RecordStatementPosition(node);
1006 node->set_break_stack_height(break_stack_height_);
1007 VisitStatements(node->statements());
1008 __ bind(node->break_target());
1009}
1010
1011
ager@chromium.org7c537e22008-10-16 08:43:32 +00001012void CodeGenerator::DeclareGlobals(Handle<FixedArray> pairs) {
mads.s.ager31e71382008-08-13 09:32:07 +00001013 __ mov(r0, Operand(pairs));
1014 __ push(r0);
1015 __ push(cp);
1016 __ mov(r0, Operand(Smi::FromInt(is_eval() ? 1 : 0)));
1017 __ push(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001018 __ CallRuntime(Runtime::kDeclareGlobals, 3);
mads.s.ager31e71382008-08-13 09:32:07 +00001019 // The result is discarded.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001020}
1021
1022
ager@chromium.org7c537e22008-10-16 08:43:32 +00001023void CodeGenerator::VisitDeclaration(Declaration* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001024 Comment cmnt(masm_, "[ Declaration");
1025 Variable* var = node->proxy()->var();
1026 ASSERT(var != NULL); // must have been resolved
1027 Slot* slot = var->slot();
1028
1029 // If it was not possible to allocate the variable at compile time,
1030 // we need to "declare" it at runtime to make sure it actually
1031 // exists in the local context.
1032 if (slot != NULL && slot->type() == Slot::LOOKUP) {
1033 // Variables with a "LOOKUP" slot were introduced as non-locals
1034 // during variable resolution and must have mode DYNAMIC.
1035 ASSERT(var->mode() == Variable::DYNAMIC);
1036 // For now, just do a runtime call.
mads.s.ager31e71382008-08-13 09:32:07 +00001037 __ push(cp);
1038 __ mov(r0, Operand(var->name()));
1039 __ push(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001040 // Declaration nodes are always declared in only two modes.
1041 ASSERT(node->mode() == Variable::VAR || node->mode() == Variable::CONST);
1042 PropertyAttributes attr = node->mode() == Variable::VAR ? NONE : READ_ONLY;
mads.s.ager31e71382008-08-13 09:32:07 +00001043 __ mov(r0, Operand(Smi::FromInt(attr)));
1044 __ push(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001045 // Push initial value, if any.
1046 // Note: For variables we must not push an initial value (such as
1047 // 'undefined') because we may have a (legal) redeclaration and we
1048 // must not destroy the current value.
1049 if (node->mode() == Variable::CONST) {
mads.s.ager31e71382008-08-13 09:32:07 +00001050 __ mov(r0, Operand(Factory::the_hole_value()));
1051 __ push(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001052 } else if (node->fun() != NULL) {
1053 Load(node->fun());
1054 } else {
mads.s.ager31e71382008-08-13 09:32:07 +00001055 __ mov(r0, Operand(0)); // no initial value!
1056 __ push(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001057 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001058 __ CallRuntime(Runtime::kDeclareContextSlot, 4);
1059 // Ignore the return value (declarations are statements).
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001060 return;
1061 }
1062
1063 ASSERT(!var->is_global());
1064
1065 // If we have a function or a constant, we need to initialize the variable.
1066 Expression* val = NULL;
1067 if (node->mode() == Variable::CONST) {
1068 val = new Literal(Factory::the_hole_value());
1069 } else {
1070 val = node->fun(); // NULL if we don't have a function
1071 }
1072
1073 if (val != NULL) {
1074 // Set initial value.
1075 Reference target(this, node->proxy());
ager@chromium.org7c537e22008-10-16 08:43:32 +00001076 ASSERT(target.is_slot());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001077 Load(val);
ager@chromium.org7c537e22008-10-16 08:43:32 +00001078 target.SetValue(NOT_CONST_INIT);
1079 // Get rid of the assigned value (declarations are statements). It's
1080 // safe to pop the value lying on top of the reference before unloading
1081 // the reference itself (which preserves the top of stack) because we
1082 // know it is a zero-sized reference.
mads.s.ager31e71382008-08-13 09:32:07 +00001083 __ pop();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001084 }
1085}
1086
1087
ager@chromium.org7c537e22008-10-16 08:43:32 +00001088void CodeGenerator::VisitExpressionStatement(ExpressionStatement* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001089 Comment cmnt(masm_, "[ ExpressionStatement");
1090 if (FLAG_debug_info) RecordStatementPosition(node);
1091 Expression* expression = node->expression();
1092 expression->MarkAsStatement();
1093 Load(expression);
mads.s.ager31e71382008-08-13 09:32:07 +00001094 __ pop();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001095}
1096
1097
ager@chromium.org7c537e22008-10-16 08:43:32 +00001098void CodeGenerator::VisitEmptyStatement(EmptyStatement* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001099 Comment cmnt(masm_, "// EmptyStatement");
1100 // nothing to do
1101}
1102
1103
ager@chromium.org7c537e22008-10-16 08:43:32 +00001104void CodeGenerator::VisitIfStatement(IfStatement* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001105 Comment cmnt(masm_, "[ IfStatement");
1106 // Generate different code depending on which
1107 // parts of the if statement are present or not.
1108 bool has_then_stm = node->HasThenStatement();
1109 bool has_else_stm = node->HasElseStatement();
1110
1111 if (FLAG_debug_info) RecordStatementPosition(node);
1112
1113 Label exit;
1114 if (has_then_stm && has_else_stm) {
mads.s.ager31e71382008-08-13 09:32:07 +00001115 Comment cmnt(masm_, "[ IfThenElse");
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001116 Label then;
1117 Label else_;
1118 // if (cond)
ager@chromium.org7c537e22008-10-16 08:43:32 +00001119 LoadCondition(node->condition(), NOT_INSIDE_TYPEOF, &then, &else_, true);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001120 Branch(false, &else_);
1121 // then
1122 __ bind(&then);
1123 Visit(node->then_statement());
1124 __ b(&exit);
1125 // else
1126 __ bind(&else_);
1127 Visit(node->else_statement());
1128
1129 } else if (has_then_stm) {
mads.s.ager31e71382008-08-13 09:32:07 +00001130 Comment cmnt(masm_, "[ IfThen");
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001131 ASSERT(!has_else_stm);
1132 Label then;
1133 // if (cond)
ager@chromium.org7c537e22008-10-16 08:43:32 +00001134 LoadCondition(node->condition(), NOT_INSIDE_TYPEOF, &then, &exit, true);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001135 Branch(false, &exit);
1136 // then
1137 __ bind(&then);
1138 Visit(node->then_statement());
1139
1140 } else if (has_else_stm) {
mads.s.ager31e71382008-08-13 09:32:07 +00001141 Comment cmnt(masm_, "[ IfElse");
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001142 ASSERT(!has_then_stm);
1143 Label else_;
1144 // if (!cond)
ager@chromium.org7c537e22008-10-16 08:43:32 +00001145 LoadCondition(node->condition(), NOT_INSIDE_TYPEOF, &exit, &else_, true);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001146 Branch(true, &exit);
1147 // else
1148 __ bind(&else_);
1149 Visit(node->else_statement());
1150
1151 } else {
mads.s.ager31e71382008-08-13 09:32:07 +00001152 Comment cmnt(masm_, "[ If");
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001153 ASSERT(!has_then_stm && !has_else_stm);
1154 // if (cond)
ager@chromium.org7c537e22008-10-16 08:43:32 +00001155 LoadCondition(node->condition(), NOT_INSIDE_TYPEOF, &exit, &exit, false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001156 if (has_cc()) {
1157 cc_reg_ = al;
1158 } else {
1159 __ pop(r0); // __ Pop(no_reg)
1160 }
1161 }
1162
1163 // end
1164 __ bind(&exit);
1165}
1166
1167
ager@chromium.org7c537e22008-10-16 08:43:32 +00001168void CodeGenerator::CleanStack(int num_bytes) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001169 ASSERT(num_bytes >= 0);
1170 if (num_bytes > 0) {
mads.s.ager31e71382008-08-13 09:32:07 +00001171 __ add(sp, sp, Operand(num_bytes));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001172 }
1173}
1174
1175
ager@chromium.org7c537e22008-10-16 08:43:32 +00001176void CodeGenerator::VisitContinueStatement(ContinueStatement* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001177 Comment cmnt(masm_, "[ ContinueStatement");
1178 if (FLAG_debug_info) RecordStatementPosition(node);
1179 CleanStack(break_stack_height_ - node->target()->break_stack_height());
1180 __ b(node->target()->continue_target());
1181}
1182
1183
ager@chromium.org7c537e22008-10-16 08:43:32 +00001184void CodeGenerator::VisitBreakStatement(BreakStatement* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001185 Comment cmnt(masm_, "[ BreakStatement");
1186 if (FLAG_debug_info) RecordStatementPosition(node);
1187 CleanStack(break_stack_height_ - node->target()->break_stack_height());
1188 __ b(node->target()->break_target());
1189}
1190
1191
ager@chromium.org7c537e22008-10-16 08:43:32 +00001192void CodeGenerator::VisitReturnStatement(ReturnStatement* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001193 Comment cmnt(masm_, "[ ReturnStatement");
1194 if (FLAG_debug_info) RecordStatementPosition(node);
1195 Load(node->expression());
mads.s.ager31e71382008-08-13 09:32:07 +00001196 // Move the function result into r0.
1197 __ pop(r0);
1198
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001199 __ b(&function_return_);
1200}
1201
1202
ager@chromium.org7c537e22008-10-16 08:43:32 +00001203void CodeGenerator::VisitWithEnterStatement(WithEnterStatement* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001204 Comment cmnt(masm_, "[ WithEnterStatement");
1205 if (FLAG_debug_info) RecordStatementPosition(node);
1206 Load(node->expression());
kasper.lund7276f142008-07-30 08:49:36 +00001207 __ CallRuntime(Runtime::kPushContext, 1);
1208 if (kDebug) {
1209 Label verified_true;
1210 __ cmp(r0, Operand(cp));
1211 __ b(eq, &verified_true);
1212 __ stop("PushContext: r0 is expected to be the same as cp");
1213 __ bind(&verified_true);
1214 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001215 // Update context local.
1216 __ str(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
1217}
1218
1219
ager@chromium.org7c537e22008-10-16 08:43:32 +00001220void CodeGenerator::VisitWithExitStatement(WithExitStatement* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001221 Comment cmnt(masm_, "[ WithExitStatement");
1222 // Pop context.
1223 __ ldr(cp, ContextOperand(cp, Context::PREVIOUS_INDEX));
1224 // Update context local.
1225 __ str(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
1226}
1227
1228
ager@chromium.org7c537e22008-10-16 08:43:32 +00001229int CodeGenerator::FastCaseSwitchMaxOverheadFactor() {
1230 return kFastSwitchMaxOverheadFactor;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001231}
1232
ager@chromium.org7c537e22008-10-16 08:43:32 +00001233int CodeGenerator::FastCaseSwitchMinCaseCount() {
1234 return kFastSwitchMinCaseCount;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001235}
1236
1237
ager@chromium.org7c537e22008-10-16 08:43:32 +00001238void CodeGenerator::GenerateFastCaseSwitchJumpTable(
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001239 SwitchStatement* node, int min_index, int range, Label *fail_label,
1240 SmartPointer<Label*> &case_targets, SmartPointer<Label> &case_labels) {
1241
1242 ASSERT(kSmiTag == 0 && kSmiTagSize <= 2);
1243
1244 __ pop(r0);
1245 if (min_index != 0) {
1246 // small positive numbers can be immediate operands.
1247 if (min_index < 0) {
1248 __ add(r0, r0, Operand(Smi::FromInt(-min_index)));
1249 } else {
1250 __ sub(r0, r0, Operand(Smi::FromInt(min_index)));
1251 }
1252 }
1253 __ tst(r0, Operand(0x80000000 | kSmiTagMask));
1254 __ b(ne, fail_label);
1255 __ cmp(r0, Operand(Smi::FromInt(range)));
1256 __ b(ge, fail_label);
1257 __ add(pc, pc, Operand(r0, LSL, 2 - kSmiTagSize));
1258 // One extra instruction offsets the table, so the table's start address is
1259 // the pc-register at the above add.
1260 __ stop("Unreachable: Switch table alignment");
1261
1262 // table containing branch operations.
1263 for (int i = 0; i < range; i++) {
1264 __ b(case_targets[i]);
1265 }
1266
1267 GenerateFastCaseSwitchCases(node, case_labels);
1268}
1269
1270
ager@chromium.org7c537e22008-10-16 08:43:32 +00001271void CodeGenerator::VisitSwitchStatement(SwitchStatement* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001272 Comment cmnt(masm_, "[ SwitchStatement");
1273 if (FLAG_debug_info) RecordStatementPosition(node);
1274 node->set_break_stack_height(break_stack_height_);
1275
1276 Load(node->tag());
1277
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001278 if (TryGenerateFastCaseSwitchStatement(node)) {
1279 return;
1280 }
1281
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001282 Label next, fall_through, default_case;
1283 ZoneList<CaseClause*>* cases = node->cases();
1284 int length = cases->length();
1285
1286 for (int i = 0; i < length; i++) {
1287 CaseClause* clause = cases->at(i);
1288
1289 Comment cmnt(masm_, "[ case clause");
1290
1291 if (clause->is_default()) {
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00001292 // Continue matching cases. The program will execute the default case's
1293 // statements if it does not match any of the cases.
1294 __ b(&next);
1295
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001296 // Bind the default case label, so we can branch to it when we
1297 // have compared against all other cases.
1298 ASSERT(default_case.is_unused()); // at most one default clause
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00001299 __ bind(&default_case);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001300 } else {
1301 __ bind(&next);
1302 next.Unuse();
mads.s.ager31e71382008-08-13 09:32:07 +00001303 __ ldr(r0, MemOperand(sp, 0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001304 __ push(r0); // duplicate TOS
1305 Load(clause->label());
1306 Comparison(eq, true);
1307 Branch(false, &next);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001308 }
1309
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00001310 // Entering the case statement for the first time. Remove the switch value
1311 // from the stack.
1312 __ pop(r0);
1313
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001314 // Generate code for the body.
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00001315 // This is also the target for the fall through from the previous case's
1316 // statements which has to skip over the matching code and the popping of
1317 // the switch value.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001318 __ bind(&fall_through);
1319 fall_through.Unuse();
1320 VisitStatements(clause->statements());
1321 __ b(&fall_through);
1322 }
1323
1324 __ bind(&next);
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00001325 // Reached the end of the case statements without matching any of the cases.
1326 if (default_case.is_bound()) {
1327 // A default case exists -> execute its statements.
1328 __ b(&default_case);
1329 } else {
1330 // Remove the switch value from the stack.
1331 __ pop(r0);
1332 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001333
1334 __ bind(&fall_through);
1335 __ bind(node->break_target());
1336}
1337
1338
ager@chromium.org7c537e22008-10-16 08:43:32 +00001339void CodeGenerator::VisitLoopStatement(LoopStatement* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001340 Comment cmnt(masm_, "[ LoopStatement");
1341 if (FLAG_debug_info) RecordStatementPosition(node);
1342 node->set_break_stack_height(break_stack_height_);
1343
1344 // simple condition analysis
1345 enum { ALWAYS_TRUE, ALWAYS_FALSE, DONT_KNOW } info = DONT_KNOW;
1346 if (node->cond() == NULL) {
1347 ASSERT(node->type() == LoopStatement::FOR_LOOP);
1348 info = ALWAYS_TRUE;
1349 } else {
1350 Literal* lit = node->cond()->AsLiteral();
1351 if (lit != NULL) {
1352 if (lit->IsTrue()) {
1353 info = ALWAYS_TRUE;
1354 } else if (lit->IsFalse()) {
1355 info = ALWAYS_FALSE;
1356 }
1357 }
1358 }
1359
1360 Label loop, entry;
1361
1362 // init
1363 if (node->init() != NULL) {
1364 ASSERT(node->type() == LoopStatement::FOR_LOOP);
1365 Visit(node->init());
1366 }
1367 if (node->type() != LoopStatement::DO_LOOP && info != ALWAYS_TRUE) {
1368 __ b(&entry);
1369 }
1370
1371 // body
1372 __ bind(&loop);
1373 Visit(node->body());
1374
1375 // next
1376 __ bind(node->continue_target());
1377 if (node->next() != NULL) {
1378 // Record source position of the statement as this code which is after the
1379 // code for the body actually belongs to the loop statement and not the
1380 // body.
1381 if (FLAG_debug_info) __ RecordPosition(node->statement_pos());
1382 ASSERT(node->type() == LoopStatement::FOR_LOOP);
1383 Visit(node->next());
1384 }
1385
1386 // cond
1387 __ bind(&entry);
1388 switch (info) {
1389 case ALWAYS_TRUE:
1390 CheckStack(); // TODO(1222600): ignore if body contains calls.
1391 __ b(&loop);
1392 break;
1393 case ALWAYS_FALSE:
1394 break;
1395 case DONT_KNOW:
1396 CheckStack(); // TODO(1222600): ignore if body contains calls.
1397 LoadCondition(node->cond(),
ager@chromium.org7c537e22008-10-16 08:43:32 +00001398 NOT_INSIDE_TYPEOF,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001399 &loop,
1400 node->break_target(),
1401 true);
1402 Branch(true, &loop);
1403 break;
1404 }
1405
1406 // exit
1407 __ bind(node->break_target());
1408}
1409
1410
ager@chromium.org7c537e22008-10-16 08:43:32 +00001411void CodeGenerator::VisitForInStatement(ForInStatement* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001412 Comment cmnt(masm_, "[ ForInStatement");
1413 if (FLAG_debug_info) RecordStatementPosition(node);
1414
1415 // We keep stuff on the stack while the body is executing.
1416 // Record it, so that a break/continue crossing this statement
1417 // can restore the stack.
1418 const int kForInStackSize = 5 * kPointerSize;
1419 break_stack_height_ += kForInStackSize;
1420 node->set_break_stack_height(break_stack_height_);
1421
1422 Label loop, next, entry, cleanup, exit, primitive, jsobject;
1423 Label filter_key, end_del_check, fixed_array, non_string;
1424
1425 // Get the object to enumerate over (converted to JSObject).
1426 Load(node->enumerable());
mads.s.ager31e71382008-08-13 09:32:07 +00001427 __ pop(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001428
1429 // Both SpiderMonkey and kjs ignore null and undefined in contrast
1430 // to the specification. 12.6.4 mandates a call to ToObject.
1431 __ cmp(r0, Operand(Factory::undefined_value()));
1432 __ b(eq, &exit);
1433 __ cmp(r0, Operand(Factory::null_value()));
1434 __ b(eq, &exit);
1435
1436 // Stack layout in body:
1437 // [iteration counter (Smi)]
1438 // [length of array]
1439 // [FixedArray]
1440 // [Map or 0]
1441 // [Object]
1442
1443 // Check if enumerable is already a JSObject
1444 __ tst(r0, Operand(kSmiTagMask));
1445 __ b(eq, &primitive);
1446 __ ldr(r1, FieldMemOperand(r0, HeapObject::kMapOffset));
1447 __ ldrb(r1, FieldMemOperand(r1, Map::kInstanceTypeOffset));
ager@chromium.orgc27e4e72008-09-04 13:52:27 +00001448 __ cmp(r1, Operand(FIRST_JS_OBJECT_TYPE));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001449 __ b(hs, &jsobject);
1450
1451 __ bind(&primitive);
mads.s.ager31e71382008-08-13 09:32:07 +00001452 __ push(r0);
1453 __ mov(r0, Operand(0));
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00001454 __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_JS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001455
1456
1457 __ bind(&jsobject);
1458
1459 // Get the set of properties (as a FixedArray or Map).
1460 __ push(r0); // duplicate the object being enumerated
mads.s.ager31e71382008-08-13 09:32:07 +00001461 __ push(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001462 __ CallRuntime(Runtime::kGetPropertyNamesFast, 1);
1463
1464 // If we got a Map, we can do a fast modification check.
1465 // Otherwise, we got a FixedArray, and we have to do a slow check.
1466 __ mov(r2, Operand(r0));
1467 __ ldr(r1, FieldMemOperand(r2, HeapObject::kMapOffset));
1468 __ cmp(r1, Operand(Factory::meta_map()));
1469 __ b(ne, &fixed_array);
1470
1471 // Get enum cache
1472 __ mov(r1, Operand(r0));
1473 __ ldr(r1, FieldMemOperand(r1, Map::kInstanceDescriptorsOffset));
1474 __ ldr(r1, FieldMemOperand(r1, DescriptorArray::kEnumerationIndexOffset));
1475 __ ldr(r2,
1476 FieldMemOperand(r1, DescriptorArray::kEnumCacheBridgeCacheOffset));
1477
mads.s.ager31e71382008-08-13 09:32:07 +00001478 __ push(r0); // map
1479 __ push(r2); // enum cache bridge cache
1480 __ ldr(r0, FieldMemOperand(r2, FixedArray::kLengthOffset));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001481 __ mov(r0, Operand(r0, LSL, kSmiTagSize));
mads.s.ager31e71382008-08-13 09:32:07 +00001482 __ push(r0);
1483 __ mov(r0, Operand(Smi::FromInt(0)));
1484 __ push(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001485 __ b(&entry);
1486
1487
1488 __ bind(&fixed_array);
1489
1490 __ mov(r1, Operand(Smi::FromInt(0)));
1491 __ push(r1); // insert 0 in place of Map
mads.s.ager31e71382008-08-13 09:32:07 +00001492 __ push(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001493
1494 // Push the length of the array and the initial index onto the stack.
mads.s.ager31e71382008-08-13 09:32:07 +00001495 __ ldr(r0, FieldMemOperand(r0, FixedArray::kLengthOffset));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001496 __ mov(r0, Operand(r0, LSL, kSmiTagSize));
mads.s.ager31e71382008-08-13 09:32:07 +00001497 __ push(r0);
1498 __ mov(r0, Operand(Smi::FromInt(0))); // init index
1499 __ push(r0);
1500
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001501 __ b(&entry);
1502
1503 // Body.
1504 __ bind(&loop);
1505 Visit(node->body());
1506
1507 // Next.
1508 __ bind(node->continue_target());
1509 __ bind(&next);
mads.s.ager31e71382008-08-13 09:32:07 +00001510 __ pop(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001511 __ add(r0, r0, Operand(Smi::FromInt(1)));
mads.s.ager31e71382008-08-13 09:32:07 +00001512 __ push(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001513
1514 // Condition.
1515 __ bind(&entry);
1516
mads.s.ager31e71382008-08-13 09:32:07 +00001517 // sp[0] : index
1518 // sp[1] : array/enum cache length
1519 // sp[2] : array or enum cache
1520 // sp[3] : 0 or map
1521 // sp[4] : enumerable
1522 __ ldr(r0, MemOperand(sp, 0 * kPointerSize)); // load the current count
1523 __ ldr(r1, MemOperand(sp, 1 * kPointerSize)); // load the length
1524 __ cmp(r0, Operand(r1)); // compare to the array length
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001525 __ b(hs, &cleanup);
1526
mads.s.ager31e71382008-08-13 09:32:07 +00001527 __ ldr(r0, MemOperand(sp, 0 * kPointerSize));
1528
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001529 // Get the i'th entry of the array.
mads.s.ager31e71382008-08-13 09:32:07 +00001530 __ ldr(r2, MemOperand(sp, 2 * kPointerSize));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001531 __ add(r2, r2, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
1532 __ ldr(r3, MemOperand(r2, r0, LSL, kPointerSizeLog2 - kSmiTagSize));
1533
1534 // Get Map or 0.
mads.s.ager31e71382008-08-13 09:32:07 +00001535 __ ldr(r2, MemOperand(sp, 3 * kPointerSize));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001536 // Check if this (still) matches the map of the enumerable.
1537 // If not, we have to filter the key.
mads.s.ager31e71382008-08-13 09:32:07 +00001538 __ ldr(r1, MemOperand(sp, 4 * kPointerSize));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001539 __ ldr(r1, FieldMemOperand(r1, HeapObject::kMapOffset));
1540 __ cmp(r1, Operand(r2));
1541 __ b(eq, &end_del_check);
1542
1543 // Convert the entry to a string (or null if it isn't a property anymore).
mads.s.ager31e71382008-08-13 09:32:07 +00001544 __ ldr(r0, MemOperand(sp, 4 * kPointerSize)); // push enumerable
1545 __ push(r0);
1546 __ push(r3); // push entry
1547 __ mov(r0, Operand(1));
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00001548 __ InvokeBuiltin(Builtins::FILTER_KEY, CALL_JS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001549 __ mov(r3, Operand(r0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001550
1551 // If the property has been removed while iterating, we just skip it.
1552 __ cmp(r3, Operand(Factory::null_value()));
1553 __ b(eq, &next);
1554
1555
1556 __ bind(&end_del_check);
1557
1558 // Store the entry in the 'each' expression and take another spin in the loop.
mads.s.ager31e71382008-08-13 09:32:07 +00001559 // r3: i'th entry of the enum cache (or string there of)
1560 __ push(r3); // push entry
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001561 { Reference each(this, node->each());
1562 if (!each.is_illegal()) {
mads.s.ager31e71382008-08-13 09:32:07 +00001563 if (each.size() > 0) {
1564 __ ldr(r0, MemOperand(sp, kPointerSize * each.size()));
1565 __ push(r0);
1566 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001567 // If the reference was to a slot we rely on the convenient property
1568 // that it doesn't matter whether a value (eg, r3 pushed above) is
1569 // right on top of or right underneath a zero-sized reference.
1570 each.SetValue(NOT_CONST_INIT);
mads.s.ager31e71382008-08-13 09:32:07 +00001571 if (each.size() > 0) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001572 // It's safe to pop the value lying on top of the reference before
1573 // unloading the reference itself (which preserves the top of stack,
1574 // ie, now the topmost value of the non-zero sized reference), since
1575 // we will discard the top of stack after unloading the reference
1576 // anyway.
1577 __ pop(r0);
mads.s.ager31e71382008-08-13 09:32:07 +00001578 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001579 }
1580 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001581 // Discard the i'th entry pushed above or else the remainder of the
1582 // reference, whichever is currently on top of the stack.
1583 __ pop();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001584 CheckStack(); // TODO(1222600): ignore if body contains calls.
1585 __ jmp(&loop);
1586
1587 // Cleanup.
1588 __ bind(&cleanup);
1589 __ bind(node->break_target());
mads.s.ager31e71382008-08-13 09:32:07 +00001590 __ add(sp, sp, Operand(5 * kPointerSize));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001591
1592 // Exit.
1593 __ bind(&exit);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001594
1595 break_stack_height_ -= kForInStackSize;
1596}
1597
1598
ager@chromium.org7c537e22008-10-16 08:43:32 +00001599void CodeGenerator::VisitTryCatch(TryCatch* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001600 Comment cmnt(masm_, "[ TryCatch");
1601
1602 Label try_block, exit;
1603
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001604 __ bl(&try_block);
1605
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001606 // --- Catch block ---
1607
1608 // Store the caught exception in the catch variable.
mads.s.ager31e71382008-08-13 09:32:07 +00001609 __ push(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001610 { Reference ref(this, node->catch_var());
ager@chromium.org7c537e22008-10-16 08:43:32 +00001611 ASSERT(ref.is_slot());
1612 // Here we make use of the convenient property that it doesn't matter
1613 // whether a value is immediately on top of or underneath a zero-sized
1614 // reference.
1615 ref.SetValue(NOT_CONST_INIT);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001616 }
1617
1618 // Remove the exception from the stack.
mads.s.ager31e71382008-08-13 09:32:07 +00001619 __ pop();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001620
1621 VisitStatements(node->catch_block()->statements());
1622 __ b(&exit);
1623
1624
1625 // --- Try block ---
1626 __ bind(&try_block);
1627
1628 __ PushTryHandler(IN_JAVASCRIPT, TRY_CATCH_HANDLER);
1629
1630 // Introduce shadow labels for all escapes from the try block,
1631 // including returns. We should probably try to unify the escaping
1632 // labels and the return label.
1633 int nof_escapes = node->escaping_labels()->length();
1634 List<LabelShadow*> shadows(1 + nof_escapes);
1635 shadows.Add(new LabelShadow(&function_return_));
1636 for (int i = 0; i < nof_escapes; i++) {
1637 shadows.Add(new LabelShadow(node->escaping_labels()->at(i)));
1638 }
1639
1640 // Generate code for the statements in the try block.
1641 VisitStatements(node->try_block()->statements());
mads.s.ager31e71382008-08-13 09:32:07 +00001642 __ pop(r0); // Discard the result.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001643
1644 // Stop the introduced shadowing and count the number of required unlinks.
1645 int nof_unlinks = 0;
1646 for (int i = 0; i <= nof_escapes; i++) {
1647 shadows[i]->StopShadowing();
1648 if (shadows[i]->is_linked()) nof_unlinks++;
1649 }
1650
1651 // Unlink from try chain.
1652 // TOS contains code slot
1653 const int kNextOffset = StackHandlerConstants::kNextOffset +
1654 StackHandlerConstants::kAddressDisplacement;
1655 __ ldr(r1, MemOperand(sp, kNextOffset)); // read next_sp
1656 __ mov(r3, Operand(ExternalReference(Top::k_handler_address)));
1657 __ str(r1, MemOperand(r3));
1658 ASSERT(StackHandlerConstants::kCodeOffset == 0); // first field is code
1659 __ add(sp, sp, Operand(StackHandlerConstants::kSize - kPointerSize));
1660 // Code slot popped.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001661 if (nof_unlinks > 0) __ b(&exit);
1662
1663 // Generate unlink code for all used shadow labels.
1664 for (int i = 0; i <= nof_escapes; i++) {
1665 if (shadows[i]->is_linked()) {
mads.s.ager31e71382008-08-13 09:32:07 +00001666 // Unlink from try chain;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001667 __ bind(shadows[i]);
1668
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001669 // Reload sp from the top handler, because some statements that we
1670 // break from (eg, for...in) may have left stuff on the stack.
1671 __ mov(r3, Operand(ExternalReference(Top::k_handler_address)));
1672 __ ldr(sp, MemOperand(r3));
1673
1674 __ ldr(r1, MemOperand(sp, kNextOffset));
1675 __ str(r1, MemOperand(r3));
1676 ASSERT(StackHandlerConstants::kCodeOffset == 0); // first field is code
1677 __ add(sp, sp, Operand(StackHandlerConstants::kSize - kPointerSize));
1678 // Code slot popped.
1679
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001680 __ b(shadows[i]->shadowed());
1681 }
1682 }
1683
1684 __ bind(&exit);
1685}
1686
1687
ager@chromium.org7c537e22008-10-16 08:43:32 +00001688void CodeGenerator::VisitTryFinally(TryFinally* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001689 Comment cmnt(masm_, "[ TryFinally");
1690
1691 // State: Used to keep track of reason for entering the finally
1692 // block. Should probably be extended to hold information for
1693 // break/continue from within the try block.
1694 enum { FALLING, THROWING, JUMPING };
1695
1696 Label exit, unlink, try_block, finally_block;
1697
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001698 __ bl(&try_block);
1699
mads.s.ager31e71382008-08-13 09:32:07 +00001700 __ push(r0); // save exception object on the stack
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001701 // In case of thrown exceptions, this is where we continue.
1702 __ mov(r2, Operand(Smi::FromInt(THROWING)));
1703 __ b(&finally_block);
1704
1705
1706 // --- Try block ---
1707 __ bind(&try_block);
1708
1709 __ PushTryHandler(IN_JAVASCRIPT, TRY_FINALLY_HANDLER);
1710
1711 // Introduce shadow labels for all escapes from the try block,
1712 // including returns. We should probably try to unify the escaping
1713 // labels and the return label.
1714 int nof_escapes = node->escaping_labels()->length();
1715 List<LabelShadow*> shadows(1 + nof_escapes);
1716 shadows.Add(new LabelShadow(&function_return_));
1717 for (int i = 0; i < nof_escapes; i++) {
1718 shadows.Add(new LabelShadow(node->escaping_labels()->at(i)));
1719 }
1720
1721 // Generate code for the statements in the try block.
1722 VisitStatements(node->try_block()->statements());
1723
1724 // Stop the introduced shadowing and count the number of required
1725 // unlinks.
1726 int nof_unlinks = 0;
1727 for (int i = 0; i <= nof_escapes; i++) {
1728 shadows[i]->StopShadowing();
1729 if (shadows[i]->is_linked()) nof_unlinks++;
1730 }
1731
1732 // Set the state on the stack to FALLING.
mads.s.ager31e71382008-08-13 09:32:07 +00001733 __ mov(r0, Operand(Factory::undefined_value())); // fake TOS
1734 __ push(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001735 __ mov(r2, Operand(Smi::FromInt(FALLING)));
1736 if (nof_unlinks > 0) __ b(&unlink);
1737
1738 // Generate code that sets the state for all used shadow labels.
1739 for (int i = 0; i <= nof_escapes; i++) {
1740 if (shadows[i]->is_linked()) {
1741 __ bind(shadows[i]);
mads.s.ager31e71382008-08-13 09:32:07 +00001742 if (shadows[i]->shadowed() == &function_return_) {
1743 __ push(r0); // Materialize the return value on the stack
1744 } else {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001745 // Fake TOS for break and continue (not return).
mads.s.ager31e71382008-08-13 09:32:07 +00001746 __ mov(r0, Operand(Factory::undefined_value()));
1747 __ push(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001748 }
1749 __ mov(r2, Operand(Smi::FromInt(JUMPING + i)));
1750 __ b(&unlink);
1751 }
1752 }
1753
mads.s.ager31e71382008-08-13 09:32:07 +00001754 // Unlink from try chain;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001755 __ bind(&unlink);
1756
mads.s.ager31e71382008-08-13 09:32:07 +00001757 __ pop(r0); // Store TOS in r0 across stack manipulation
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001758 // Reload sp from the top handler, because some statements that we
1759 // break from (eg, for...in) may have left stuff on the stack.
1760 __ mov(r3, Operand(ExternalReference(Top::k_handler_address)));
1761 __ ldr(sp, MemOperand(r3));
1762 const int kNextOffset = StackHandlerConstants::kNextOffset +
1763 StackHandlerConstants::kAddressDisplacement;
1764 __ ldr(r1, MemOperand(sp, kNextOffset));
1765 __ str(r1, MemOperand(r3));
1766 ASSERT(StackHandlerConstants::kCodeOffset == 0); // first field is code
1767 __ add(sp, sp, Operand(StackHandlerConstants::kSize - kPointerSize));
1768 // Code slot popped.
mads.s.ager31e71382008-08-13 09:32:07 +00001769 __ push(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001770
1771 // --- Finally block ---
1772 __ bind(&finally_block);
1773
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001774 // Push the state on the stack.
mads.s.ager31e71382008-08-13 09:32:07 +00001775 __ push(r2);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001776
1777 // We keep two elements on the stack - the (possibly faked) result
1778 // and the state - while evaluating the finally block. Record it, so
1779 // that a break/continue crossing this statement can restore the
1780 // stack.
1781 const int kFinallyStackSize = 2 * kPointerSize;
1782 break_stack_height_ += kFinallyStackSize;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001783
1784 // Generate code for the statements in the finally block.
1785 VisitStatements(node->finally_block()->statements());
1786
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001787 // Restore state and return value or faked TOS.
mads.s.ager31e71382008-08-13 09:32:07 +00001788 __ pop(r2);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001789 __ pop(r0);
1790 break_stack_height_ -= kFinallyStackSize;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001791
1792 // Generate code that jumps to the right destination for all used
1793 // shadow labels.
1794 for (int i = 0; i <= nof_escapes; i++) {
1795 if (shadows[i]->is_bound()) {
1796 __ cmp(r2, Operand(Smi::FromInt(JUMPING + i)));
1797 if (shadows[i]->shadowed() != &function_return_) {
1798 Label next;
1799 __ b(ne, &next);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001800 __ b(shadows[i]->shadowed());
1801 __ bind(&next);
1802 } else {
1803 __ b(eq, shadows[i]->shadowed());
1804 }
1805 }
1806 }
1807
1808 // Check if we need to rethrow the exception.
1809 __ cmp(r2, Operand(Smi::FromInt(THROWING)));
1810 __ b(ne, &exit);
1811
1812 // Rethrow exception.
mads.s.ager31e71382008-08-13 09:32:07 +00001813 __ push(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001814 __ CallRuntime(Runtime::kReThrow, 1);
1815
1816 // Done.
1817 __ bind(&exit);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001818}
1819
1820
ager@chromium.org7c537e22008-10-16 08:43:32 +00001821void CodeGenerator::VisitDebuggerStatement(DebuggerStatement* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001822 Comment cmnt(masm_, "[ DebuggerStatament");
1823 if (FLAG_debug_info) RecordStatementPosition(node);
1824 __ CallRuntime(Runtime::kDebugBreak, 1);
mads.s.ager31e71382008-08-13 09:32:07 +00001825 __ push(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001826}
1827
1828
ager@chromium.org7c537e22008-10-16 08:43:32 +00001829void CodeGenerator::InstantiateBoilerplate(Handle<JSFunction> boilerplate) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001830 ASSERT(boilerplate->IsBoilerplate());
1831
1832 // Push the boilerplate on the stack.
mads.s.ager31e71382008-08-13 09:32:07 +00001833 __ mov(r0, Operand(boilerplate));
1834 __ push(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001835
1836 // Create a new closure.
mads.s.ager31e71382008-08-13 09:32:07 +00001837 __ push(cp);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001838 __ CallRuntime(Runtime::kNewClosure, 2);
mads.s.ager31e71382008-08-13 09:32:07 +00001839 __ push(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001840}
1841
1842
ager@chromium.org7c537e22008-10-16 08:43:32 +00001843void CodeGenerator::VisitFunctionLiteral(FunctionLiteral* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001844 Comment cmnt(masm_, "[ FunctionLiteral");
1845
1846 // Build the function boilerplate and instantiate it.
1847 Handle<JSFunction> boilerplate = BuildBoilerplate(node);
kasper.lund212ac232008-07-16 07:07:30 +00001848 // Check for stack-overflow exception.
1849 if (HasStackOverflow()) return;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001850 InstantiateBoilerplate(boilerplate);
1851}
1852
1853
ager@chromium.org7c537e22008-10-16 08:43:32 +00001854void CodeGenerator::VisitFunctionBoilerplateLiteral(
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001855 FunctionBoilerplateLiteral* node) {
1856 Comment cmnt(masm_, "[ FunctionBoilerplateLiteral");
1857 InstantiateBoilerplate(node->boilerplate());
1858}
1859
1860
ager@chromium.org7c537e22008-10-16 08:43:32 +00001861void CodeGenerator::VisitConditional(Conditional* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001862 Comment cmnt(masm_, "[ Conditional");
1863 Label then, else_, exit;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001864 LoadCondition(node->condition(), NOT_INSIDE_TYPEOF, &then, &else_, true);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001865 Branch(false, &else_);
1866 __ bind(&then);
ager@chromium.org7c537e22008-10-16 08:43:32 +00001867 Load(node->then_expression(), typeof_state());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001868 __ b(&exit);
1869 __ bind(&else_);
ager@chromium.org7c537e22008-10-16 08:43:32 +00001870 Load(node->else_expression(), typeof_state());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001871 __ bind(&exit);
1872}
1873
1874
ager@chromium.org7c537e22008-10-16 08:43:32 +00001875void CodeGenerator::LoadFromSlot(Slot* slot, TypeofState typeof_state) {
1876 if (slot->type() == Slot::LOOKUP) {
1877 ASSERT(slot->var()->mode() == Variable::DYNAMIC);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001878
1879 // For now, just do a runtime call.
mads.s.ager31e71382008-08-13 09:32:07 +00001880 __ push(cp);
ager@chromium.org7c537e22008-10-16 08:43:32 +00001881 __ mov(r0, Operand(slot->var()->name()));
mads.s.ager31e71382008-08-13 09:32:07 +00001882 __ push(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001883
ager@chromium.org7c537e22008-10-16 08:43:32 +00001884 if (typeof_state == INSIDE_TYPEOF) {
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00001885 __ CallRuntime(Runtime::kLoadContextSlotNoReferenceError, 2);
ager@chromium.org7c537e22008-10-16 08:43:32 +00001886 } else {
1887 __ CallRuntime(Runtime::kLoadContextSlot, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001888 }
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00001889 __ push(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001890
1891 } else {
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00001892 // Note: We would like to keep the assert below, but it fires because of
1893 // some nasty code in LoadTypeofExpression() which should be removed...
ager@chromium.org7c537e22008-10-16 08:43:32 +00001894 // ASSERT(slot->var()->mode() != Variable::DYNAMIC);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001895
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00001896 // Special handling for locals allocated in registers.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001897 __ ldr(r0, SlotOperand(slot, r2));
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00001898 __ push(r0);
ager@chromium.org7c537e22008-10-16 08:43:32 +00001899 if (slot->var()->mode() == Variable::CONST) {
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00001900 // Const slots may contain 'the hole' value (the constant hasn't been
1901 // initialized yet) which needs to be converted into the 'undefined'
1902 // value.
1903 Comment cmnt(masm_, "[ Unhole const");
1904 __ pop(r0);
1905 __ cmp(r0, Operand(Factory::the_hole_value()));
1906 __ mov(r0, Operand(Factory::undefined_value()), LeaveCC, eq);
1907 __ push(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001908 }
1909 }
1910}
1911
1912
ager@chromium.org7c537e22008-10-16 08:43:32 +00001913void CodeGenerator::VisitSlot(Slot* node) {
1914 Comment cmnt(masm_, "[ Slot");
1915 LoadFromSlot(node, typeof_state());
1916}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001917
ager@chromium.org7c537e22008-10-16 08:43:32 +00001918
1919void CodeGenerator::VisitVariableProxy(VariableProxy* node) {
1920 Comment cmnt(masm_, "[ VariableProxy");
1921
1922 Variable* var = node->var();
1923 Expression* expr = var->rewrite();
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00001924 if (expr != NULL) {
1925 Visit(expr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001926 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001927 ASSERT(var->is_global());
1928 Reference ref(this, node);
1929 ref.GetValue(typeof_state());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001930 }
1931}
1932
1933
ager@chromium.org7c537e22008-10-16 08:43:32 +00001934void CodeGenerator::VisitLiteral(Literal* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001935 Comment cmnt(masm_, "[ Literal");
mads.s.ager31e71382008-08-13 09:32:07 +00001936 __ mov(r0, Operand(node->handle()));
1937 __ push(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001938}
1939
1940
ager@chromium.org7c537e22008-10-16 08:43:32 +00001941void CodeGenerator::VisitRegExpLiteral(RegExpLiteral* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001942 Comment cmnt(masm_, "[ RexExp Literal");
1943
1944 // Retrieve the literal array and check the allocated entry.
1945
1946 // Load the function of this activation.
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00001947 __ ldr(r1, FunctionOperand());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001948
1949 // Load the literals array of the function.
1950 __ ldr(r1, FieldMemOperand(r1, JSFunction::kLiteralsOffset));
1951
1952 // Load the literal at the ast saved index.
1953 int literal_offset =
1954 FixedArray::kHeaderSize + node->literal_index() * kPointerSize;
1955 __ ldr(r2, FieldMemOperand(r1, literal_offset));
1956
1957 Label done;
1958 __ cmp(r2, Operand(Factory::undefined_value()));
1959 __ b(ne, &done);
1960
1961 // If the entry is undefined we call the runtime system to computed
1962 // the literal.
mads.s.ager31e71382008-08-13 09:32:07 +00001963 __ push(r1); // literal array (0)
1964 __ mov(r0, Operand(Smi::FromInt(node->literal_index())));
1965 __ push(r0); // literal index (1)
1966 __ mov(r0, Operand(node->pattern())); // RegExp pattern (2)
1967 __ push(r0);
1968 __ mov(r0, Operand(node->flags())); // RegExp flags (3)
1969 __ push(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001970 __ CallRuntime(Runtime::kMaterializeRegExpLiteral, 4);
mads.s.ager31e71382008-08-13 09:32:07 +00001971 __ mov(r2, Operand(r0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001972
mads.s.ager31e71382008-08-13 09:32:07 +00001973 __ bind(&done);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001974 // Push the literal.
mads.s.ager31e71382008-08-13 09:32:07 +00001975 __ push(r2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001976}
1977
1978
1979// This deferred code stub will be used for creating the boilerplate
1980// by calling Runtime_CreateObjectLiteral.
1981// Each created boilerplate is stored in the JSFunction and they are
1982// therefore context dependent.
1983class ObjectLiteralDeferred: public DeferredCode {
1984 public:
1985 ObjectLiteralDeferred(CodeGenerator* generator, ObjectLiteral* node)
1986 : DeferredCode(generator), node_(node) {
1987 set_comment("[ ObjectLiteralDeferred");
1988 }
1989 virtual void Generate();
1990 private:
1991 ObjectLiteral* node_;
1992};
1993
1994
1995void ObjectLiteralDeferred::Generate() {
1996 // If the entry is undefined we call the runtime system to computed
1997 // the literal.
1998
1999 // Literal array (0).
mads.s.ager31e71382008-08-13 09:32:07 +00002000 __ push(r1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002001 // Literal index (1).
mads.s.ager31e71382008-08-13 09:32:07 +00002002 __ mov(r0, Operand(Smi::FromInt(node_->literal_index())));
2003 __ push(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002004 // Constant properties (2).
mads.s.ager31e71382008-08-13 09:32:07 +00002005 __ mov(r0, Operand(node_->constant_properties()));
2006 __ push(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002007 __ CallRuntime(Runtime::kCreateObjectLiteralBoilerplate, 3);
mads.s.ager31e71382008-08-13 09:32:07 +00002008 __ mov(r2, Operand(r0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002009}
2010
2011
ager@chromium.org7c537e22008-10-16 08:43:32 +00002012void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002013 Comment cmnt(masm_, "[ ObjectLiteral");
2014
2015 ObjectLiteralDeferred* deferred = new ObjectLiteralDeferred(this, node);
2016
2017 // Retrieve the literal array and check the allocated entry.
2018
2019 // Load the function of this activation.
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00002020 __ ldr(r1, FunctionOperand());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002021
2022 // Load the literals array of the function.
2023 __ ldr(r1, FieldMemOperand(r1, JSFunction::kLiteralsOffset));
2024
2025 // Load the literal at the ast saved index.
2026 int literal_offset =
2027 FixedArray::kHeaderSize + node->literal_index() * kPointerSize;
2028 __ ldr(r2, FieldMemOperand(r1, literal_offset));
2029
2030 // Check whether we need to materialize the object literal boilerplate.
2031 // If so, jump to the deferred code.
2032 __ cmp(r2, Operand(Factory::undefined_value()));
2033 __ b(eq, deferred->enter());
2034 __ bind(deferred->exit());
2035
2036 // Push the object literal boilerplate.
mads.s.ager31e71382008-08-13 09:32:07 +00002037 __ push(r2);
2038
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002039 // Clone the boilerplate object.
2040 __ CallRuntime(Runtime::kCloneObjectLiteralBoilerplate, 1);
mads.s.ager31e71382008-08-13 09:32:07 +00002041 __ push(r0); // save the result
2042 // r0: cloned object literal
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002043
2044 for (int i = 0; i < node->properties()->length(); i++) {
2045 ObjectLiteral::Property* property = node->properties()->at(i);
2046 Literal* key = property->key();
2047 Expression* value = property->value();
2048 switch (property->kind()) {
2049 case ObjectLiteral::Property::CONSTANT: break;
2050 case ObjectLiteral::Property::COMPUTED: // fall through
2051 case ObjectLiteral::Property::PROTOTYPE: {
mads.s.ager31e71382008-08-13 09:32:07 +00002052 __ push(r0); // dup the result
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002053 Load(key);
2054 Load(value);
2055 __ CallRuntime(Runtime::kSetProperty, 3);
mads.s.ager31e71382008-08-13 09:32:07 +00002056 // restore r0
2057 __ ldr(r0, MemOperand(sp, 0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002058 break;
2059 }
2060 case ObjectLiteral::Property::SETTER: {
2061 __ push(r0);
2062 Load(key);
mads.s.ager31e71382008-08-13 09:32:07 +00002063 __ mov(r0, Operand(Smi::FromInt(1)));
2064 __ push(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002065 Load(value);
2066 __ CallRuntime(Runtime::kDefineAccessor, 4);
mads.s.ager31e71382008-08-13 09:32:07 +00002067 __ ldr(r0, MemOperand(sp, 0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002068 break;
2069 }
2070 case ObjectLiteral::Property::GETTER: {
2071 __ push(r0);
2072 Load(key);
mads.s.ager31e71382008-08-13 09:32:07 +00002073 __ mov(r0, Operand(Smi::FromInt(0)));
2074 __ push(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002075 Load(value);
2076 __ CallRuntime(Runtime::kDefineAccessor, 4);
mads.s.ager31e71382008-08-13 09:32:07 +00002077 __ ldr(r0, MemOperand(sp, 0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002078 break;
2079 }
2080 }
2081 }
2082}
2083
2084
ager@chromium.org7c537e22008-10-16 08:43:32 +00002085void CodeGenerator::VisitArrayLiteral(ArrayLiteral* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002086 Comment cmnt(masm_, "[ ArrayLiteral");
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002087
2088 // Call runtime to create the array literal.
2089 __ mov(r0, Operand(node->literals()));
2090 __ push(r0);
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00002091 // Load the function of this frame.
2092 __ ldr(r0, FunctionOperand());
2093 __ ldr(r0, FieldMemOperand(r0, JSFunction::kLiteralsOffset));
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002094 __ push(r0);
2095 __ CallRuntime(Runtime::kCreateArrayLiteral, 2);
2096
2097 // Push the resulting array literal on the stack.
2098 __ push(r0);
2099
2100 // Generate code to set the elements in the array that are not
2101 // literals.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002102 for (int i = 0; i < node->values()->length(); i++) {
2103 Expression* value = node->values()->at(i);
2104
2105 // If value is literal the property value is already
2106 // set in the boilerplate object.
2107 if (value->AsLiteral() == NULL) {
2108 // The property must be set by generated code.
2109 Load(value);
mads.s.ager31e71382008-08-13 09:32:07 +00002110 __ pop(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002111
2112 // Fetch the object literal
2113 __ ldr(r1, MemOperand(sp, 0));
2114 // Get the elements array.
2115 __ ldr(r1, FieldMemOperand(r1, JSObject::kElementsOffset));
2116
2117 // Write to the indexed properties array.
2118 int offset = i * kPointerSize + Array::kHeaderSize;
2119 __ str(r0, FieldMemOperand(r1, offset));
2120
2121 // Update the write barrier for the array address.
2122 __ mov(r3, Operand(offset));
2123 __ RecordWrite(r1, r3, r2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002124 }
2125 }
2126}
2127
2128
ager@chromium.org7c537e22008-10-16 08:43:32 +00002129void CodeGenerator::VisitAssignment(Assignment* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002130 Comment cmnt(masm_, "[ Assignment");
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002131 if (FLAG_debug_info) RecordStatementPosition(node);
mads.s.ager31e71382008-08-13 09:32:07 +00002132
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002133 Reference target(this, node->target());
2134 if (target.is_illegal()) return;
2135
2136 if (node->op() == Token::ASSIGN ||
2137 node->op() == Token::INIT_VAR ||
2138 node->op() == Token::INIT_CONST) {
2139 Load(node->value());
2140
2141 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002142 target.GetValue(NOT_INSIDE_TYPEOF);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002143 Literal* literal = node->value()->AsLiteral();
2144 if (literal != NULL && literal->handle()->IsSmi()) {
2145 SmiOperation(node->binary_op(), literal->handle(), false);
mads.s.ager31e71382008-08-13 09:32:07 +00002146 __ push(r0);
2147
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002148 } else {
2149 Load(node->value());
kasper.lund7276f142008-07-30 08:49:36 +00002150 GenericBinaryOperation(node->binary_op());
mads.s.ager31e71382008-08-13 09:32:07 +00002151 __ push(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002152 }
2153 }
2154
2155 Variable* var = node->target()->AsVariableProxy()->AsVariable();
2156 if (var != NULL &&
2157 (var->mode() == Variable::CONST) &&
2158 node->op() != Token::INIT_VAR && node->op() != Token::INIT_CONST) {
2159 // Assignment ignored - leave the value on the stack.
mads.s.ager31e71382008-08-13 09:32:07 +00002160
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002161 } else {
2162 __ RecordPosition(node->position());
2163 if (node->op() == Token::INIT_CONST) {
2164 // Dynamic constant initializations must use the function context
2165 // and initialize the actual constant declared. Dynamic variable
2166 // initializations are simply assignments and use SetValue.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002167 target.SetValue(CONST_INIT);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002168 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002169 target.SetValue(NOT_CONST_INIT);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002170 }
2171 }
2172}
2173
2174
ager@chromium.org7c537e22008-10-16 08:43:32 +00002175void CodeGenerator::VisitThrow(Throw* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002176 Comment cmnt(masm_, "[ Throw");
2177
2178 Load(node->exception());
2179 __ RecordPosition(node->position());
2180 __ CallRuntime(Runtime::kThrow, 1);
mads.s.ager31e71382008-08-13 09:32:07 +00002181 __ push(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002182}
2183
2184
ager@chromium.org7c537e22008-10-16 08:43:32 +00002185void CodeGenerator::VisitProperty(Property* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002186 Comment cmnt(masm_, "[ Property");
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00002187
ager@chromium.org7c537e22008-10-16 08:43:32 +00002188 Reference property(this, node);
2189 property.GetValue(typeof_state());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002190}
2191
2192
ager@chromium.org7c537e22008-10-16 08:43:32 +00002193void CodeGenerator::VisitCall(Call* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002194 Comment cmnt(masm_, "[ Call");
2195
2196 ZoneList<Expression*>* args = node->arguments();
2197
2198 if (FLAG_debug_info) RecordStatementPosition(node);
2199 // Standard function call.
2200
2201 // Check if the function is a variable or a property.
2202 Expression* function = node->expression();
2203 Variable* var = function->AsVariableProxy()->AsVariable();
2204 Property* property = function->AsProperty();
2205
2206 // ------------------------------------------------------------------------
2207 // Fast-case: Use inline caching.
2208 // ---
2209 // According to ECMA-262, section 11.2.3, page 44, the function to call
2210 // must be resolved after the arguments have been evaluated. The IC code
2211 // automatically handles this by loading the arguments before the function
2212 // is resolved in cache misses (this also holds for megamorphic calls).
2213 // ------------------------------------------------------------------------
2214
2215 if (var != NULL && !var->is_this() && var->is_global()) {
2216 // ----------------------------------
2217 // JavaScript example: 'foo(1, 2, 3)' // foo is global
2218 // ----------------------------------
2219
2220 // Push the name of the function and the receiver onto the stack.
mads.s.ager31e71382008-08-13 09:32:07 +00002221 __ mov(r0, Operand(var->name()));
2222 __ push(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002223 LoadGlobal();
2224
2225 // Load the arguments.
2226 for (int i = 0; i < args->length(); i++) Load(args->at(i));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002227
2228 // Setup the receiver register and call the IC initialization code.
2229 Handle<Code> stub = ComputeCallInitialize(args->length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002230 __ RecordPosition(node->position());
ager@chromium.org236ad962008-09-25 09:45:57 +00002231 __ Call(stub, RelocInfo::CODE_TARGET_CONTEXT);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002232 __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002233 // Remove the function from the stack.
mads.s.ager31e71382008-08-13 09:32:07 +00002234 __ pop();
2235 __ push(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002236
2237 } else if (var != NULL && var->slot() != NULL &&
2238 var->slot()->type() == Slot::LOOKUP) {
2239 // ----------------------------------
2240 // JavaScript example: 'with (obj) foo(1, 2, 3)' // foo is in obj
2241 // ----------------------------------
2242
2243 // Load the function
mads.s.ager31e71382008-08-13 09:32:07 +00002244 __ push(cp);
2245 __ mov(r0, Operand(var->name()));
2246 __ push(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002247 __ CallRuntime(Runtime::kLoadContextSlot, 2);
2248 // r0: slot value; r1: receiver
2249
2250 // Load the receiver.
mads.s.ager31e71382008-08-13 09:32:07 +00002251 __ push(r0); // function
2252 __ push(r1); // receiver
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002253
2254 // Call the function.
2255 CallWithArguments(args, node->position());
mads.s.ager31e71382008-08-13 09:32:07 +00002256 __ push(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002257
2258 } else if (property != NULL) {
2259 // Check if the key is a literal string.
2260 Literal* literal = property->key()->AsLiteral();
2261
2262 if (literal != NULL && literal->handle()->IsSymbol()) {
2263 // ------------------------------------------------------------------
2264 // JavaScript example: 'object.foo(1, 2, 3)' or 'map["key"](1, 2, 3)'
2265 // ------------------------------------------------------------------
2266
2267 // Push the name of the function and the receiver onto the stack.
mads.s.ager31e71382008-08-13 09:32:07 +00002268 __ mov(r0, Operand(literal->handle()));
2269 __ push(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002270 Load(property->obj());
2271
2272 // Load the arguments.
2273 for (int i = 0; i < args->length(); i++) Load(args->at(i));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002274
2275 // Set the receiver register and call the IC initialization code.
2276 Handle<Code> stub = ComputeCallInitialize(args->length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002277 __ RecordPosition(node->position());
ager@chromium.org236ad962008-09-25 09:45:57 +00002278 __ Call(stub, RelocInfo::CODE_TARGET);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002279 __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
2280
2281 // Remove the function from the stack.
mads.s.ager31e71382008-08-13 09:32:07 +00002282 __ pop();
2283
2284 __ push(r0); // push after get rid of function from the stack
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002285
2286 } else {
2287 // -------------------------------------------
2288 // JavaScript example: 'array[index](1, 2, 3)'
2289 // -------------------------------------------
2290
2291 // Load the function to call from the property through a reference.
2292 Reference ref(this, property);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002293 ref.GetValue(NOT_INSIDE_TYPEOF); // receiver
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002294
2295 // Pass receiver to called function.
mads.s.ager31e71382008-08-13 09:32:07 +00002296 __ ldr(r0, MemOperand(sp, ref.size() * kPointerSize));
2297 __ push(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002298 // Call the function.
2299 CallWithArguments(args, node->position());
mads.s.ager31e71382008-08-13 09:32:07 +00002300 __ push(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002301 }
2302
2303 } else {
2304 // ----------------------------------
2305 // JavaScript example: 'foo(1, 2, 3)' // foo is not global
2306 // ----------------------------------
2307
2308 // Load the function.
2309 Load(function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002310 // Pass the global object as the receiver.
2311 LoadGlobal();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002312 // Call the function.
2313 CallWithArguments(args, node->position());
mads.s.ager31e71382008-08-13 09:32:07 +00002314 __ push(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002315 }
2316}
2317
2318
ager@chromium.org7c537e22008-10-16 08:43:32 +00002319void CodeGenerator::VisitCallNew(CallNew* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002320 Comment cmnt(masm_, "[ CallNew");
2321
2322 // According to ECMA-262, section 11.2.2, page 44, the function
2323 // expression in new calls must be evaluated before the
2324 // arguments. This is different from ordinary calls, where the
2325 // actual function to call is resolved after the arguments have been
2326 // evaluated.
2327
2328 // Compute function to call and use the global object as the
2329 // receiver.
2330 Load(node->expression());
2331 LoadGlobal();
2332
2333 // Push the arguments ("left-to-right") on the stack.
2334 ZoneList<Expression*>* args = node->arguments();
2335 for (int i = 0; i < args->length(); i++) Load(args->at(i));
2336
mads.s.ager31e71382008-08-13 09:32:07 +00002337 // r0: the number of arguments.
2338 __ mov(r0, Operand(args->length()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002339
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00002340 // Load the function into r1 as per calling convention.
2341 __ ldr(r1, MemOperand(sp, (args->length() + 1) * kPointerSize));
2342
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002343 // Call the construct call builtin that handles allocation and
2344 // constructor invocation.
ager@chromium.org236ad962008-09-25 09:45:57 +00002345 __ RecordPosition(RelocInfo::POSITION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002346 __ Call(Handle<Code>(Builtins::builtin(Builtins::JSConstructCall)),
ager@chromium.org236ad962008-09-25 09:45:57 +00002347 RelocInfo::CONSTRUCT_CALL);
mads.s.ager31e71382008-08-13 09:32:07 +00002348
2349 // Discard old TOS value and push r0 on the stack (same as Pop(), push(r0)).
2350 __ str(r0, MemOperand(sp, 0 * kPointerSize));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002351}
2352
2353
ager@chromium.org7c537e22008-10-16 08:43:32 +00002354void CodeGenerator::GenerateValueOf(ZoneList<Expression*>* args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002355 ASSERT(args->length() == 1);
2356 Label leave;
2357 Load(args->at(0));
mads.s.ager31e71382008-08-13 09:32:07 +00002358 __ pop(r0); // r0 contains object.
2359 // if (object->IsSmi()) return the object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002360 __ tst(r0, Operand(kSmiTagMask));
2361 __ b(eq, &leave);
2362 // It is a heap object - get map.
2363 __ ldr(r1, FieldMemOperand(r0, HeapObject::kMapOffset));
2364 __ ldrb(r1, FieldMemOperand(r1, Map::kInstanceTypeOffset));
mads.s.ager31e71382008-08-13 09:32:07 +00002365 // if (!object->IsJSValue()) return the object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002366 __ cmp(r1, Operand(JS_VALUE_TYPE));
2367 __ b(ne, &leave);
2368 // Load the value.
2369 __ ldr(r0, FieldMemOperand(r0, JSValue::kValueOffset));
2370 __ bind(&leave);
mads.s.ager31e71382008-08-13 09:32:07 +00002371 __ push(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002372}
2373
2374
ager@chromium.org7c537e22008-10-16 08:43:32 +00002375void CodeGenerator::GenerateSetValueOf(ZoneList<Expression*>* args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002376 ASSERT(args->length() == 2);
2377 Label leave;
2378 Load(args->at(0)); // Load the object.
2379 Load(args->at(1)); // Load the value.
mads.s.ager31e71382008-08-13 09:32:07 +00002380 __ pop(r0); // r0 contains value
2381 __ pop(r1); // r1 contains object
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002382 // if (object->IsSmi()) return object.
2383 __ tst(r1, Operand(kSmiTagMask));
2384 __ b(eq, &leave);
2385 // It is a heap object - get map.
2386 __ ldr(r2, FieldMemOperand(r1, HeapObject::kMapOffset));
2387 __ ldrb(r2, FieldMemOperand(r2, Map::kInstanceTypeOffset));
2388 // if (!object->IsJSValue()) return object.
2389 __ cmp(r2, Operand(JS_VALUE_TYPE));
2390 __ b(ne, &leave);
2391 // Store the value.
2392 __ str(r0, FieldMemOperand(r1, JSValue::kValueOffset));
2393 // Update the write barrier.
2394 __ mov(r2, Operand(JSValue::kValueOffset - kHeapObjectTag));
2395 __ RecordWrite(r1, r2, r3);
2396 // Leave.
2397 __ bind(&leave);
mads.s.ager31e71382008-08-13 09:32:07 +00002398 __ push(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002399}
2400
2401
ager@chromium.org7c537e22008-10-16 08:43:32 +00002402void CodeGenerator::GenerateIsSmi(ZoneList<Expression*>* args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002403 ASSERT(args->length() == 1);
2404 Load(args->at(0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002405 __ pop(r0);
mads.s.ager31e71382008-08-13 09:32:07 +00002406 __ tst(r0, Operand(kSmiTagMask));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002407 cc_reg_ = eq;
2408}
2409
2410
ager@chromium.org7c537e22008-10-16 08:43:32 +00002411void CodeGenerator::GenerateIsNonNegativeSmi(ZoneList<Expression*>* args) {
ager@chromium.orgc27e4e72008-09-04 13:52:27 +00002412 ASSERT(args->length() == 1);
2413 Load(args->at(0));
2414 __ pop(r0);
2415 __ tst(r0, Operand(kSmiTagMask | 0x80000000));
2416 cc_reg_ = eq;
2417}
2418
2419
kasper.lund7276f142008-07-30 08:49:36 +00002420// This should generate code that performs a charCodeAt() call or returns
2421// undefined in order to trigger the slow case, Runtime_StringCharCodeAt.
2422// It is not yet implemented on ARM, so it always goes to the slow case.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002423void CodeGenerator::GenerateFastCharCodeAt(ZoneList<Expression*>* args) {
kasper.lund7276f142008-07-30 08:49:36 +00002424 ASSERT(args->length() == 2);
kasper.lund7276f142008-07-30 08:49:36 +00002425 __ mov(r0, Operand(Factory::undefined_value()));
mads.s.ager31e71382008-08-13 09:32:07 +00002426 __ push(r0);
kasper.lund7276f142008-07-30 08:49:36 +00002427}
2428
2429
ager@chromium.org7c537e22008-10-16 08:43:32 +00002430void CodeGenerator::GenerateIsArray(ZoneList<Expression*>* args) {
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00002431 ASSERT(args->length() == 1);
2432 Load(args->at(0));
2433 Label answer;
2434 // We need the CC bits to come out as not_equal in the case where the
2435 // object is a smi. This can't be done with the usual test opcode so
2436 // we use XOR to get the right CC bits.
2437 __ pop(r0);
2438 __ and_(r1, r0, Operand(kSmiTagMask));
2439 __ eor(r1, r1, Operand(kSmiTagMask), SetCC);
2440 __ b(ne, &answer);
2441 // It is a heap object - get the map.
2442 __ ldr(r1, FieldMemOperand(r0, HeapObject::kMapOffset));
2443 __ ldrb(r1, FieldMemOperand(r1, Map::kInstanceTypeOffset));
2444 // Check if the object is a JS array or not.
2445 __ cmp(r1, Operand(JS_ARRAY_TYPE));
2446 __ bind(&answer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002447 cc_reg_ = eq;
2448}
2449
2450
ager@chromium.org7c537e22008-10-16 08:43:32 +00002451void CodeGenerator::GenerateArgumentsLength(ZoneList<Expression*>* args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002452 ASSERT(args->length() == 0);
2453
mads.s.ager31e71382008-08-13 09:32:07 +00002454 // Seed the result with the formal parameters count, which will be used
2455 // in case no arguments adaptor frame is found below the current frame.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002456 __ mov(r0, Operand(Smi::FromInt(scope_->num_parameters())));
2457
2458 // Call the shared stub to get to the arguments.length.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002459 ArgumentsAccessStub stub(ArgumentsAccessStub::READ_LENGTH);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002460 __ CallStub(&stub);
mads.s.ager31e71382008-08-13 09:32:07 +00002461 __ push(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002462}
2463
2464
ager@chromium.org7c537e22008-10-16 08:43:32 +00002465void CodeGenerator::GenerateArgumentsAccess(ZoneList<Expression*>* args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002466 ASSERT(args->length() == 1);
2467
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00002468 // Satisfy contract with ArgumentsAccessStub:
2469 // Load the key into r1 and the formal parameters count into r0.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002470 Load(args->at(0));
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00002471 __ pop(r1);
2472 __ mov(r0, Operand(Smi::FromInt(scope_->num_parameters())));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002473
2474 // Call the shared stub to get to arguments[key].
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002475 ArgumentsAccessStub stub(ArgumentsAccessStub::READ_ELEMENT);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002476 __ CallStub(&stub);
mads.s.ager31e71382008-08-13 09:32:07 +00002477 __ push(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002478}
2479
2480
ager@chromium.org7c537e22008-10-16 08:43:32 +00002481void CodeGenerator::GenerateObjectEquals(ZoneList<Expression*>* args) {
ager@chromium.org9258b6b2008-09-11 09:11:10 +00002482 ASSERT(args->length() == 2);
2483
2484 // Load the two objects into registers and perform the comparison.
2485 Load(args->at(0));
2486 Load(args->at(1));
2487 __ pop(r0);
2488 __ pop(r1);
2489 __ cmp(r0, Operand(r1));
2490 cc_reg_ = eq;
2491}
2492
2493
ager@chromium.org7c537e22008-10-16 08:43:32 +00002494void CodeGenerator::VisitCallRuntime(CallRuntime* node) {
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00002495 if (CheckForInlineRuntimeCall(node)) return;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002496
2497 ZoneList<Expression*>* args = node->arguments();
2498 Comment cmnt(masm_, "[ CallRuntime");
2499 Runtime::Function* function = node->function();
2500
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002501 if (function != NULL) {
mads.s.ager31e71382008-08-13 09:32:07 +00002502 // Push the arguments ("left-to-right").
2503 for (int i = 0; i < args->length(); i++) Load(args->at(i));
2504
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002505 // Call the C runtime function.
2506 __ CallRuntime(function, args->length());
mads.s.ager31e71382008-08-13 09:32:07 +00002507 __ push(r0);
2508
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002509 } else {
mads.s.ager31e71382008-08-13 09:32:07 +00002510 // Prepare stack for calling JS runtime function.
2511 __ mov(r0, Operand(node->name()));
2512 __ push(r0);
2513 // Push the builtins object found in the current global object.
2514 __ ldr(r1, GlobalObject());
2515 __ ldr(r0, FieldMemOperand(r1, GlobalObject::kBuiltinsOffset));
2516 __ push(r0);
2517
2518 for (int i = 0; i < args->length(); i++) Load(args->at(i));
2519
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002520 // Call the JS runtime function.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002521 Handle<Code> stub = ComputeCallInitialize(args->length());
ager@chromium.org236ad962008-09-25 09:45:57 +00002522 __ Call(stub, RelocInfo::CODE_TARGET);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002523 __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
mads.s.ager31e71382008-08-13 09:32:07 +00002524 __ pop();
2525 __ push(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002526 }
2527}
2528
2529
ager@chromium.org7c537e22008-10-16 08:43:32 +00002530void CodeGenerator::VisitUnaryOperation(UnaryOperation* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002531 Comment cmnt(masm_, "[ UnaryOperation");
2532
2533 Token::Value op = node->op();
2534
2535 if (op == Token::NOT) {
2536 LoadCondition(node->expression(),
ager@chromium.org7c537e22008-10-16 08:43:32 +00002537 NOT_INSIDE_TYPEOF,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002538 false_target(),
2539 true_target(),
2540 true);
2541 cc_reg_ = NegateCondition(cc_reg_);
2542
2543 } else if (op == Token::DELETE) {
2544 Property* property = node->expression()->AsProperty();
mads.s.ager31e71382008-08-13 09:32:07 +00002545 Variable* variable = node->expression()->AsVariableProxy()->AsVariable();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002546 if (property != NULL) {
2547 Load(property->obj());
2548 Load(property->key());
mads.s.ager31e71382008-08-13 09:32:07 +00002549 __ mov(r0, Operand(1)); // not counting receiver
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00002550 __ InvokeBuiltin(Builtins::DELETE, CALL_JS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002551
mads.s.ager31e71382008-08-13 09:32:07 +00002552 } else if (variable != NULL) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002553 Slot* slot = variable->slot();
2554 if (variable->is_global()) {
2555 LoadGlobal();
mads.s.ager31e71382008-08-13 09:32:07 +00002556 __ mov(r0, Operand(variable->name()));
2557 __ push(r0);
2558 __ mov(r0, Operand(1)); // not counting receiver
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00002559 __ InvokeBuiltin(Builtins::DELETE, CALL_JS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002560
2561 } else if (slot != NULL && slot->type() == Slot::LOOKUP) {
2562 // lookup the context holding the named variable
mads.s.ager31e71382008-08-13 09:32:07 +00002563 __ push(cp);
2564 __ mov(r0, Operand(variable->name()));
2565 __ push(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002566 __ CallRuntime(Runtime::kLookupContext, 2);
2567 // r0: context
mads.s.ager31e71382008-08-13 09:32:07 +00002568 __ push(r0);
2569 __ mov(r0, Operand(variable->name()));
2570 __ push(r0);
2571 __ mov(r0, Operand(1)); // not counting receiver
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00002572 __ InvokeBuiltin(Builtins::DELETE, CALL_JS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002573
mads.s.ager31e71382008-08-13 09:32:07 +00002574 } else {
2575 // Default: Result of deleting non-global, not dynamically
2576 // introduced variables is false.
2577 __ mov(r0, Operand(Factory::false_value()));
2578 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002579
2580 } else {
2581 // Default: Result of deleting expressions is true.
2582 Load(node->expression()); // may have side-effects
mads.s.ager31e71382008-08-13 09:32:07 +00002583 __ pop();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002584 __ mov(r0, Operand(Factory::true_value()));
2585 }
mads.s.ager31e71382008-08-13 09:32:07 +00002586 __ push(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002587
2588 } else if (op == Token::TYPEOF) {
2589 // Special case for loading the typeof expression; see comment on
2590 // LoadTypeofExpression().
2591 LoadTypeofExpression(node->expression());
2592 __ CallRuntime(Runtime::kTypeof, 1);
mads.s.ager31e71382008-08-13 09:32:07 +00002593 __ push(r0); // r0 has result
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002594
2595 } else {
2596 Load(node->expression());
mads.s.ager31e71382008-08-13 09:32:07 +00002597 __ pop(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002598 switch (op) {
2599 case Token::NOT:
2600 case Token::DELETE:
2601 case Token::TYPEOF:
2602 UNREACHABLE(); // handled above
2603 break;
2604
2605 case Token::SUB: {
2606 UnarySubStub stub;
2607 __ CallStub(&stub);
2608 break;
2609 }
2610
2611 case Token::BIT_NOT: {
2612 // smi check
2613 Label smi_label;
2614 Label continue_label;
2615 __ tst(r0, Operand(kSmiTagMask));
2616 __ b(eq, &smi_label);
2617
mads.s.ager31e71382008-08-13 09:32:07 +00002618 __ push(r0);
2619 __ mov(r0, Operand(0)); // not counting receiver
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00002620 __ InvokeBuiltin(Builtins::BIT_NOT, CALL_JS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002621
2622 __ b(&continue_label);
2623 __ bind(&smi_label);
2624 __ mvn(r0, Operand(r0));
2625 __ bic(r0, r0, Operand(kSmiTagMask)); // bit-clear inverted smi-tag
2626 __ bind(&continue_label);
2627 break;
2628 }
2629
2630 case Token::VOID:
2631 // since the stack top is cached in r0, popping and then
2632 // pushing a value can be done by just writing to r0.
2633 __ mov(r0, Operand(Factory::undefined_value()));
2634 break;
2635
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002636 case Token::ADD: {
2637 // Smi check.
2638 Label continue_label;
2639 __ tst(r0, Operand(kSmiTagMask));
2640 __ b(eq, &continue_label);
mads.s.ager31e71382008-08-13 09:32:07 +00002641 __ push(r0);
2642 __ mov(r0, Operand(0)); // not counting receiver
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00002643 __ InvokeBuiltin(Builtins::TO_NUMBER, CALL_JS);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002644 __ bind(&continue_label);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002645 break;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002646 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002647 default:
2648 UNREACHABLE();
2649 }
mads.s.ager31e71382008-08-13 09:32:07 +00002650 __ push(r0); // r0 has result
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002651 }
2652}
2653
2654
ager@chromium.org7c537e22008-10-16 08:43:32 +00002655void CodeGenerator::VisitCountOperation(CountOperation* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002656 Comment cmnt(masm_, "[ CountOperation");
2657
2658 bool is_postfix = node->is_postfix();
2659 bool is_increment = node->op() == Token::INC;
2660
2661 Variable* var = node->expression()->AsVariableProxy()->AsVariable();
2662 bool is_const = (var != NULL && var->mode() == Variable::CONST);
2663
2664 // Postfix: Make room for the result.
mads.s.ager31e71382008-08-13 09:32:07 +00002665 if (is_postfix) {
2666 __ mov(r0, Operand(0));
2667 __ push(r0);
2668 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002669
2670 { Reference target(this, node->expression());
2671 if (target.is_illegal()) return;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002672 target.GetValue(NOT_INSIDE_TYPEOF);
mads.s.ager31e71382008-08-13 09:32:07 +00002673 __ pop(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002674
2675 Label slow, exit;
2676
2677 // Load the value (1) into register r1.
2678 __ mov(r1, Operand(Smi::FromInt(1)));
2679
2680 // Check for smi operand.
2681 __ tst(r0, Operand(kSmiTagMask));
2682 __ b(ne, &slow);
2683
2684 // Postfix: Store the old value as the result.
2685 if (is_postfix) __ str(r0, MemOperand(sp, target.size() * kPointerSize));
2686
2687 // Perform optimistic increment/decrement.
2688 if (is_increment) {
2689 __ add(r0, r0, Operand(r1), SetCC);
2690 } else {
2691 __ sub(r0, r0, Operand(r1), SetCC);
2692 }
2693
2694 // If the increment/decrement didn't overflow, we're done.
2695 __ b(vc, &exit);
2696
2697 // Revert optimistic increment/decrement.
2698 if (is_increment) {
2699 __ sub(r0, r0, Operand(r1));
2700 } else {
2701 __ add(r0, r0, Operand(r1));
2702 }
2703
2704 // Slow case: Convert to number.
2705 __ bind(&slow);
2706
2707 // Postfix: Convert the operand to a number and store it as the result.
2708 if (is_postfix) {
2709 InvokeBuiltinStub stub(InvokeBuiltinStub::ToNumber, 2);
2710 __ CallStub(&stub);
2711 // Store to result (on the stack).
2712 __ str(r0, MemOperand(sp, target.size() * kPointerSize));
2713 }
2714
2715 // Compute the new value by calling the right JavaScript native.
2716 if (is_increment) {
2717 InvokeBuiltinStub stub(InvokeBuiltinStub::Inc, 1);
2718 __ CallStub(&stub);
2719 } else {
2720 InvokeBuiltinStub stub(InvokeBuiltinStub::Dec, 1);
2721 __ CallStub(&stub);
2722 }
2723
2724 // Store the new value in the target if not const.
2725 __ bind(&exit);
mads.s.ager31e71382008-08-13 09:32:07 +00002726 __ push(r0);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002727 if (!is_const) target.SetValue(NOT_CONST_INIT);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002728 }
2729
2730 // Postfix: Discard the new value and use the old.
2731 if (is_postfix) __ pop(r0);
2732}
2733
2734
ager@chromium.org7c537e22008-10-16 08:43:32 +00002735void CodeGenerator::VisitBinaryOperation(BinaryOperation* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002736 Comment cmnt(masm_, "[ BinaryOperation");
2737 Token::Value op = node->op();
2738
2739 // According to ECMA-262 section 11.11, page 58, the binary logical
2740 // operators must yield the result of one of the two expressions
2741 // before any ToBoolean() conversions. This means that the value
2742 // produced by a && or || operator is not necessarily a boolean.
2743
2744 // NOTE: If the left hand side produces a materialized value (not in
2745 // the CC register), we force the right hand side to do the
2746 // same. This is necessary because we may have to branch to the exit
2747 // after evaluating the left hand side (due to the shortcut
2748 // semantics), but the compiler must (statically) know if the result
2749 // of compiling the binary operation is materialized or not.
2750
2751 if (op == Token::AND) {
2752 Label is_true;
2753 LoadCondition(node->left(),
ager@chromium.org7c537e22008-10-16 08:43:32 +00002754 NOT_INSIDE_TYPEOF,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002755 &is_true,
2756 false_target(),
2757 false);
2758 if (has_cc()) {
2759 Branch(false, false_target());
2760
2761 // Evaluate right side expression.
2762 __ bind(&is_true);
2763 LoadCondition(node->right(),
ager@chromium.org7c537e22008-10-16 08:43:32 +00002764 NOT_INSIDE_TYPEOF,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002765 true_target(),
2766 false_target(),
2767 false);
2768
2769 } else {
2770 Label pop_and_continue, exit;
2771
mads.s.ager31e71382008-08-13 09:32:07 +00002772 __ ldr(r0, MemOperand(sp, 0)); // dup the stack top
2773 __ push(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002774 // Avoid popping the result if it converts to 'false' using the
2775 // standard ToBoolean() conversion as described in ECMA-262,
2776 // section 9.2, page 30.
mads.s.ager31e71382008-08-13 09:32:07 +00002777 ToBoolean(&pop_and_continue, &exit);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002778 Branch(false, &exit);
2779
2780 // Pop the result of evaluating the first part.
2781 __ bind(&pop_and_continue);
2782 __ pop(r0);
2783
2784 // Evaluate right side expression.
2785 __ bind(&is_true);
2786 Load(node->right());
2787
2788 // Exit (always with a materialized value).
2789 __ bind(&exit);
2790 }
2791
2792 } else if (op == Token::OR) {
2793 Label is_false;
2794 LoadCondition(node->left(),
ager@chromium.org7c537e22008-10-16 08:43:32 +00002795 NOT_INSIDE_TYPEOF,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002796 true_target(),
2797 &is_false,
2798 false);
2799 if (has_cc()) {
2800 Branch(true, true_target());
2801
2802 // Evaluate right side expression.
2803 __ bind(&is_false);
2804 LoadCondition(node->right(),
ager@chromium.org7c537e22008-10-16 08:43:32 +00002805 NOT_INSIDE_TYPEOF,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002806 true_target(),
2807 false_target(),
2808 false);
2809
2810 } else {
2811 Label pop_and_continue, exit;
2812
mads.s.ager31e71382008-08-13 09:32:07 +00002813 __ ldr(r0, MemOperand(sp, 0));
2814 __ push(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002815 // Avoid popping the result if it converts to 'true' using the
2816 // standard ToBoolean() conversion as described in ECMA-262,
2817 // section 9.2, page 30.
mads.s.ager31e71382008-08-13 09:32:07 +00002818 ToBoolean(&exit, &pop_and_continue);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002819 Branch(true, &exit);
2820
2821 // Pop the result of evaluating the first part.
2822 __ bind(&pop_and_continue);
2823 __ pop(r0);
2824
2825 // Evaluate right side expression.
2826 __ bind(&is_false);
2827 Load(node->right());
2828
2829 // Exit (always with a materialized value).
2830 __ bind(&exit);
2831 }
2832
2833 } else {
2834 // Optimize for the case where (at least) one of the expressions
2835 // is a literal small integer.
2836 Literal* lliteral = node->left()->AsLiteral();
2837 Literal* rliteral = node->right()->AsLiteral();
2838
2839 if (rliteral != NULL && rliteral->handle()->IsSmi()) {
2840 Load(node->left());
2841 SmiOperation(node->op(), rliteral->handle(), false);
2842
2843 } else if (lliteral != NULL && lliteral->handle()->IsSmi()) {
2844 Load(node->right());
2845 SmiOperation(node->op(), lliteral->handle(), true);
2846
2847 } else {
2848 Load(node->left());
2849 Load(node->right());
kasper.lund7276f142008-07-30 08:49:36 +00002850 GenericBinaryOperation(node->op());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002851 }
mads.s.ager31e71382008-08-13 09:32:07 +00002852 __ push(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002853 }
2854}
2855
2856
ager@chromium.org7c537e22008-10-16 08:43:32 +00002857void CodeGenerator::VisitThisFunction(ThisFunction* node) {
mads.s.ager31e71382008-08-13 09:32:07 +00002858 __ ldr(r0, FunctionOperand());
2859 __ push(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002860}
2861
2862
ager@chromium.org7c537e22008-10-16 08:43:32 +00002863void CodeGenerator::VisitCompareOperation(CompareOperation* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002864 Comment cmnt(masm_, "[ CompareOperation");
2865
2866 // Get the expressions from the node.
2867 Expression* left = node->left();
2868 Expression* right = node->right();
2869 Token::Value op = node->op();
2870
2871 // NOTE: To make null checks efficient, we check if either left or
2872 // right is the literal 'null'. If so, we optimize the code by
2873 // inlining a null check instead of calling the (very) general
2874 // runtime routine for checking equality.
2875
2876 bool left_is_null =
2877 left->AsLiteral() != NULL && left->AsLiteral()->IsNull();
2878 bool right_is_null =
2879 right->AsLiteral() != NULL && right->AsLiteral()->IsNull();
2880
2881 if (op == Token::EQ || op == Token::EQ_STRICT) {
2882 // The 'null' value is only equal to 'null' or 'undefined'.
2883 if (left_is_null || right_is_null) {
2884 Load(left_is_null ? right : left);
2885 Label exit, undetectable;
mads.s.ager31e71382008-08-13 09:32:07 +00002886 __ pop(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002887 __ cmp(r0, Operand(Factory::null_value()));
2888
2889 // The 'null' value is only equal to 'undefined' if using
2890 // non-strict comparisons.
2891 if (op != Token::EQ_STRICT) {
2892 __ b(eq, &exit);
2893 __ cmp(r0, Operand(Factory::undefined_value()));
2894
2895 // NOTE: it can be undetectable object.
2896 __ b(eq, &exit);
2897 __ tst(r0, Operand(kSmiTagMask));
2898
2899 __ b(ne, &undetectable);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002900 __ b(false_target());
2901
2902 __ bind(&undetectable);
2903 __ ldr(r1, FieldMemOperand(r0, HeapObject::kMapOffset));
2904 __ ldrb(r2, FieldMemOperand(r1, Map::kBitFieldOffset));
2905 __ and_(r2, r2, Operand(1 << Map::kIsUndetectable));
2906 __ cmp(r2, Operand(1 << Map::kIsUndetectable));
2907 }
2908
2909 __ bind(&exit);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002910
2911 cc_reg_ = eq;
2912 return;
2913 }
2914 }
2915
2916
2917 // NOTE: To make typeof testing for natives implemented in
2918 // JavaScript really efficient, we generate special code for
2919 // expressions of the form: 'typeof <expression> == <string>'.
2920
2921 UnaryOperation* operation = left->AsUnaryOperation();
2922 if ((op == Token::EQ || op == Token::EQ_STRICT) &&
2923 (operation != NULL && operation->op() == Token::TYPEOF) &&
2924 (right->AsLiteral() != NULL &&
2925 right->AsLiteral()->handle()->IsString())) {
2926 Handle<String> check(String::cast(*right->AsLiteral()->handle()));
2927
mads.s.ager31e71382008-08-13 09:32:07 +00002928 // Load the operand, move it to register r1.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002929 LoadTypeofExpression(operation->expression());
mads.s.ager31e71382008-08-13 09:32:07 +00002930 __ pop(r1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002931
2932 if (check->Equals(Heap::number_symbol())) {
2933 __ tst(r1, Operand(kSmiTagMask));
2934 __ b(eq, true_target());
2935 __ ldr(r1, FieldMemOperand(r1, HeapObject::kMapOffset));
2936 __ cmp(r1, Operand(Factory::heap_number_map()));
2937 cc_reg_ = eq;
2938
2939 } else if (check->Equals(Heap::string_symbol())) {
2940 __ tst(r1, Operand(kSmiTagMask));
2941 __ b(eq, false_target());
2942
2943 __ ldr(r1, FieldMemOperand(r1, HeapObject::kMapOffset));
2944
2945 // NOTE: it might be an undetectable string object
2946 __ ldrb(r2, FieldMemOperand(r1, Map::kBitFieldOffset));
2947 __ and_(r2, r2, Operand(1 << Map::kIsUndetectable));
2948 __ cmp(r2, Operand(1 << Map::kIsUndetectable));
2949 __ b(eq, false_target());
2950
2951 __ ldrb(r2, FieldMemOperand(r1, Map::kInstanceTypeOffset));
2952 __ cmp(r2, Operand(FIRST_NONSTRING_TYPE));
2953 cc_reg_ = lt;
2954
2955 } else if (check->Equals(Heap::boolean_symbol())) {
2956 __ cmp(r1, Operand(Factory::true_value()));
2957 __ b(eq, true_target());
2958 __ cmp(r1, Operand(Factory::false_value()));
2959 cc_reg_ = eq;
2960
2961 } else if (check->Equals(Heap::undefined_symbol())) {
2962 __ cmp(r1, Operand(Factory::undefined_value()));
2963 __ b(eq, true_target());
2964
2965 __ tst(r1, Operand(kSmiTagMask));
2966 __ b(eq, false_target());
2967
2968 // NOTE: it can be undetectable object.
2969 __ ldr(r1, FieldMemOperand(r1, HeapObject::kMapOffset));
2970 __ ldrb(r2, FieldMemOperand(r1, Map::kBitFieldOffset));
2971 __ and_(r2, r2, Operand(1 << Map::kIsUndetectable));
2972 __ cmp(r2, Operand(1 << Map::kIsUndetectable));
2973
2974 cc_reg_ = eq;
2975
2976 } else if (check->Equals(Heap::function_symbol())) {
2977 __ tst(r1, Operand(kSmiTagMask));
2978 __ b(eq, false_target());
2979 __ ldr(r1, FieldMemOperand(r1, HeapObject::kMapOffset));
2980 __ ldrb(r1, FieldMemOperand(r1, Map::kInstanceTypeOffset));
2981 __ cmp(r1, Operand(JS_FUNCTION_TYPE));
2982 cc_reg_ = eq;
2983
2984 } else if (check->Equals(Heap::object_symbol())) {
2985 __ tst(r1, Operand(kSmiTagMask));
2986 __ b(eq, false_target());
2987
2988 __ ldr(r2, FieldMemOperand(r1, HeapObject::kMapOffset));
2989 __ cmp(r1, Operand(Factory::null_value()));
2990 __ b(eq, true_target());
2991
2992 // NOTE: it might be an undetectable object.
2993 __ ldrb(r1, FieldMemOperand(r2, Map::kBitFieldOffset));
2994 __ and_(r1, r1, Operand(1 << Map::kIsUndetectable));
2995 __ cmp(r1, Operand(1 << Map::kIsUndetectable));
2996 __ b(eq, false_target());
2997
2998 __ ldrb(r2, FieldMemOperand(r2, Map::kInstanceTypeOffset));
2999 __ cmp(r2, Operand(FIRST_JS_OBJECT_TYPE));
3000 __ b(lt, false_target());
3001 __ cmp(r2, Operand(LAST_JS_OBJECT_TYPE));
3002 cc_reg_ = le;
3003
3004 } else {
3005 // Uncommon case: Typeof testing against a string literal that
3006 // is never returned from the typeof operator.
3007 __ b(false_target());
3008 }
3009 return;
3010 }
3011
3012 Load(left);
3013 Load(right);
3014 switch (op) {
3015 case Token::EQ:
3016 Comparison(eq, false);
3017 break;
3018
3019 case Token::LT:
3020 Comparison(lt);
3021 break;
3022
3023 case Token::GT:
3024 Comparison(gt);
3025 break;
3026
3027 case Token::LTE:
3028 Comparison(le);
3029 break;
3030
3031 case Token::GTE:
3032 Comparison(ge);
3033 break;
3034
3035 case Token::EQ_STRICT:
3036 Comparison(eq, true);
3037 break;
3038
3039 case Token::IN:
mads.s.ager31e71382008-08-13 09:32:07 +00003040 __ mov(r0, Operand(1)); // not counting receiver
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00003041 __ InvokeBuiltin(Builtins::IN, CALL_JS);
mads.s.ager31e71382008-08-13 09:32:07 +00003042 __ push(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003043 break;
3044
3045 case Token::INSTANCEOF:
mads.s.ager31e71382008-08-13 09:32:07 +00003046 __ mov(r0, Operand(1)); // not counting receiver
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00003047 __ InvokeBuiltin(Builtins::INSTANCE_OF, CALL_JS);
ager@chromium.org7c537e22008-10-16 08:43:32 +00003048 __ tst(r0, Operand(r0));
3049 cc_reg_ = eq;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003050 break;
3051
3052 default:
3053 UNREACHABLE();
3054 }
3055}
3056
3057
ager@chromium.org7c537e22008-10-16 08:43:32 +00003058void CodeGenerator::RecordStatementPosition(Node* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003059 if (FLAG_debug_info) {
3060 int statement_pos = node->statement_pos();
ager@chromium.org236ad962008-09-25 09:45:57 +00003061 if (statement_pos == RelocInfo::kNoPosition) return;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003062 __ RecordStatementPosition(statement_pos);
3063 }
3064}
3065
3066
ager@chromium.org7c537e22008-10-16 08:43:32 +00003067void CodeGenerator::EnterJSFrame() {
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00003068#if defined(DEBUG)
3069 { Label done, fail;
3070 __ tst(r1, Operand(kSmiTagMask));
3071 __ b(eq, &fail);
3072 __ ldr(r2, FieldMemOperand(r1, HeapObject::kMapOffset));
3073 __ ldrb(r2, FieldMemOperand(r2, Map::kInstanceTypeOffset));
3074 __ cmp(r2, Operand(JS_FUNCTION_TYPE));
3075 __ b(eq, &done);
3076 __ bind(&fail);
ager@chromium.org7c537e22008-10-16 08:43:32 +00003077 __ stop("CodeGenerator::EnterJSFrame - r1 not a function");
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00003078 __ bind(&done);
3079 }
3080#endif // DEBUG
3081
3082 __ stm(db_w, sp, r1.bit() | cp.bit() | fp.bit() | lr.bit());
3083 __ add(fp, sp, Operand(2 * kPointerSize)); // Adjust FP to point to saved FP.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003084}
3085
3086
ager@chromium.org7c537e22008-10-16 08:43:32 +00003087void CodeGenerator::ExitJSFrame() {
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00003088 // Drop the execution stack down to the frame pointer and restore the caller
3089 // frame pointer and return address.
3090 __ mov(sp, fp);
3091 __ ldm(ia_w, sp, fp.bit() | lr.bit());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003092}
3093
3094
3095#undef __
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003096#define __ masm->
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003097
ager@chromium.org7c537e22008-10-16 08:43:32 +00003098Handle<String> Reference::GetName() {
3099 ASSERT(type_ == NAMED);
3100 Property* property = expression_->AsProperty();
3101 if (property == NULL) {
3102 // Global variable reference treated as a named property reference.
3103 VariableProxy* proxy = expression_->AsVariableProxy();
3104 ASSERT(proxy->AsVariable() != NULL);
3105 ASSERT(proxy->AsVariable()->is_global());
3106 return proxy->name();
3107 } else {
3108 Literal* raw_name = property->key()->AsLiteral();
3109 ASSERT(raw_name != NULL);
3110 return Handle<String>(String::cast(*raw_name->handle()));
3111 }
3112}
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003113
ager@chromium.org7c537e22008-10-16 08:43:32 +00003114
3115void Reference::GetValue(TypeofState typeof_state) {
3116 ASSERT(!is_illegal());
3117 ASSERT(!cgen_->has_cc());
3118 MacroAssembler* masm = cgen_->masm();
3119 Property* property = expression_->AsProperty();
3120 if (property != NULL) {
3121 __ RecordPosition(property->position());
3122 }
3123
3124 switch (type_) {
3125 case SLOT: {
3126 Comment cmnt(masm, "[ Load from Slot");
3127 Slot* slot = expression_->AsVariableProxy()->AsVariable()->slot();
3128 ASSERT(slot != NULL);
3129 cgen_->LoadFromSlot(slot, typeof_state);
3130 break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003131 }
3132
ager@chromium.org7c537e22008-10-16 08:43:32 +00003133 case NAMED: {
3134 // TODO(1241834): Make sure that this it is safe to ignore the
3135 // distinction between expressions in a typeof and not in a typeof. If
3136 // there is a chance that reference errors can be thrown below, we
3137 // must distinguish between the two kinds of loads (typeof expression
3138 // loads must not throw a reference error).
3139 Comment cmnt(masm, "[ Load from named Property");
3140 // Setup the name register.
3141 Handle<String> name(GetName());
3142 __ mov(r2, Operand(name));
3143 Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize));
3144
3145 Variable* var = expression_->AsVariableProxy()->AsVariable();
3146 if (var != NULL) {
3147 ASSERT(var->is_global());
3148 __ Call(ic, RelocInfo::CODE_TARGET_CONTEXT);
3149 } else {
3150 __ Call(ic, RelocInfo::CODE_TARGET);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003151 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00003152 __ push(r0);
3153 break;
3154 }
3155
3156 case KEYED: {
3157 // TODO(1241834): Make sure that this it is safe to ignore the
3158 // distinction between expressions in a typeof and not in a typeof.
3159 Comment cmnt(masm, "[ Load from keyed Property");
3160 ASSERT(property != NULL);
3161 // TODO(1224671): Implement inline caching for keyed loads as on ia32.
3162 GetPropertyStub stub;
3163 __ CallStub(&stub);
3164 __ push(r0);
3165 break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003166 }
3167
3168 default:
3169 UNREACHABLE();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003170 }
3171}
3172
3173
ager@chromium.org7c537e22008-10-16 08:43:32 +00003174void Reference::SetValue(InitState init_state) {
3175 ASSERT(!is_illegal());
3176 ASSERT(!cgen_->has_cc());
3177 MacroAssembler* masm = cgen_->masm();
3178 Property* property = expression_->AsProperty();
3179 if (property != NULL) {
3180 __ RecordPosition(property->position());
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003181 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003182
ager@chromium.org7c537e22008-10-16 08:43:32 +00003183 switch (type_) {
3184 case SLOT: {
3185 Comment cmnt(masm, "[ Store to Slot");
3186 Slot* slot = expression_->AsVariableProxy()->AsVariable()->slot();
3187 ASSERT(slot != NULL);
3188 if (slot->type() == Slot::LOOKUP) {
3189 ASSERT(slot->var()->mode() == Variable::DYNAMIC);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003190
ager@chromium.org7c537e22008-10-16 08:43:32 +00003191 // For now, just do a runtime call.
3192 __ push(cp);
3193 __ mov(r0, Operand(slot->var()->name()));
3194 __ push(r0);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003195
ager@chromium.org7c537e22008-10-16 08:43:32 +00003196 if (init_state == CONST_INIT) {
3197 // Same as the case for a normal store, but ignores attribute
3198 // (e.g. READ_ONLY) of context slot so that we can initialize
3199 // const properties (introduced via eval("const foo = (some
3200 // expr);")). Also, uses the current function context instead of
3201 // the top context.
3202 //
3203 // Note that we must declare the foo upon entry of eval(), via a
3204 // context slot declaration, but we cannot initialize it at the
3205 // same time, because the const declaration may be at the end of
3206 // the eval code (sigh...) and the const variable may have been
3207 // used before (where its value is 'undefined'). Thus, we can only
3208 // do the initialization when we actually encounter the expression
3209 // and when the expression operands are defined and valid, and
3210 // thus we need the split into 2 operations: declaration of the
3211 // context slot followed by initialization.
3212 __ CallRuntime(Runtime::kInitializeConstContextSlot, 3);
3213 } else {
3214 __ CallRuntime(Runtime::kStoreContextSlot, 3);
3215 }
3216 // Storing a variable must keep the (new) value on the expression
3217 // stack. This is necessary for compiling assignment expressions.
3218 __ push(r0);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003219
ager@chromium.org7c537e22008-10-16 08:43:32 +00003220 } else {
3221 ASSERT(slot->var()->mode() != Variable::DYNAMIC);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003222
ager@chromium.org7c537e22008-10-16 08:43:32 +00003223 Label exit;
3224 if (init_state == CONST_INIT) {
3225 ASSERT(slot->var()->mode() == Variable::CONST);
3226 // Only the first const initialization must be executed (the slot
3227 // still contains 'the hole' value). When the assignment is
3228 // executed, the code is identical to a normal store (see below).
3229 Comment cmnt(masm, "[ Init const");
3230 __ ldr(r2, cgen_->SlotOperand(slot, r2));
3231 __ cmp(r2, Operand(Factory::the_hole_value()));
3232 __ b(ne, &exit);
3233 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003234
ager@chromium.org7c537e22008-10-16 08:43:32 +00003235 // We must execute the store. Storing a variable must keep the
3236 // (new) value on the stack. This is necessary for compiling
3237 // assignment expressions.
3238 //
3239 // Note: We will reach here even with slot->var()->mode() ==
3240 // Variable::CONST because of const declarations which will
3241 // initialize consts to 'the hole' value and by doing so, end up
3242 // calling this code. r2 may be loaded with context; used below in
3243 // RecordWrite.
3244 __ pop(r0);
3245 __ str(r0, cgen_->SlotOperand(slot, r2));
3246 __ push(r0);
3247 if (slot->type() == Slot::CONTEXT) {
3248 // Skip write barrier if the written value is a smi.
3249 __ tst(r0, Operand(kSmiTagMask));
3250 __ b(eq, &exit);
3251 // r2 is loaded with context when calling SlotOperand above.
3252 int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize;
3253 __ mov(r3, Operand(offset));
3254 __ RecordWrite(r2, r3, r1);
3255 }
3256 // If we definitely did not jump over the assignment, we do not need
3257 // to bind the exit label. Doing so can defeat peephole
3258 // optimization.
3259 if (init_state == CONST_INIT || slot->type() == Slot::CONTEXT) {
3260 __ bind(&exit);
3261 }
3262 }
3263 break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003264 }
3265
ager@chromium.org7c537e22008-10-16 08:43:32 +00003266 case NAMED: {
3267 Comment cmnt(masm, "[ Store to named Property");
3268 // Call the appropriate IC code.
3269 __ pop(r0); // value
3270 // Setup the name register.
3271 Handle<String> name(GetName());
3272 __ mov(r2, Operand(name));
3273 Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
3274 __ Call(ic, RelocInfo::CODE_TARGET);
3275 __ push(r0);
3276 break;
3277 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003278
ager@chromium.org7c537e22008-10-16 08:43:32 +00003279 case KEYED: {
3280 Comment cmnt(masm, "[ Store to keyed Property");
3281 Property* property = expression_->AsProperty();
3282 ASSERT(property != NULL);
3283 __ RecordPosition(property->position());
3284 __ pop(r0); // value
3285 SetPropertyStub stub;
3286 __ CallStub(&stub);
3287 __ push(r0);
3288 break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003289 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00003290
3291 default:
3292 UNREACHABLE();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003293 }
3294}
3295
3296
3297void GetPropertyStub::Generate(MacroAssembler* masm) {
3298 // sp[0]: key
3299 // sp[1]: receiver
3300 Label slow, fast;
3301 // Get the key and receiver object from the stack.
3302 __ ldm(ia, sp, r0.bit() | r1.bit());
3303 // Check that the key is a smi.
3304 __ tst(r0, Operand(kSmiTagMask));
3305 __ b(ne, &slow);
3306 __ mov(r0, Operand(r0, ASR, kSmiTagSize));
3307 // Check that the object isn't a smi.
3308 __ tst(r1, Operand(kSmiTagMask));
3309 __ b(eq, &slow);
3310
3311 // Check that the object is some kind of JS object EXCEPT JS Value type.
3312 // In the case that the object is a value-wrapper object,
3313 // we enter the runtime system to make sure that indexing into string
3314 // objects work as intended.
3315 ASSERT(JS_OBJECT_TYPE > JS_VALUE_TYPE);
3316 __ ldr(r2, FieldMemOperand(r1, HeapObject::kMapOffset));
3317 __ ldrb(r2, FieldMemOperand(r2, Map::kInstanceTypeOffset));
3318 __ cmp(r2, Operand(JS_OBJECT_TYPE));
3319 __ b(lt, &slow);
3320
3321 // Get the elements array of the object.
3322 __ ldr(r1, FieldMemOperand(r1, JSObject::kElementsOffset));
3323 // Check that the object is in fast mode (not dictionary).
3324 __ ldr(r3, FieldMemOperand(r1, HeapObject::kMapOffset));
3325 __ cmp(r3, Operand(Factory::hash_table_map()));
3326 __ b(eq, &slow);
3327 // Check that the key (index) is within bounds.
3328 __ ldr(r3, FieldMemOperand(r1, Array::kLengthOffset));
3329 __ cmp(r0, Operand(r3));
3330 __ b(lo, &fast);
3331
3332 // Slow case: Push extra copies of the arguments (2).
3333 __ bind(&slow);
3334 __ ldm(ia, sp, r0.bit() | r1.bit());
3335 __ stm(db_w, sp, r0.bit() | r1.bit());
3336 // Do tail-call to runtime routine.
3337 __ TailCallRuntime(ExternalReference(Runtime::kGetProperty), 2);
3338
3339 // Fast case: Do the load.
3340 __ bind(&fast);
3341 __ add(r3, r1, Operand(Array::kHeaderSize - kHeapObjectTag));
3342 __ ldr(r0, MemOperand(r3, r0, LSL, kPointerSizeLog2));
3343 __ cmp(r0, Operand(Factory::the_hole_value()));
3344 // In case the loaded value is the_hole we have to consult GetProperty
3345 // to ensure the prototype chain is searched.
3346 __ b(eq, &slow);
3347
3348 __ StubReturn(1);
3349}
3350
3351
3352void SetPropertyStub::Generate(MacroAssembler* masm) {
3353 // r0 : value
3354 // sp[0] : key
3355 // sp[1] : receiver
3356
3357 Label slow, fast, array, extra, exit;
3358 // Get the key and the object from the stack.
3359 __ ldm(ia, sp, r1.bit() | r3.bit()); // r1 = key, r3 = receiver
3360 // Check that the key is a smi.
3361 __ tst(r1, Operand(kSmiTagMask));
3362 __ b(ne, &slow);
3363 // Check that the object isn't a smi.
3364 __ tst(r3, Operand(kSmiTagMask));
3365 __ b(eq, &slow);
3366 // Get the type of the object from its map.
3367 __ ldr(r2, FieldMemOperand(r3, HeapObject::kMapOffset));
3368 __ ldrb(r2, FieldMemOperand(r2, Map::kInstanceTypeOffset));
3369 // Check if the object is a JS array or not.
3370 __ cmp(r2, Operand(JS_ARRAY_TYPE));
3371 __ b(eq, &array);
3372 // Check that the object is some kind of JS object.
3373 __ cmp(r2, Operand(FIRST_JS_OBJECT_TYPE));
3374 __ b(lt, &slow);
3375
3376
3377 // Object case: Check key against length in the elements array.
3378 __ ldr(r3, FieldMemOperand(r3, JSObject::kElementsOffset));
3379 // Check that the object is in fast mode (not dictionary).
3380 __ ldr(r2, FieldMemOperand(r3, HeapObject::kMapOffset));
3381 __ cmp(r2, Operand(Factory::hash_table_map()));
3382 __ b(eq, &slow);
3383 // Untag the key (for checking against untagged length in the fixed array).
3384 __ mov(r1, Operand(r1, ASR, kSmiTagSize));
3385 // Compute address to store into and check array bounds.
3386 __ add(r2, r3, Operand(Array::kHeaderSize - kHeapObjectTag));
3387 __ add(r2, r2, Operand(r1, LSL, kPointerSizeLog2));
3388 __ ldr(ip, FieldMemOperand(r3, Array::kLengthOffset));
3389 __ cmp(r1, Operand(ip));
3390 __ b(lo, &fast);
3391
3392
3393 // Slow case: Push extra copies of the arguments (3).
3394 __ bind(&slow);
3395 __ ldm(ia, sp, r1.bit() | r3.bit()); // r0 == value, r1 == key, r3 == object
3396 __ stm(db_w, sp, r0.bit() | r1.bit() | r3.bit());
3397 // Do tail-call to runtime routine.
3398 __ TailCallRuntime(ExternalReference(Runtime::kSetProperty), 3);
3399
3400
3401 // Extra capacity case: Check if there is extra capacity to
3402 // perform the store and update the length. Used for adding one
3403 // element to the array by writing to array[array.length].
3404 // r0 == value, r1 == key, r2 == elements, r3 == object
3405 __ bind(&extra);
3406 __ b(ne, &slow); // do not leave holes in the array
3407 __ mov(r1, Operand(r1, ASR, kSmiTagSize)); // untag
3408 __ ldr(ip, FieldMemOperand(r2, Array::kLengthOffset));
3409 __ cmp(r1, Operand(ip));
3410 __ b(hs, &slow);
3411 __ mov(r1, Operand(r1, LSL, kSmiTagSize)); // restore tag
3412 __ add(r1, r1, Operand(1 << kSmiTagSize)); // and increment
3413 __ str(r1, FieldMemOperand(r3, JSArray::kLengthOffset));
3414 __ mov(r3, Operand(r2));
3415 // NOTE: Computing the address to store into must take the fact
3416 // that the key has been incremented into account.
3417 int displacement = Array::kHeaderSize - kHeapObjectTag -
3418 ((1 << kSmiTagSize) * 2);
3419 __ add(r2, r2, Operand(displacement));
3420 __ add(r2, r2, Operand(r1, LSL, kPointerSizeLog2 - kSmiTagSize));
3421 __ b(&fast);
3422
3423
3424 // Array case: Get the length and the elements array from the JS
3425 // array. Check that the array is in fast mode; if it is the
3426 // length is always a smi.
3427 // r0 == value, r3 == object
3428 __ bind(&array);
3429 __ ldr(r2, FieldMemOperand(r3, JSObject::kElementsOffset));
3430 __ ldr(r1, FieldMemOperand(r2, HeapObject::kMapOffset));
3431 __ cmp(r1, Operand(Factory::hash_table_map()));
3432 __ b(eq, &slow);
3433
3434 // Check the key against the length in the array, compute the
3435 // address to store into and fall through to fast case.
3436 __ ldr(r1, MemOperand(sp));
3437 // r0 == value, r1 == key, r2 == elements, r3 == object.
3438 __ ldr(ip, FieldMemOperand(r3, JSArray::kLengthOffset));
3439 __ cmp(r1, Operand(ip));
3440 __ b(hs, &extra);
3441 __ mov(r3, Operand(r2));
3442 __ add(r2, r2, Operand(Array::kHeaderSize - kHeapObjectTag));
3443 __ add(r2, r2, Operand(r1, LSL, kPointerSizeLog2 - kSmiTagSize));
3444
3445
3446 // Fast case: Do the store.
3447 // r0 == value, r2 == address to store into, r3 == elements
3448 __ bind(&fast);
3449 __ str(r0, MemOperand(r2));
3450 // Skip write barrier if the written value is a smi.
3451 __ tst(r0, Operand(kSmiTagMask));
3452 __ b(eq, &exit);
3453 // Update write barrier for the elements array address.
3454 __ sub(r1, r2, Operand(r3));
3455 __ RecordWrite(r3, r1, r2);
3456 __ bind(&exit);
3457 __ StubReturn(1);
3458}
3459
3460
3461void GenericBinaryOpStub::Generate(MacroAssembler* masm) {
3462 // r1 : x
3463 // r0 : y
3464 // result : r0
3465
3466 switch (op_) {
3467 case Token::ADD: {
3468 Label slow, exit;
3469 // fast path
3470 __ orr(r2, r1, Operand(r0)); // r2 = x | y;
3471 __ add(r0, r1, Operand(r0), SetCC); // add y optimistically
3472 // go slow-path in case of overflow
3473 __ b(vs, &slow);
3474 // go slow-path in case of non-smi operands
3475 ASSERT(kSmiTag == 0); // adjust code below
3476 __ tst(r2, Operand(kSmiTagMask));
3477 __ b(eq, &exit);
3478 // slow path
3479 __ bind(&slow);
3480 __ sub(r0, r0, Operand(r1)); // revert optimistic add
3481 __ push(r1);
3482 __ push(r0);
3483 __ mov(r0, Operand(1)); // set number of arguments
3484 __ InvokeBuiltin(Builtins::ADD, JUMP_JS);
3485 // done
3486 __ bind(&exit);
3487 break;
3488 }
3489
3490 case Token::SUB: {
3491 Label slow, exit;
3492 // fast path
3493 __ orr(r2, r1, Operand(r0)); // r2 = x | y;
3494 __ sub(r3, r1, Operand(r0), SetCC); // subtract y optimistically
3495 // go slow-path in case of overflow
3496 __ b(vs, &slow);
3497 // go slow-path in case of non-smi operands
3498 ASSERT(kSmiTag == 0); // adjust code below
3499 __ tst(r2, Operand(kSmiTagMask));
3500 __ mov(r0, Operand(r3), LeaveCC, eq); // conditionally set r0 to result
3501 __ b(eq, &exit);
3502 // slow path
3503 __ bind(&slow);
3504 __ push(r1);
3505 __ push(r0);
3506 __ mov(r0, Operand(1)); // set number of arguments
3507 __ InvokeBuiltin(Builtins::SUB, JUMP_JS);
3508 // done
3509 __ bind(&exit);
3510 break;
3511 }
3512
3513 case Token::MUL: {
3514 Label slow, exit;
3515 // tag check
3516 __ orr(r2, r1, Operand(r0)); // r2 = x | y;
3517 ASSERT(kSmiTag == 0); // adjust code below
3518 __ tst(r2, Operand(kSmiTagMask));
3519 __ b(ne, &slow);
3520 // remove tag from one operand (but keep sign), so that result is smi
3521 __ mov(ip, Operand(r0, ASR, kSmiTagSize));
3522 // do multiplication
3523 __ smull(r3, r2, r1, ip); // r3 = lower 32 bits of ip*r1
3524 // go slow on overflows (overflow bit is not set)
3525 __ mov(ip, Operand(r3, ASR, 31));
3526 __ cmp(ip, Operand(r2)); // no overflow if higher 33 bits are identical
3527 __ b(ne, &slow);
3528 // go slow on zero result to handle -0
3529 __ tst(r3, Operand(r3));
3530 __ mov(r0, Operand(r3), LeaveCC, ne);
3531 __ b(ne, &exit);
3532 // slow case
3533 __ bind(&slow);
3534 __ push(r1);
3535 __ push(r0);
3536 __ mov(r0, Operand(1)); // set number of arguments
3537 __ InvokeBuiltin(Builtins::MUL, JUMP_JS);
3538 // done
3539 __ bind(&exit);
3540 break;
3541 }
3542
3543 case Token::BIT_OR:
3544 case Token::BIT_AND:
3545 case Token::BIT_XOR: {
3546 Label slow, exit;
3547 // tag check
3548 __ orr(r2, r1, Operand(r0)); // r2 = x | y;
3549 ASSERT(kSmiTag == 0); // adjust code below
3550 __ tst(r2, Operand(kSmiTagMask));
3551 __ b(ne, &slow);
3552 switch (op_) {
3553 case Token::BIT_OR: __ orr(r0, r0, Operand(r1)); break;
3554 case Token::BIT_AND: __ and_(r0, r0, Operand(r1)); break;
3555 case Token::BIT_XOR: __ eor(r0, r0, Operand(r1)); break;
3556 default: UNREACHABLE();
3557 }
3558 __ b(&exit);
3559 __ bind(&slow);
3560 __ push(r1); // restore stack
3561 __ push(r0);
3562 __ mov(r0, Operand(1)); // 1 argument (not counting receiver).
3563 switch (op_) {
3564 case Token::BIT_OR:
3565 __ InvokeBuiltin(Builtins::BIT_OR, JUMP_JS);
3566 break;
3567 case Token::BIT_AND:
3568 __ InvokeBuiltin(Builtins::BIT_AND, JUMP_JS);
3569 break;
3570 case Token::BIT_XOR:
3571 __ InvokeBuiltin(Builtins::BIT_XOR, JUMP_JS);
3572 break;
3573 default:
3574 UNREACHABLE();
3575 }
3576 __ bind(&exit);
3577 break;
3578 }
3579
3580 case Token::SHL:
3581 case Token::SHR:
3582 case Token::SAR: {
3583 Label slow, exit;
3584 // tag check
3585 __ orr(r2, r1, Operand(r0)); // r2 = x | y;
3586 ASSERT(kSmiTag == 0); // adjust code below
3587 __ tst(r2, Operand(kSmiTagMask));
3588 __ b(ne, &slow);
3589 // remove tags from operands (but keep sign)
3590 __ mov(r3, Operand(r1, ASR, kSmiTagSize)); // x
3591 __ mov(r2, Operand(r0, ASR, kSmiTagSize)); // y
3592 // use only the 5 least significant bits of the shift count
3593 __ and_(r2, r2, Operand(0x1f));
3594 // perform operation
3595 switch (op_) {
3596 case Token::SAR:
3597 __ mov(r3, Operand(r3, ASR, r2));
3598 // no checks of result necessary
3599 break;
3600
3601 case Token::SHR:
3602 __ mov(r3, Operand(r3, LSR, r2));
3603 // check that the *unsigned* result fits in a smi
3604 // neither of the two high-order bits can be set:
3605 // - 0x80000000: high bit would be lost when smi tagging
3606 // - 0x40000000: this number would convert to negative when
3607 // smi tagging these two cases can only happen with shifts
3608 // by 0 or 1 when handed a valid smi
3609 __ and_(r2, r3, Operand(0xc0000000), SetCC);
3610 __ b(ne, &slow);
3611 break;
3612
3613 case Token::SHL:
3614 __ mov(r3, Operand(r3, LSL, r2));
3615 // check that the *signed* result fits in a smi
3616 __ add(r2, r3, Operand(0x40000000), SetCC);
3617 __ b(mi, &slow);
3618 break;
3619
3620 default: UNREACHABLE();
3621 }
3622 // tag result and store it in r0
3623 ASSERT(kSmiTag == 0); // adjust code below
3624 __ mov(r0, Operand(r3, LSL, kSmiTagSize));
3625 __ b(&exit);
3626 // slow case
3627 __ bind(&slow);
3628 __ push(r1); // restore stack
3629 __ push(r0);
3630 __ mov(r0, Operand(1)); // 1 argument (not counting receiver).
3631 switch (op_) {
3632 case Token::SAR: __ InvokeBuiltin(Builtins::SAR, JUMP_JS); break;
3633 case Token::SHR: __ InvokeBuiltin(Builtins::SHR, JUMP_JS); break;
3634 case Token::SHL: __ InvokeBuiltin(Builtins::SHL, JUMP_JS); break;
3635 default: UNREACHABLE();
3636 }
3637 __ bind(&exit);
3638 break;
3639 }
3640
3641 default: UNREACHABLE();
3642 }
3643 __ Ret();
3644}
3645
3646
3647void StackCheckStub::Generate(MacroAssembler* masm) {
3648 Label within_limit;
3649 __ mov(ip, Operand(ExternalReference::address_of_stack_guard_limit()));
3650 __ ldr(ip, MemOperand(ip));
3651 __ cmp(sp, Operand(ip));
3652 __ b(hs, &within_limit);
3653 // Do tail-call to runtime routine.
3654 __ push(r0);
3655 __ TailCallRuntime(ExternalReference(Runtime::kStackGuard), 1);
3656 __ bind(&within_limit);
3657
3658 __ StubReturn(1);
3659}
3660
3661
3662void UnarySubStub::Generate(MacroAssembler* masm) {
3663 Label undo;
3664 Label slow;
3665 Label done;
3666
3667 // Enter runtime system if the value is not a smi.
3668 __ tst(r0, Operand(kSmiTagMask));
3669 __ b(ne, &slow);
3670
3671 // Enter runtime system if the value of the expression is zero
3672 // to make sure that we switch between 0 and -0.
3673 __ cmp(r0, Operand(0));
3674 __ b(eq, &slow);
3675
3676 // The value of the expression is a smi that is not zero. Try
3677 // optimistic subtraction '0 - value'.
3678 __ rsb(r1, r0, Operand(0), SetCC);
3679 __ b(vs, &slow);
3680
3681 // If result is a smi we are done.
3682 __ tst(r1, Operand(kSmiTagMask));
3683 __ mov(r0, Operand(r1), LeaveCC, eq); // conditionally set r0 to result
3684 __ b(eq, &done);
3685
3686 // Enter runtime system.
3687 __ bind(&slow);
3688 __ push(r0);
3689 __ mov(r0, Operand(0)); // set number of arguments
3690 __ InvokeBuiltin(Builtins::UNARY_MINUS, JUMP_JS);
3691
3692 __ bind(&done);
3693 __ StubReturn(1);
3694}
3695
3696
3697void InvokeBuiltinStub::Generate(MacroAssembler* masm) {
3698 __ push(r0);
3699 __ mov(r0, Operand(0)); // set number of arguments
3700 switch (kind_) {
3701 case ToNumber: __ InvokeBuiltin(Builtins::TO_NUMBER, JUMP_JS); break;
3702 case Inc: __ InvokeBuiltin(Builtins::INC, JUMP_JS); break;
3703 case Dec: __ InvokeBuiltin(Builtins::DEC, JUMP_JS); break;
3704 default: UNREACHABLE();
3705 }
3706 __ StubReturn(argc_);
3707}
3708
3709
3710void CEntryStub::GenerateThrowTOS(MacroAssembler* masm) {
3711 // r0 holds exception
3712 ASSERT(StackHandlerConstants::kSize == 6 * kPointerSize); // adjust this code
3713 __ mov(r3, Operand(ExternalReference(Top::k_handler_address)));
3714 __ ldr(sp, MemOperand(r3));
3715 __ pop(r2); // pop next in chain
3716 __ str(r2, MemOperand(r3));
3717 // restore parameter- and frame-pointer and pop state.
3718 __ ldm(ia_w, sp, r3.bit() | pp.bit() | fp.bit());
3719 // Before returning we restore the context from the frame pointer if not NULL.
3720 // The frame pointer is NULL in the exception handler of a JS entry frame.
3721 __ cmp(fp, Operand(0));
3722 // Set cp to NULL if fp is NULL.
3723 __ mov(cp, Operand(0), LeaveCC, eq);
3724 // Restore cp otherwise.
3725 __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset), ne);
3726 if (kDebug && FLAG_debug_code) __ mov(lr, Operand(pc));
3727 __ pop(pc);
3728}
3729
3730
3731void CEntryStub::GenerateThrowOutOfMemory(MacroAssembler* masm) {
3732 // Fetch top stack handler.
3733 __ mov(r3, Operand(ExternalReference(Top::k_handler_address)));
3734 __ ldr(r3, MemOperand(r3));
3735
3736 // Unwind the handlers until the ENTRY handler is found.
3737 Label loop, done;
3738 __ bind(&loop);
3739 // Load the type of the current stack handler.
3740 const int kStateOffset = StackHandlerConstants::kAddressDisplacement +
3741 StackHandlerConstants::kStateOffset;
3742 __ ldr(r2, MemOperand(r3, kStateOffset));
3743 __ cmp(r2, Operand(StackHandler::ENTRY));
3744 __ b(eq, &done);
3745 // Fetch the next handler in the list.
3746 const int kNextOffset = StackHandlerConstants::kAddressDisplacement +
3747 StackHandlerConstants::kNextOffset;
3748 __ ldr(r3, MemOperand(r3, kNextOffset));
3749 __ jmp(&loop);
3750 __ bind(&done);
3751
3752 // Set the top handler address to next handler past the current ENTRY handler.
3753 __ ldr(r0, MemOperand(r3, kNextOffset));
3754 __ mov(r2, Operand(ExternalReference(Top::k_handler_address)));
3755 __ str(r0, MemOperand(r2));
3756
3757 // Set external caught exception to false.
3758 __ mov(r0, Operand(false));
3759 ExternalReference external_caught(Top::k_external_caught_exception_address);
3760 __ mov(r2, Operand(external_caught));
3761 __ str(r0, MemOperand(r2));
3762
3763 // Set pending exception and r0 to out of memory exception.
3764 Failure* out_of_memory = Failure::OutOfMemoryException();
3765 __ mov(r0, Operand(reinterpret_cast<int32_t>(out_of_memory)));
3766 __ mov(r2, Operand(ExternalReference(Top::k_pending_exception_address)));
3767 __ str(r0, MemOperand(r2));
3768
3769 // Restore the stack to the address of the ENTRY handler
3770 __ mov(sp, Operand(r3));
3771
3772 // restore parameter- and frame-pointer and pop state.
3773 __ ldm(ia_w, sp, r3.bit() | pp.bit() | fp.bit());
3774 // Before returning we restore the context from the frame pointer if not NULL.
3775 // The frame pointer is NULL in the exception handler of a JS entry frame.
3776 __ cmp(fp, Operand(0));
3777 // Set cp to NULL if fp is NULL.
3778 __ mov(cp, Operand(0), LeaveCC, eq);
3779 // Restore cp otherwise.
3780 __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset), ne);
3781 if (kDebug && FLAG_debug_code) __ mov(lr, Operand(pc));
3782 __ pop(pc);
3783}
3784
3785
3786void CEntryStub::GenerateCore(MacroAssembler* masm,
3787 Label* throw_normal_exception,
3788 Label* throw_out_of_memory_exception,
3789 StackFrame::Type frame_type,
3790 bool do_gc) {
3791 // r0: result parameter for PerformGC, if any
3792 // r4: number of arguments including receiver (C callee-saved)
3793 // r5: pointer to builtin function (C callee-saved)
3794 // r6: pointer to the first argument (C callee-saved)
3795
3796 if (do_gc) {
3797 // Passing r0.
3798 __ Call(FUNCTION_ADDR(Runtime::PerformGC), RelocInfo::RUNTIME_ENTRY);
3799 }
3800
3801 // Call C built-in.
3802 // r0 = argc, r1 = argv
3803 __ mov(r0, Operand(r4));
3804 __ mov(r1, Operand(r6));
3805
3806 // TODO(1242173): To let the GC traverse the return address of the exit
3807 // frames, we need to know where the return address is. Right now,
3808 // we push it on the stack to be able to find it again, but we never
3809 // restore from it in case of changes, which makes it impossible to
3810 // support moving the C entry code stub. This should be fixed, but currently
3811 // this is OK because the CEntryStub gets generated so early in the V8 boot
3812 // sequence that it is not moving ever.
3813 __ add(lr, pc, Operand(4)); // compute return address: (pc + 8) + 4
3814 __ push(lr);
3815#if !defined(__arm__)
3816 // Notify the simulator of the transition to C code.
3817 __ swi(assembler::arm::call_rt_r5);
3818#else /* !defined(__arm__) */
3819 __ mov(pc, Operand(r5));
3820#endif /* !defined(__arm__) */
3821 // result is in r0 or r0:r1 - do not destroy these registers!
3822
3823 // check for failure result
3824 Label failure_returned;
3825 ASSERT(((kFailureTag + 1) & kFailureTagMask) == 0);
3826 // Lower 2 bits of r2 are 0 iff r0 has failure tag.
3827 __ add(r2, r0, Operand(1));
3828 __ tst(r2, Operand(kFailureTagMask));
3829 __ b(eq, &failure_returned);
3830
3831 // Exit C frame and return.
3832 // r0:r1: result
3833 // sp: stack pointer
3834 // fp: frame pointer
3835 // pp: caller's parameter pointer pp (restored as C callee-saved)
3836 __ LeaveExitFrame(frame_type);
3837
3838 // check if we should retry or throw exception
3839 Label retry;
3840 __ bind(&failure_returned);
3841 ASSERT(Failure::RETRY_AFTER_GC == 0);
3842 __ tst(r0, Operand(((1 << kFailureTypeTagSize) - 1) << kFailureTagSize));
3843 __ b(eq, &retry);
3844
3845 Label continue_exception;
3846 // If the returned failure is EXCEPTION then promote Top::pending_exception().
3847 __ cmp(r0, Operand(reinterpret_cast<int32_t>(Failure::Exception())));
3848 __ b(ne, &continue_exception);
3849
3850 // Retrieve the pending exception and clear the variable.
3851 __ mov(ip, Operand(Factory::the_hole_value().location()));
3852 __ ldr(r3, MemOperand(ip));
3853 __ mov(ip, Operand(Top::pending_exception_address()));
3854 __ ldr(r0, MemOperand(ip));
3855 __ str(r3, MemOperand(ip));
3856
3857 __ bind(&continue_exception);
3858 // Special handling of out of memory exception.
3859 Failure* out_of_memory = Failure::OutOfMemoryException();
3860 __ cmp(r0, Operand(reinterpret_cast<int32_t>(out_of_memory)));
3861 __ b(eq, throw_out_of_memory_exception);
3862
3863 // Handle normal exception.
3864 __ jmp(throw_normal_exception);
3865
3866 __ bind(&retry); // pass last failure (r0) as parameter (r0) when retrying
3867}
3868
3869
3870void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) {
3871 // Called from JavaScript; parameters are on stack as if calling JS function
3872 // r0: number of arguments including receiver
3873 // r1: pointer to builtin function
3874 // fp: frame pointer (restored after C call)
3875 // sp: stack pointer (restored as callee's pp after C call)
3876 // cp: current context (C callee-saved)
3877 // pp: caller's parameter pointer pp (C callee-saved)
3878
3879 // NOTE: Invocations of builtins may return failure objects
3880 // instead of a proper result. The builtin entry handles
3881 // this by performing a garbage collection and retrying the
3882 // builtin once.
3883
3884 StackFrame::Type frame_type = is_debug_break
3885 ? StackFrame::EXIT_DEBUG
3886 : StackFrame::EXIT;
3887
3888 // Enter the exit frame that transitions from JavaScript to C++.
3889 __ EnterExitFrame(frame_type);
3890
3891 // r4: number of arguments (C callee-saved)
3892 // r5: pointer to builtin function (C callee-saved)
3893 // r6: pointer to first argument (C callee-saved)
3894
3895 Label throw_out_of_memory_exception;
3896 Label throw_normal_exception;
3897
3898#ifdef DEBUG
3899 if (FLAG_gc_greedy) {
3900 Failure* failure = Failure::RetryAfterGC(0, NEW_SPACE);
3901 __ mov(r0, Operand(reinterpret_cast<intptr_t>(failure)));
3902 }
3903 GenerateCore(masm,
3904 &throw_normal_exception,
3905 &throw_out_of_memory_exception,
3906 frame_type,
3907 FLAG_gc_greedy);
3908#else
3909 GenerateCore(masm,
3910 &throw_normal_exception,
3911 &throw_out_of_memory_exception,
3912 frame_type,
3913 false);
3914#endif
3915 GenerateCore(masm,
3916 &throw_normal_exception,
3917 &throw_out_of_memory_exception,
3918 frame_type,
3919 true);
3920
3921 __ bind(&throw_out_of_memory_exception);
3922 GenerateThrowOutOfMemory(masm);
3923 // control flow for generated will not return.
3924
3925 __ bind(&throw_normal_exception);
3926 GenerateThrowTOS(masm);
3927}
3928
3929
3930void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) {
3931 // r0: code entry
3932 // r1: function
3933 // r2: receiver
3934 // r3: argc
3935 // [sp+0]: argv
3936
3937 Label invoke, exit;
3938
3939 // Called from C, so do not pop argc and args on exit (preserve sp)
3940 // No need to save register-passed args
3941 // Save callee-saved registers (incl. cp, pp, and fp), sp, and lr
3942 __ stm(db_w, sp, kCalleeSaved | lr.bit());
3943
3944 // Get address of argv, see stm above.
3945 // r0: code entry
3946 // r1: function
3947 // r2: receiver
3948 // r3: argc
3949 __ add(r4, sp, Operand((kNumCalleeSaved + 1)*kPointerSize));
3950 __ ldr(r4, MemOperand(r4)); // argv
3951
3952 // Push a frame with special values setup to mark it as an entry frame.
3953 // r0: code entry
3954 // r1: function
3955 // r2: receiver
3956 // r3: argc
3957 // r4: argv
3958 int marker = is_construct ? StackFrame::ENTRY_CONSTRUCT : StackFrame::ENTRY;
3959 __ mov(r8, Operand(-1)); // Push a bad frame pointer to fail if it is used.
3960 __ mov(r7, Operand(~ArgumentsAdaptorFrame::SENTINEL));
3961 __ mov(r6, Operand(Smi::FromInt(marker)));
3962 __ mov(r5, Operand(ExternalReference(Top::k_c_entry_fp_address)));
3963 __ ldr(r5, MemOperand(r5));
3964 __ stm(db_w, sp, r5.bit() | r6.bit() | r7.bit() | r8.bit());
3965
3966 // Setup frame pointer for the frame to be pushed.
3967 __ add(fp, sp, Operand(-EntryFrameConstants::kCallerFPOffset));
3968
3969 // Call a faked try-block that does the invoke.
3970 __ bl(&invoke);
3971
3972 // Caught exception: Store result (exception) in the pending
3973 // exception field in the JSEnv and return a failure sentinel.
3974 // Coming in here the fp will be invalid because the PushTryHandler below
3975 // sets it to 0 to signal the existence of the JSEntry frame.
3976 __ mov(ip, Operand(Top::pending_exception_address()));
3977 __ str(r0, MemOperand(ip));
3978 __ mov(r0, Operand(Handle<Failure>(Failure::Exception())));
3979 __ b(&exit);
3980
3981 // Invoke: Link this frame into the handler chain.
3982 __ bind(&invoke);
3983 // Must preserve r0-r4, r5-r7 are available.
3984 __ PushTryHandler(IN_JS_ENTRY, JS_ENTRY_HANDLER);
3985 // If an exception not caught by another handler occurs, this handler returns
3986 // control to the code after the bl(&invoke) above, which restores all
3987 // kCalleeSaved registers (including cp, pp and fp) to their saved values
3988 // before returning a failure to C.
3989
3990 // Clear any pending exceptions.
3991 __ mov(ip, Operand(ExternalReference::the_hole_value_location()));
3992 __ ldr(r5, MemOperand(ip));
3993 __ mov(ip, Operand(Top::pending_exception_address()));
3994 __ str(r5, MemOperand(ip));
3995
3996 // Invoke the function by calling through JS entry trampoline builtin.
3997 // Notice that we cannot store a reference to the trampoline code directly in
3998 // this stub, because runtime stubs are not traversed when doing GC.
3999
4000 // Expected registers by Builtins::JSEntryTrampoline
4001 // r0: code entry
4002 // r1: function
4003 // r2: receiver
4004 // r3: argc
4005 // r4: argv
4006 if (is_construct) {
4007 ExternalReference construct_entry(Builtins::JSConstructEntryTrampoline);
4008 __ mov(ip, Operand(construct_entry));
4009 } else {
4010 ExternalReference entry(Builtins::JSEntryTrampoline);
4011 __ mov(ip, Operand(entry));
4012 }
4013 __ ldr(ip, MemOperand(ip)); // deref address
4014
4015 // Branch and link to JSEntryTrampoline
4016 __ mov(lr, Operand(pc));
4017 __ add(pc, ip, Operand(Code::kHeaderSize - kHeapObjectTag));
4018
4019 // Unlink this frame from the handler chain. When reading the
4020 // address of the next handler, there is no need to use the address
4021 // displacement since the current stack pointer (sp) points directly
4022 // to the stack handler.
4023 __ ldr(r3, MemOperand(sp, StackHandlerConstants::kNextOffset));
4024 __ mov(ip, Operand(ExternalReference(Top::k_handler_address)));
4025 __ str(r3, MemOperand(ip));
4026 // No need to restore registers
4027 __ add(sp, sp, Operand(StackHandlerConstants::kSize));
4028
4029 __ bind(&exit); // r0 holds result
4030 // Restore the top frame descriptors from the stack.
4031 __ pop(r3);
4032 __ mov(ip, Operand(ExternalReference(Top::k_c_entry_fp_address)));
4033 __ str(r3, MemOperand(ip));
4034
4035 // Reset the stack to the callee saved registers.
4036 __ add(sp, sp, Operand(-EntryFrameConstants::kCallerFPOffset));
4037
4038 // Restore callee-saved registers and return.
4039#ifdef DEBUG
4040 if (FLAG_debug_code) __ mov(lr, Operand(pc));
4041#endif
4042 __ ldm(ia_w, sp, kCalleeSaved | pc.bit());
4043}
4044
4045
ager@chromium.org7c537e22008-10-16 08:43:32 +00004046void ArgumentsAccessStub::GenerateReadLength(MacroAssembler* masm) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004047 // Check if the calling frame is an arguments adaptor frame.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004048 Label adaptor;
4049 __ ldr(r2, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
4050 __ ldr(r3, MemOperand(r2, StandardFrameConstants::kContextOffset));
4051 __ cmp(r3, Operand(ArgumentsAdaptorFrame::SENTINEL));
ager@chromium.org7c537e22008-10-16 08:43:32 +00004052 __ b(eq, &adaptor);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004053
ager@chromium.org7c537e22008-10-16 08:43:32 +00004054 // Nothing to do: The formal number of parameters has already been
4055 // passed in register r0 by calling function. Just return it.
4056 __ mov(pc, lr);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004057
ager@chromium.org7c537e22008-10-16 08:43:32 +00004058 // Arguments adaptor case: Read the arguments length from the
4059 // adaptor frame and return it.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004060 __ bind(&adaptor);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004061 __ ldr(r0, MemOperand(r2, ArgumentsAdaptorFrameConstants::kLengthOffset));
ager@chromium.org7c537e22008-10-16 08:43:32 +00004062 __ mov(pc, lr);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004063}
4064
4065
ager@chromium.org7c537e22008-10-16 08:43:32 +00004066void ArgumentsAccessStub::GenerateReadElement(MacroAssembler* masm) {
4067 // The displacement is the offset of the last parameter (if any)
4068 // relative to the frame pointer.
4069 static const int kDisplacement =
4070 StandardFrameConstants::kCallerSPOffset - kPointerSize;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004071
ager@chromium.org7c537e22008-10-16 08:43:32 +00004072 // Check that the key is a smi.
4073 Label slow;
4074 __ tst(r1, Operand(kSmiTagMask));
4075 __ b(ne, &slow);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004076
ager@chromium.org7c537e22008-10-16 08:43:32 +00004077 // Check if the calling frame is an arguments adaptor frame.
4078 Label adaptor;
4079 __ ldr(r2, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
4080 __ ldr(r3, MemOperand(r2, StandardFrameConstants::kContextOffset));
4081 __ cmp(r3, Operand(ArgumentsAdaptorFrame::SENTINEL));
4082 __ b(eq, &adaptor);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004083
ager@chromium.org7c537e22008-10-16 08:43:32 +00004084 // Check index against formal parameters count limit passed in
4085 // through register eax. Use unsigned comparison to get negative
4086 // check for free.
4087 __ cmp(r1, r0);
4088 __ b(cs, &slow);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004089
ager@chromium.org7c537e22008-10-16 08:43:32 +00004090 // Read the argument from the stack and return it.
4091 __ sub(r3, r0, r1);
4092 __ add(r3, fp, Operand(r3, LSL, kPointerSizeLog2 - kSmiTagSize));
4093 __ ldr(r0, MemOperand(r3, kDisplacement));
4094 __ mov(pc, lr);
4095
4096 // Arguments adaptor case: Check index against actual arguments
4097 // limit found in the arguments adaptor frame. Use unsigned
4098 // comparison to get negative check for free.
4099 __ bind(&adaptor);
4100 __ ldr(r0, MemOperand(r2, ArgumentsAdaptorFrameConstants::kLengthOffset));
4101 __ cmp(r1, r0);
4102 __ b(cs, &slow);
4103
4104 // Read the argument from the adaptor frame and return it.
4105 __ sub(r3, r0, r1);
4106 __ add(r3, r2, Operand(r3, LSL, kPointerSizeLog2 - kSmiTagSize));
4107 __ ldr(r0, MemOperand(r3, kDisplacement));
4108 __ mov(pc, lr);
4109
4110 // Slow-case: Handle non-smi or out-of-bounds access to arguments
4111 // by calling the runtime system.
4112 __ bind(&slow);
4113 __ push(r1);
4114 __ TailCallRuntime(ExternalReference(Runtime::kGetArgumentsProperty), 1);
4115}
4116
4117
4118void ArgumentsAccessStub::GenerateNewObject(MacroAssembler* masm) {
4119 // Check if the calling frame is an arguments adaptor frame.
4120 Label runtime;
4121 __ ldr(r2, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
4122 __ ldr(r3, MemOperand(r2, StandardFrameConstants::kContextOffset));
4123 __ cmp(r3, Operand(ArgumentsAdaptorFrame::SENTINEL));
4124 __ b(ne, &runtime);
4125
4126 // Patch the arguments.length and the parameters pointer.
4127 __ ldr(r0, MemOperand(r2, ArgumentsAdaptorFrameConstants::kLengthOffset));
4128 __ str(r0, MemOperand(sp, 0 * kPointerSize));
4129 __ add(r3, r2, Operand(r0, LSL, kPointerSizeLog2 - kSmiTagSize));
4130 __ add(r3, r3, Operand(StandardFrameConstants::kCallerSPOffset));
4131 __ str(r3, MemOperand(sp, 1 * kPointerSize));
4132
4133 // Do the runtime call to allocate the arguments object.
4134 __ bind(&runtime);
4135 __ TailCallRuntime(ExternalReference(Runtime::kNewArgumentsFast), 3);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004136}
4137
4138
4139void CallFunctionStub::Generate(MacroAssembler* masm) {
4140 Label slow;
4141 // Get the function to call from the stack.
4142 // function, receiver [, arguments]
4143 __ ldr(r1, MemOperand(sp, (argc_ + 1) * kPointerSize));
4144
4145 // Check that the function is really a JavaScript function.
4146 // r1: pushed function (to be verified)
4147 __ tst(r1, Operand(kSmiTagMask));
4148 __ b(eq, &slow);
4149 // Get the map of the function object.
4150 __ ldr(r2, FieldMemOperand(r1, HeapObject::kMapOffset));
4151 __ ldrb(r2, FieldMemOperand(r2, Map::kInstanceTypeOffset));
4152 __ cmp(r2, Operand(JS_FUNCTION_TYPE));
4153 __ b(ne, &slow);
4154
4155 // Fast-case: Invoke the function now.
4156 // r1: pushed function
4157 ParameterCount actual(argc_);
4158 __ InvokeFunction(r1, actual, JUMP_FUNCTION);
4159
4160 // Slow-case: Non-function called.
4161 __ bind(&slow);
4162 __ mov(r0, Operand(argc_)); // Setup the number of arguments.
4163 __ InvokeBuiltin(Builtins::CALL_NON_FUNCTION, JUMP_JS);
4164}
4165
4166
4167#undef __
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004168
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004169} } // namespace v8::internal