blob: c5987a3cf0ffbd4440587372d54f83d65a769372 [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
ager@chromium.org7c537e22008-10-16 08:43:32 +000038#define __ masm_->
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000039
kasperl@chromium.orgb9123622008-09-17 14:05:56 +000040// -------------------------------------------------------------------------
ager@chromium.org7c537e22008-10-16 08:43:32 +000041// VirtualFrame implementation.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000042
ager@chromium.org7c537e22008-10-16 08:43:32 +000043VirtualFrame::VirtualFrame(CodeGenerator* cgen) {
44 ASSERT(cgen->scope() != NULL);
kasperl@chromium.orgb9123622008-09-17 14:05:56 +000045
ager@chromium.org7c537e22008-10-16 08:43:32 +000046 masm_ = cgen->masm();
47 frame_local_count_ = cgen->scope()->num_stack_slots();
48 parameter_count_ = cgen->scope()->num_parameters();
49}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000050
51
ager@chromium.org7c537e22008-10-16 08:43:32 +000052void VirtualFrame::Enter() {
53 Comment cmnt(masm_, "[ Enter JS frame");
54 __ push(ebp);
55 __ mov(ebp, Operand(esp));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000056
ager@chromium.org7c537e22008-10-16 08:43:32 +000057 // Store the context and the function in the frame.
58 __ push(esi);
59 __ push(edi);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000060
ager@chromium.org7c537e22008-10-16 08:43:32 +000061 // Clear the function slot when generating debug code.
62 if (FLAG_debug_code) {
63 __ Set(edi, Immediate(reinterpret_cast<int>(kZapValue)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000064 }
ager@chromium.org7c537e22008-10-16 08:43:32 +000065}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000066
ager@chromium.org7c537e22008-10-16 08:43:32 +000067
68void VirtualFrame::Exit() {
69 Comment cmnt(masm_, "[ Exit JS frame");
70 // Record the location of the JS exit code for patching when setting
71 // break point.
72 __ RecordJSReturn();
73
74 // Avoid using the leave instruction here, because it is too
75 // short. We need the return sequence to be a least the size of a
76 // call instruction to support patching the exit code in the
77 // debugger. See VisitReturnStatement for the full return sequence.
78 __ mov(esp, Operand(ebp));
79 __ pop(ebp);
80}
81
82
83void VirtualFrame::AllocateLocals() {
84 if (frame_local_count_ > 0) {
85 Comment cmnt(masm_, "[ Allocate space for locals");
86 __ Set(eax, Immediate(Factory::undefined_value()));
87 for (int i = 0; i < frame_local_count_; i++) {
88 __ push(eax);
89 }
kasperl@chromium.orgb9123622008-09-17 14:05:56 +000090 }
ager@chromium.org7c537e22008-10-16 08:43:32 +000091}
kasperl@chromium.orgb9123622008-09-17 14:05:56 +000092
ager@chromium.org7c537e22008-10-16 08:43:32 +000093
94void VirtualFrame::Drop(int count) {
95 ASSERT(count >= 0);
96 if (count > 0) {
97 __ add(Operand(esp), Immediate(count * kPointerSize));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000098 }
ager@chromium.org7c537e22008-10-16 08:43:32 +000099}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000100
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000101
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000102void VirtualFrame::Pop() { Drop(1); }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000103
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000104
ager@chromium.org7c537e22008-10-16 08:43:32 +0000105void VirtualFrame::Pop(Register reg) {
106 __ pop(reg);
107}
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000108
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000109
ager@chromium.org7c537e22008-10-16 08:43:32 +0000110void VirtualFrame::Pop(Operand operand) {
111 __ pop(operand);
112}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000113
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000114
ager@chromium.org7c537e22008-10-16 08:43:32 +0000115void VirtualFrame::Push(Register reg) {
116 __ push(reg);
117}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000118
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000119
ager@chromium.org7c537e22008-10-16 08:43:32 +0000120void VirtualFrame::Push(Operand operand) {
121 __ push(operand);
122}
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000123
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000124
ager@chromium.org7c537e22008-10-16 08:43:32 +0000125void VirtualFrame::Push(Immediate immediate) {
126 __ push(immediate);
127}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000128
129
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000130// -------------------------------------------------------------------------
131// CodeGenState implementation.
132
ager@chromium.org7c537e22008-10-16 08:43:32 +0000133CodeGenState::CodeGenState(CodeGenerator* owner)
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000134 : owner_(owner),
ager@chromium.org7c537e22008-10-16 08:43:32 +0000135 typeof_state_(NOT_INSIDE_TYPEOF),
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000136 true_target_(NULL),
137 false_target_(NULL),
138 previous_(NULL) {
139 owner_->set_state(this);
140}
141
142
ager@chromium.org7c537e22008-10-16 08:43:32 +0000143CodeGenState::CodeGenState(CodeGenerator* owner,
144 TypeofState typeof_state,
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000145 Label* true_target,
146 Label* false_target)
147 : owner_(owner),
ager@chromium.org7c537e22008-10-16 08:43:32 +0000148 typeof_state_(typeof_state),
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000149 true_target_(true_target),
150 false_target_(false_target),
151 previous_(owner->state()) {
152 owner_->set_state(this);
153}
154
155
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000156CodeGenState::~CodeGenState() {
157 ASSERT(owner_->state() == this);
158 owner_->set_state(previous_);
159}
160
161
ager@chromium.org7c537e22008-10-16 08:43:32 +0000162// -------------------------------------------------------------------------
163// CodeGenerator implementation
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000164
ager@chromium.org7c537e22008-10-16 08:43:32 +0000165CodeGenerator::CodeGenerator(int buffer_size, Handle<Script> script,
166 bool is_eval)
167 : is_eval_(is_eval),
168 script_(script),
169 deferred_(8),
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000170 masm_(new MacroAssembler(NULL, buffer_size)),
171 scope_(NULL),
ager@chromium.org7c537e22008-10-16 08:43:32 +0000172 frame_(NULL),
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000173 cc_reg_(no_condition),
174 state_(NULL),
175 is_inside_try_(false),
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +0000176 break_stack_height_(0),
177 loop_nesting_(0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000178}
179
180
181// Calling conventions:
182// ebp: frame pointer
183// esp: stack pointer
184// edi: caller's parameter pointer
185// esi: callee's context
186
ager@chromium.org7c537e22008-10-16 08:43:32 +0000187void CodeGenerator::GenCode(FunctionLiteral* fun) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000188 // Record the position for debugging purposes.
189 __ RecordPosition(fun->start_position());
190
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000191 ZoneList<Statement*>* body = fun->body();
192
193 // Initialize state.
ager@chromium.org7c537e22008-10-16 08:43:32 +0000194 ASSERT(scope_ == NULL);
195 scope_ = fun->scope();
196 ASSERT(frame_ == NULL);
197 VirtualFrame virtual_frame(this);
198 frame_ = &virtual_frame;
199 cc_reg_ = no_condition;
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000200
201 // Adjust for function-level loop nesting.
202 loop_nesting_ += fun->loop_nesting();
203
ager@chromium.org7c537e22008-10-16 08:43:32 +0000204 {
205 CodeGenState state(this);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000206
207 // Entry
208 // stack: function, receiver, arguments, return address
209 // esp: stack pointer
210 // ebp: frame pointer
211 // edi: caller's parameter pointer
212 // esi: callee's context
213
ager@chromium.org7c537e22008-10-16 08:43:32 +0000214 frame_->Enter();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000215 // tos: code slot
216#ifdef DEBUG
217 if (strlen(FLAG_stop_at) > 0 &&
218 fun->name()->IsEqualTo(CStrVector(FLAG_stop_at))) {
219 __ int3();
220 }
221#endif
222
223 // This section now only allocates and copies the formals into the
224 // arguments object. It saves the address in ecx, which is saved
225 // at any point before either garbage collection or ecx is
226 // overwritten. The flag arguments_array_allocated communicates
227 // with the store into the arguments variable and guards the lazy
228 // pushes of ecx to TOS. The flag arguments_array_saved notes
229 // when the push has happened.
230 bool arguments_object_allocated = false;
231 bool arguments_object_saved = false;
232
233 // Allocate arguments object.
234 // The arguments object pointer needs to be saved in ecx, since we need
235 // to store arguments into the context.
ager@chromium.org7c537e22008-10-16 08:43:32 +0000236 if (scope_->arguments() != NULL) {
237 ASSERT(scope_->arguments_shadow() != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000238 Comment cmnt(masm_, "[ allocate arguments object");
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000239 ArgumentsAccessStub stub(ArgumentsAccessStub::NEW_OBJECT);
ager@chromium.org7c537e22008-10-16 08:43:32 +0000240 __ lea(eax, frame_->Receiver());
241 frame_->Push(frame_->Function());
242 frame_->Push(eax);
243 frame_->Push(Immediate(Smi::FromInt(scope_->num_parameters())));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000244 __ CallStub(&stub);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000245 __ mov(ecx, Operand(eax));
246 arguments_object_allocated = true;
247 }
248
249 // Allocate space for locals and initialize them.
ager@chromium.org7c537e22008-10-16 08:43:32 +0000250 frame_->AllocateLocals();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000251
ager@chromium.org7c537e22008-10-16 08:43:32 +0000252 if (scope_->num_heap_slots() > 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000253 Comment cmnt(masm_, "[ allocate local context");
254 // Save the arguments object pointer, if any.
255 if (arguments_object_allocated && !arguments_object_saved) {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000256 frame_->Push(ecx);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000257 arguments_object_saved = true;
258 }
259 // Allocate local context.
260 // Get outer context and create a new context based on it.
ager@chromium.org7c537e22008-10-16 08:43:32 +0000261 frame_->Push(frame_->Function());
kasper.lund7276f142008-07-30 08:49:36 +0000262 __ CallRuntime(Runtime::kNewContext, 1); // eax holds the result
263
264 if (kDebug) {
265 Label verified_true;
266 // Verify eax and esi are the same in debug mode
267 __ cmp(eax, Operand(esi));
268 __ j(equal, &verified_true);
269 __ int3();
270 __ bind(&verified_true);
271 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000272 // Update context local.
ager@chromium.org7c537e22008-10-16 08:43:32 +0000273 __ mov(frame_->Context(), esi);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000274 }
275
276 // TODO(1241774): Improve this code:
277 // 1) only needed if we have a context
278 // 2) no need to recompute context ptr every single time
279 // 3) don't copy parameter operand code from SlotOperand!
280 {
281 Comment cmnt2(masm_, "[ copy context parameters into .context");
282
283 // Note that iteration order is relevant here! If we have the same
284 // parameter twice (e.g., function (x, y, x)), and that parameter
285 // needs to be copied into the context, it must be the last argument
286 // passed to the parameter that needs to be copied. This is a rare
287 // case so we don't check for it, instead we rely on the copying
288 // order: such a parameter is copied repeatedly into the same
289 // context location and thus the last value is what is seen inside
290 // the function.
ager@chromium.org7c537e22008-10-16 08:43:32 +0000291 for (int i = 0; i < scope_->num_parameters(); i++) {
292 Variable* par = scope_->parameter(i);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000293 Slot* slot = par->slot();
294 if (slot != NULL && slot->type() == Slot::CONTEXT) {
295 // Save the arguments object pointer, if any.
296 if (arguments_object_allocated && !arguments_object_saved) {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000297 frame_->Push(ecx);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000298 arguments_object_saved = true;
299 }
ager@chromium.org7c537e22008-10-16 08:43:32 +0000300 ASSERT(!scope_->is_global_scope()); // no parameters in global scope
301 __ mov(eax, frame_->Parameter(i));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000302 // Loads ecx with context; used below in RecordWrite.
303 __ mov(SlotOperand(slot, ecx), eax);
304 int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize;
305 __ RecordWrite(ecx, offset, eax, ebx);
306 }
307 }
308 }
309
310 // This section stores the pointer to the arguments object that
311 // was allocated and copied into above. If the address was not
312 // saved to TOS, we push ecx onto the stack.
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000313 //
314 // Store the arguments object. This must happen after context
315 // initialization because the arguments object may be stored in the
316 // context.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000317 if (arguments_object_allocated) {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000318 ASSERT(scope_->arguments() != NULL);
319 ASSERT(scope_->arguments_shadow() != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000320 Comment cmnt(masm_, "[ store arguments object");
ager@chromium.org7c537e22008-10-16 08:43:32 +0000321 { Reference shadow_ref(this, scope_->arguments_shadow());
322 ASSERT(shadow_ref.is_slot());
323 { Reference arguments_ref(this, scope_->arguments());
324 ASSERT(arguments_ref.is_slot());
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000325 // If the newly-allocated arguments object is already on the
ager@chromium.org7c537e22008-10-16 08:43:32 +0000326 // stack, we make use of the convenient property that references
327 // representing slots take up no space on the expression stack
328 // (ie, it doesn't matter that the stored value is actually below
329 // the reference).
330 //
331 // If the newly-allocated argument object is not already on
332 // the stack, we rely on the property that loading a
333 // zero-sized reference will not clobber the ecx register.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000334 if (!arguments_object_saved) {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000335 frame_->Push(ecx);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000336 }
ager@chromium.org7c537e22008-10-16 08:43:32 +0000337 arguments_ref.SetValue(NOT_CONST_INIT);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000338 }
ager@chromium.org7c537e22008-10-16 08:43:32 +0000339 shadow_ref.SetValue(NOT_CONST_INIT);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000340 }
ager@chromium.org7c537e22008-10-16 08:43:32 +0000341 frame_->Pop(); // Value is no longer needed.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000342 }
343
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000344 // Generate code to 'execute' declarations and initialize functions
345 // (source elements). In case of an illegal redeclaration we need to
346 // handle that instead of processing the declarations.
ager@chromium.org7c537e22008-10-16 08:43:32 +0000347 if (scope_->HasIllegalRedeclaration()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000348 Comment cmnt(masm_, "[ illegal redeclarations");
ager@chromium.org7c537e22008-10-16 08:43:32 +0000349 scope_->VisitIllegalRedeclaration(this);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000350 } else {
351 Comment cmnt(masm_, "[ declarations");
ager@chromium.org7c537e22008-10-16 08:43:32 +0000352 ProcessDeclarations(scope_->declarations());
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000353 // Bail out if a stack-overflow exception occurred when processing
354 // declarations.
kasper.lund212ac232008-07-16 07:07:30 +0000355 if (HasStackOverflow()) return;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000356 }
357
358 if (FLAG_trace) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +0000359 __ CallRuntime(Runtime::kTraceEnter, 0);
360 // Ignore the return value.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000361 }
362 CheckStack();
363
364 // Compile the body of the function in a vanilla state. Don't
365 // bother compiling all the code if the scope has an illegal
366 // redeclaration.
ager@chromium.org7c537e22008-10-16 08:43:32 +0000367 if (!scope_->HasIllegalRedeclaration()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000368 Comment cmnt(masm_, "[ function body");
369#ifdef DEBUG
370 bool is_builtin = Bootstrapper::IsActive();
371 bool should_trace =
372 is_builtin ? FLAG_trace_builtin_calls : FLAG_trace_calls;
373 if (should_trace) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +0000374 __ CallRuntime(Runtime::kDebugTrace, 0);
375 // Ignore the return value.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000376 }
377#endif
378 VisitStatements(body);
379
380 // Generate a return statement if necessary.
381 if (body->is_empty() || body->last()->AsReturnStatement() == NULL) {
382 Literal undefined(Factory::undefined_value());
383 ReturnStatement statement(&undefined);
384 statement.set_statement_pos(fun->end_position());
385 VisitReturnStatement(&statement);
386 }
387 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000388 }
389
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000390 // Adjust for function-level loop nesting.
391 loop_nesting_ -= fun->loop_nesting();
392
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000393 // Code generation state must be reset.
394 scope_ = NULL;
ager@chromium.org7c537e22008-10-16 08:43:32 +0000395 frame_ = NULL;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000396 ASSERT(!has_cc());
397 ASSERT(state_ == NULL);
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000398 ASSERT(loop_nesting() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000399}
400
401
ager@chromium.org7c537e22008-10-16 08:43:32 +0000402Operand CodeGenerator::SlotOperand(Slot* slot, Register tmp) {
403 // Currently, this assertion will fail if we try to assign to
404 // a constant variable that is constant because it is read-only
405 // (such as the variable referring to a named function expression).
406 // We need to implement assignments to read-only variables.
407 // Ideally, we should do this during AST generation (by converting
408 // such assignments into expression statements); however, in general
409 // we may not be able to make the decision until past AST generation,
410 // that is when the entire program is known.
411 ASSERT(slot != NULL);
412 int index = slot->index();
413 switch (slot->type()) {
414 case Slot::PARAMETER:
415 return frame_->Parameter(index);
416
417 case Slot::LOCAL:
418 return frame_->Local(index);
419
420 case Slot::CONTEXT: {
421 // Follow the context chain if necessary.
422 ASSERT(!tmp.is(esi)); // do not overwrite context register
423 Register context = esi;
424 int chain_length = scope()->ContextChainLength(slot->var()->scope());
425 for (int i = chain_length; i-- > 0;) {
426 // Load the closure.
427 // (All contexts, even 'with' contexts, have a closure,
428 // and it is the same for all contexts inside a function.
429 // There is no need to go to the function context first.)
430 __ mov(tmp, ContextOperand(context, Context::CLOSURE_INDEX));
431 // Load the function context (which is the incoming, outer context).
432 __ mov(tmp, FieldOperand(tmp, JSFunction::kContextOffset));
433 context = tmp;
434 }
435 // We may have a 'with' context now. Get the function context.
436 // (In fact this mov may never be the needed, since the scope analysis
437 // may not permit a direct context access in this case and thus we are
438 // always at a function context. However it is safe to dereference be-
439 // cause the function context of a function context is itself. Before
440 // deleting this mov we should try to create a counter-example first,
441 // though...)
442 __ mov(tmp, ContextOperand(context, Context::FCONTEXT_INDEX));
443 return ContextOperand(tmp, index);
444 }
445
446 default:
447 UNREACHABLE();
448 return Operand(eax);
449 }
450}
451
452
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000453// Loads a value on TOS. If it is a boolean value, the result may have been
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000454// (partially) translated into branches, or it may have set the condition
455// code register. If force_cc is set, the value is forced to set the
456// condition code register and no value is pushed. If the condition code
457// register was set, has_cc() is true and cc_reg_ contains the condition to
458// test for 'true'.
ager@chromium.org7c537e22008-10-16 08:43:32 +0000459void CodeGenerator::LoadCondition(Expression* x,
460 TypeofState typeof_state,
461 Label* true_target,
462 Label* false_target,
463 bool force_cc) {
464 ASSERT(!has_cc());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000465
ager@chromium.org7c537e22008-10-16 08:43:32 +0000466 { CodeGenState new_state(this, typeof_state, true_target, false_target);
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000467 Visit(x);
468 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000469 if (force_cc && !has_cc()) {
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000470 // Convert the TOS value to a boolean in the condition code register.
471 // Visiting an expression may possibly choose neither (a) to leave a
472 // value in the condition code register nor (b) to leave a value in TOS
473 // (eg, by compiling to only jumps to the targets). In that case the
474 // code generated by ToBoolean is wrong because it assumes the value of
475 // the expression in TOS. So long as there is always a value in TOS or
476 // the condition code register when control falls through to here (there
477 // is), the code generated by ToBoolean is dead and therefore safe.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000478 ToBoolean(true_target, false_target);
479 }
480 ASSERT(has_cc() || !force_cc);
481}
482
483
ager@chromium.org7c537e22008-10-16 08:43:32 +0000484void CodeGenerator::Load(Expression* x, TypeofState typeof_state) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000485 Label true_target;
486 Label false_target;
ager@chromium.org7c537e22008-10-16 08:43:32 +0000487 LoadCondition(x, typeof_state, &true_target, &false_target, false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000488
489 if (has_cc()) {
490 // convert cc_reg_ into a bool
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000491 Label loaded, materialize_true;
492 __ j(cc_reg_, &materialize_true);
ager@chromium.org7c537e22008-10-16 08:43:32 +0000493 frame_->Push(Immediate(Factory::false_value()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000494 __ jmp(&loaded);
495 __ bind(&materialize_true);
ager@chromium.org7c537e22008-10-16 08:43:32 +0000496 frame_->Push(Immediate(Factory::true_value()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000497 __ bind(&loaded);
498 cc_reg_ = no_condition;
499 }
500
501 if (true_target.is_linked() || false_target.is_linked()) {
502 // we have at least one condition value
503 // that has been "translated" into a branch,
504 // thus it needs to be loaded explicitly again
505 Label loaded;
506 __ jmp(&loaded); // don't lose current TOS
507 bool both = true_target.is_linked() && false_target.is_linked();
508 // reincarnate "true", if necessary
509 if (true_target.is_linked()) {
510 __ bind(&true_target);
ager@chromium.org7c537e22008-10-16 08:43:32 +0000511 frame_->Push(Immediate(Factory::true_value()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000512 }
513 // if both "true" and "false" need to be reincarnated,
514 // jump across code for "false"
515 if (both)
516 __ jmp(&loaded);
517 // reincarnate "false", if necessary
518 if (false_target.is_linked()) {
519 __ bind(&false_target);
ager@chromium.org7c537e22008-10-16 08:43:32 +0000520 frame_->Push(Immediate(Factory::false_value()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000521 }
522 // everything is loaded at this point
523 __ bind(&loaded);
524 }
525 ASSERT(!has_cc());
526}
527
528
ager@chromium.org7c537e22008-10-16 08:43:32 +0000529void CodeGenerator::LoadGlobal() {
530 frame_->Push(GlobalObject());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000531}
532
533
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000534void CodeGenerator::LoadGlobalReceiver(Register scratch) {
535 __ mov(scratch, GlobalObject());
536 frame_->Push(FieldOperand(scratch, GlobalObject::kGlobalReceiverOffset));
537}
538
539
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000540// TODO(1241834): Get rid of this function in favor of just using Load, now
ager@chromium.org7c537e22008-10-16 08:43:32 +0000541// that we have the INSIDE_TYPEOF typeof state. => Need to handle global
542// variables w/o reference errors elsewhere.
543void CodeGenerator::LoadTypeofExpression(Expression* x) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000544 Variable* variable = x->AsVariableProxy()->AsVariable();
545 if (variable != NULL && !variable->is_this() && variable->is_global()) {
546 // NOTE: This is somewhat nasty. We force the compiler to load
547 // the variable as if through '<global>.<variable>' to make sure we
548 // do not get reference errors.
549 Slot global(variable, Slot::CONTEXT, Context::GLOBAL_INDEX);
550 Literal key(variable->name());
551 // TODO(1241834): Fetch the position from the variable instead of using
552 // no position.
ager@chromium.org236ad962008-09-25 09:45:57 +0000553 Property property(&global, &key, RelocInfo::kNoPosition);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000554 Load(&property);
555 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000556 Load(x, INSIDE_TYPEOF);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000557 }
558}
559
560
ager@chromium.org7c537e22008-10-16 08:43:32 +0000561Reference::Reference(CodeGenerator* cgen, Expression* expression)
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000562 : cgen_(cgen), expression_(expression), type_(ILLEGAL) {
563 cgen->LoadReference(this);
564}
565
566
567Reference::~Reference() {
568 cgen_->UnloadReference(this);
569}
570
571
ager@chromium.org7c537e22008-10-16 08:43:32 +0000572void CodeGenerator::LoadReference(Reference* ref) {
573 Comment cmnt(masm_, "[ LoadReference");
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000574 Expression* e = ref->expression();
575 Property* property = e->AsProperty();
576 Variable* var = e->AsVariableProxy()->AsVariable();
577
578 if (property != NULL) {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000579 // The expression is either a property or a variable proxy that rewrites
580 // to a property.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000581 Load(property->obj());
ager@chromium.org7c537e22008-10-16 08:43:32 +0000582 // We use a named reference if the key is a literal symbol, unless it is
583 // a string that can be legally parsed as an integer. This is because
584 // otherwise we will not get into the slow case code that handles [] on
585 // String objects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000586 Literal* literal = property->key()->AsLiteral();
587 uint32_t dummy;
ager@chromium.org7c537e22008-10-16 08:43:32 +0000588 if (literal != NULL &&
589 literal->handle()->IsSymbol() &&
590 !String::cast(*(literal->handle()))->AsArrayIndex(&dummy)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000591 ref->set_type(Reference::NAMED);
592 } else {
593 Load(property->key());
594 ref->set_type(Reference::KEYED);
595 }
596 } else if (var != NULL) {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000597 // The expression is a variable proxy that does not rewrite to a
598 // property. Global variables are treated as named property references.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000599 if (var->is_global()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000600 LoadGlobal();
601 ref->set_type(Reference::NAMED);
602 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000603 ASSERT(var->slot() != NULL);
604 ref->set_type(Reference::SLOT);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000605 }
606 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000607 // Anything else is a runtime error.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000608 Load(e);
609 __ CallRuntime(Runtime::kThrowReferenceError, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000610 }
611}
612
613
ager@chromium.org7c537e22008-10-16 08:43:32 +0000614void CodeGenerator::UnloadReference(Reference* ref) {
615 // Pop a reference from the stack while preserving TOS.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000616 Comment cmnt(masm_, "[ UnloadReference");
617 int size = ref->size();
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000618 if (size == 1) {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000619 frame_->Pop(eax);
620 __ mov(frame_->Top(), eax);
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000621 } else if (size > 1) {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000622 frame_->Pop(eax);
623 frame_->Drop(size);
624 frame_->Push(eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000625 }
626}
627
628
kasper.lund7276f142008-07-30 08:49:36 +0000629class ToBooleanStub: public CodeStub {
630 public:
631 ToBooleanStub() { }
632
633 void Generate(MacroAssembler* masm);
634
635 private:
kasper.lund7276f142008-07-30 08:49:36 +0000636 Major MajorKey() { return ToBoolean; }
kasper.lund7276f142008-07-30 08:49:36 +0000637 int MinorKey() { return 0; }
kasper.lund7276f142008-07-30 08:49:36 +0000638};
639
640
kasper.lund7276f142008-07-30 08:49:36 +0000641// ECMA-262, section 9.2, page 30: ToBoolean(). Pop the top of stack and
642// convert it to a boolean in the condition code register or jump to
643// 'false_target'/'true_target' as appropriate.
ager@chromium.org7c537e22008-10-16 08:43:32 +0000644void CodeGenerator::ToBoolean(Label* true_target, Label* false_target) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000645 Comment cmnt(masm_, "[ ToBoolean");
646
kasper.lund7276f142008-07-30 08:49:36 +0000647 // The value to convert should be popped from the stack.
ager@chromium.org7c537e22008-10-16 08:43:32 +0000648 frame_->Pop(eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000649
kasper.lund7276f142008-07-30 08:49:36 +0000650 // Fast case checks.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000651
kasper.lund7276f142008-07-30 08:49:36 +0000652 // 'false' => false.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000653 __ cmp(eax, Factory::false_value());
654 __ j(equal, false_target);
655
kasper.lund7276f142008-07-30 08:49:36 +0000656 // 'true' => true.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000657 __ cmp(eax, Factory::true_value());
658 __ j(equal, true_target);
659
kasper.lund7276f142008-07-30 08:49:36 +0000660 // 'undefined' => false.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000661 __ cmp(eax, Factory::undefined_value());
662 __ j(equal, false_target);
663
kasper.lund7276f142008-07-30 08:49:36 +0000664 // Smi => false iff zero.
665 ASSERT(kSmiTag == 0);
666 __ test(eax, Operand(eax));
667 __ j(zero, false_target);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000668 __ test(eax, Immediate(kSmiTagMask));
kasper.lund7276f142008-07-30 08:49:36 +0000669 __ j(zero, true_target);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000670
kasper.lund7276f142008-07-30 08:49:36 +0000671 // Call the stub for all other cases.
ager@chromium.org7c537e22008-10-16 08:43:32 +0000672 frame_->Push(eax); // Undo the pop(eax) from above.
kasper.lund7276f142008-07-30 08:49:36 +0000673 ToBooleanStub stub;
674 __ CallStub(&stub);
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000675 // Convert the result (eax) to condition code.
kasper.lund7276f142008-07-30 08:49:36 +0000676 __ test(eax, Operand(eax));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000677
678 ASSERT(not_equal == not_zero);
679 cc_reg_ = not_equal;
680}
681
682
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000683class FloatingPointHelper : public AllStatic {
684 public:
685 // Code pattern for loading floating point values. Input values must
kasper.lund7276f142008-07-30 08:49:36 +0000686 // be either smi or heap number objects (fp values). Requirements:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000687 // operand_1 on TOS+1 , operand_2 on TOS+2; Returns operands as
688 // floating point numbers on FPU stack.
689 static void LoadFloatOperands(MacroAssembler* masm, Register scratch);
kasper.lund7276f142008-07-30 08:49:36 +0000690 // Test if operands are smi or number objects (fp). Requirements:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000691 // operand_1 in eax, operand_2 in edx; falls through on float
692 // operands, jumps to the non_float label otherwise.
693 static void CheckFloatOperands(MacroAssembler* masm,
694 Label* non_float,
695 Register scratch);
696 // Allocate a heap number in new space with undefined value.
697 // Returns tagged pointer in eax, or jumps to need_gc if new space is full.
698 static void AllocateHeapNumber(MacroAssembler* masm,
699 Label* need_gc,
700 Register scratch1,
701 Register scratch2);
702};
703
704
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000705// Flag that indicates whether or not the code for dealing with smis
706// is inlined or should be dealt with in the stub.
707enum GenericBinaryFlags {
708 SMI_CODE_IN_STUB,
709 SMI_CODE_INLINED
710};
711
712
kasper.lund7276f142008-07-30 08:49:36 +0000713class GenericBinaryOpStub: public CodeStub {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000714 public:
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000715 GenericBinaryOpStub(Token::Value op,
716 OverwriteMode mode,
717 GenericBinaryFlags flags)
718 : op_(op), mode_(mode), flags_(flags) { }
719
720 void GenerateSmiCode(MacroAssembler* masm, Label* slow);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000721
722 private:
723 Token::Value op_;
724 OverwriteMode mode_;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000725 GenericBinaryFlags flags_;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000726
727 const char* GetName();
728
729#ifdef DEBUG
730 void Print() {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000731 PrintF("GenericBinaryOpStub (op %s), (mode %d, flags %d)\n",
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000732 Token::String(op_),
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000733 static_cast<int>(mode_),
734 static_cast<int>(flags_));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000735 }
736#endif
737
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000738 // Minor key encoding in 16 bits FOOOOOOOOOOOOOMM.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000739 class ModeBits: public BitField<OverwriteMode, 0, 2> {};
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000740 class OpBits: public BitField<Token::Value, 2, 13> {};
741 class FlagBits: public BitField<GenericBinaryFlags, 15, 1> {};
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000742
kasper.lund7276f142008-07-30 08:49:36 +0000743 Major MajorKey() { return GenericBinaryOp; }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000744 int MinorKey() {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000745 // Encode the parameters in a unique 16 bit value.
746 return OpBits::encode(op_) |
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000747 ModeBits::encode(mode_) |
748 FlagBits::encode(flags_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000749 }
750 void Generate(MacroAssembler* masm);
751};
752
753
kasper.lund7276f142008-07-30 08:49:36 +0000754const char* GenericBinaryOpStub::GetName() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000755 switch (op_) {
kasper.lund7276f142008-07-30 08:49:36 +0000756 case Token::ADD: return "GenericBinaryOpStub_ADD";
757 case Token::SUB: return "GenericBinaryOpStub_SUB";
758 case Token::MUL: return "GenericBinaryOpStub_MUL";
759 case Token::DIV: return "GenericBinaryOpStub_DIV";
760 case Token::BIT_OR: return "GenericBinaryOpStub_BIT_OR";
761 case Token::BIT_AND: return "GenericBinaryOpStub_BIT_AND";
762 case Token::BIT_XOR: return "GenericBinaryOpStub_BIT_XOR";
763 case Token::SAR: return "GenericBinaryOpStub_SAR";
764 case Token::SHL: return "GenericBinaryOpStub_SHL";
765 case Token::SHR: return "GenericBinaryOpStub_SHR";
766 default: return "GenericBinaryOpStub";
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000767 }
768}
769
770
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000771class DeferredInlineBinaryOperation: public DeferredCode {
772 public:
773 DeferredInlineBinaryOperation(CodeGenerator* generator,
774 Token::Value op,
775 OverwriteMode mode,
776 GenericBinaryFlags flags)
777 : DeferredCode(generator), stub_(op, mode, flags) { }
778
779 void GenerateInlineCode() {
780 stub_.GenerateSmiCode(masm(), enter());
781 }
782
783 virtual void Generate() {
784 __ push(ebx);
785 __ CallStub(&stub_);
786 // We must preserve the eax value here, because it will be written
787 // to the top-of-stack element when getting back to the fast case
788 // code. See comment in GenericBinaryOperation where
789 // deferred->exit() is bound.
790 __ push(eax);
791 }
792
793 private:
794 GenericBinaryOpStub stub_;
795};
796
797
ager@chromium.org7c537e22008-10-16 08:43:32 +0000798void CodeGenerator::GenericBinaryOperation(Token::Value op,
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +0000799 StaticType* type,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000800 OverwriteMode overwrite_mode) {
kasper.lund7276f142008-07-30 08:49:36 +0000801 Comment cmnt(masm_, "[ BinaryOperation");
802 Comment cmnt_token(masm_, Token::String(op));
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000803
804 if (op == Token::COMMA) {
805 // Simply discard left value.
806 frame_->Pop(eax);
807 frame_->Pop();
808 frame_->Push(eax);
809 return;
810 }
811
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +0000812 // Set the flags based on the operation, type and loop nesting level.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000813 GenericBinaryFlags flags;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000814 switch (op) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000815 case Token::BIT_OR:
816 case Token::BIT_AND:
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000817 case Token::BIT_XOR:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000818 case Token::SHL:
819 case Token::SHR:
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000820 case Token::SAR:
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +0000821 // Bit operations always assume they likely operate on Smis. Still only
822 // generate the inline Smi check code if this operation is part of a loop.
823 flags = (loop_nesting() > 0)
824 ? SMI_CODE_INLINED
825 : SMI_CODE_IN_STUB;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000826 break;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000827
828 default:
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +0000829 // By default only inline the Smi check code for likely smis if this
830 // operation is part of a loop.
831 flags = ((loop_nesting() > 0) && type->IsLikelySmi())
832 ? SMI_CODE_INLINED
833 : SMI_CODE_IN_STUB;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000834 break;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000835 }
836
837 if (flags == SMI_CODE_INLINED) {
838 // Create a new deferred code for the slow-case part.
839 DeferredInlineBinaryOperation* deferred =
840 new DeferredInlineBinaryOperation(this, op, overwrite_mode, flags);
841 // Fetch the operands from the stack.
842 frame_->Pop(ebx); // get y
843 __ mov(eax, frame_->Top()); // get x
844 // Generate the inline part of the code.
845 deferred->GenerateInlineCode();
846 // Put result back on the stack. It seems somewhat weird to let
847 // the deferred code jump back before the assignment to the frame
848 // top, but this is just to let the peephole optimizer get rid of
849 // more code.
850 __ bind(deferred->exit());
851 __ mov(frame_->Top(), eax);
852 } else {
853 // Call the stub and push the result to the stack.
854 GenericBinaryOpStub stub(op, overwrite_mode, flags);
855 __ CallStub(&stub);
856 frame_->Push(eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000857 }
858}
859
860
861class DeferredInlinedSmiOperation: public DeferredCode {
862 public:
863 DeferredInlinedSmiOperation(CodeGenerator* generator,
864 Token::Value op, int value,
865 OverwriteMode overwrite_mode) :
866 DeferredCode(generator), op_(op), value_(value),
867 overwrite_mode_(overwrite_mode) {
868 set_comment("[ DeferredInlinedSmiOperation");
869 }
870 virtual void Generate() {
871 __ push(eax);
872 __ push(Immediate(Smi::FromInt(value_)));
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000873 GenericBinaryOpStub igostub(op_, overwrite_mode_, SMI_CODE_INLINED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000874 __ CallStub(&igostub);
875 }
876
877 private:
878 Token::Value op_;
879 int value_;
880 OverwriteMode overwrite_mode_;
881};
882
883
884class DeferredInlinedSmiOperationReversed: public DeferredCode {
885 public:
886 DeferredInlinedSmiOperationReversed(CodeGenerator* generator,
887 Token::Value op, int value,
888 OverwriteMode overwrite_mode) :
889 DeferredCode(generator), op_(op), value_(value),
890 overwrite_mode_(overwrite_mode) {
891 set_comment("[ DeferredInlinedSmiOperationReversed");
892 }
893 virtual void Generate() {
894 __ push(Immediate(Smi::FromInt(value_)));
895 __ push(eax);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000896 GenericBinaryOpStub igostub(op_, overwrite_mode_, SMI_CODE_INLINED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000897 __ CallStub(&igostub);
898 }
899
900 private:
901 Token::Value op_;
902 int value_;
903 OverwriteMode overwrite_mode_;
904};
905
906
907class DeferredInlinedSmiAdd: public DeferredCode {
908 public:
909 DeferredInlinedSmiAdd(CodeGenerator* generator, int value,
910 OverwriteMode overwrite_mode) :
911 DeferredCode(generator), value_(value), overwrite_mode_(overwrite_mode) {
912 set_comment("[ DeferredInlinedSmiAdd");
913 }
914
915 virtual void Generate() {
916 // Undo the optimistic add operation and call the shared stub.
917 Immediate immediate(Smi::FromInt(value_));
918 __ sub(Operand(eax), immediate);
919 __ push(eax);
920 __ push(immediate);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000921 GenericBinaryOpStub igostub(Token::ADD, overwrite_mode_, SMI_CODE_INLINED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000922 __ CallStub(&igostub);
923 }
924
925 private:
926 int value_;
927 OverwriteMode overwrite_mode_;
928};
929
930
931class DeferredInlinedSmiAddReversed: public DeferredCode {
932 public:
933 DeferredInlinedSmiAddReversed(CodeGenerator* generator, int value,
934 OverwriteMode overwrite_mode) :
935 DeferredCode(generator), value_(value), overwrite_mode_(overwrite_mode) {
936 set_comment("[ DeferredInlinedSmiAddReversed");
937 }
938
939 virtual void Generate() {
940 // Undo the optimistic add operation and call the shared stub.
941 Immediate immediate(Smi::FromInt(value_));
942 __ sub(Operand(eax), immediate);
943 __ push(immediate);
944 __ push(eax);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000945 GenericBinaryOpStub igostub(Token::ADD, overwrite_mode_, SMI_CODE_INLINED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000946 __ CallStub(&igostub);
947 }
948
949 private:
950 int value_;
951 OverwriteMode overwrite_mode_;
952};
953
954
955class DeferredInlinedSmiSub: public DeferredCode {
956 public:
957 DeferredInlinedSmiSub(CodeGenerator* generator, int value,
958 OverwriteMode overwrite_mode) :
959 DeferredCode(generator), value_(value), overwrite_mode_(overwrite_mode) {
960 set_comment("[ DeferredInlinedSmiSub");
961 }
962
963 virtual void Generate() {
964 // Undo the optimistic sub operation and call the shared stub.
965 Immediate immediate(Smi::FromInt(value_));
966 __ add(Operand(eax), immediate);
967 __ push(eax);
968 __ push(immediate);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000969 GenericBinaryOpStub igostub(Token::SUB, overwrite_mode_, SMI_CODE_INLINED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000970 __ CallStub(&igostub);
971 }
972
973 private:
974 int value_;
975 OverwriteMode overwrite_mode_;
976};
977
978
979class DeferredInlinedSmiSubReversed: public DeferredCode {
980 public:
981 // tos_reg is used to save the TOS value before reversing the operands
982 // eax will contain the immediate value after undoing the optimistic sub.
983 DeferredInlinedSmiSubReversed(CodeGenerator* generator, Register tos_reg,
984 OverwriteMode overwrite_mode) :
985 DeferredCode(generator), tos_reg_(tos_reg),
986 overwrite_mode_(overwrite_mode) {
987 set_comment("[ DeferredInlinedSmiSubReversed");
988 }
989
990 virtual void Generate() {
991 // Undo the optimistic sub operation and call the shared stub.
992 __ add(eax, Operand(tos_reg_));
993 __ push(eax);
ager@chromium.org7c537e22008-10-16 08:43:32 +0000994 __ push(tos_reg_);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000995 GenericBinaryOpStub igostub(Token::SUB, overwrite_mode_, SMI_CODE_INLINED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000996 __ CallStub(&igostub);
997 }
998
999 private:
1000 Register tos_reg_;
1001 OverwriteMode overwrite_mode_;
1002};
1003
1004
ager@chromium.org7c537e22008-10-16 08:43:32 +00001005void CodeGenerator::SmiOperation(Token::Value op,
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00001006 StaticType* type,
ager@chromium.org7c537e22008-10-16 08:43:32 +00001007 Handle<Object> value,
1008 bool reversed,
1009 OverwriteMode overwrite_mode) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001010 // NOTE: This is an attempt to inline (a bit) more of the code for
1011 // some possible smi operations (like + and -) when (at least) one
1012 // of the operands is a literal smi. With this optimization, the
1013 // performance of the system is increased by ~15%, and the generated
1014 // code size is increased by ~1% (measured on a combination of
1015 // different benchmarks).
1016
1017 // TODO(1217802): Optimize some special cases of operations
1018 // involving a smi literal (multiply by 2, shift by 0, etc.).
1019
1020 // Get the literal value.
1021 int int_value = Smi::cast(*value)->value();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001022 ASSERT(is_intn(int_value, kMaxSmiInlinedBits));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001023
1024 switch (op) {
1025 case Token::ADD: {
1026 DeferredCode* deferred = NULL;
1027 if (!reversed) {
1028 deferred = new DeferredInlinedSmiAdd(this, int_value, overwrite_mode);
1029 } else {
1030 deferred = new DeferredInlinedSmiAddReversed(this, int_value,
1031 overwrite_mode);
1032 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001033 frame_->Pop(eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001034 __ add(Operand(eax), Immediate(value));
1035 __ j(overflow, deferred->enter(), not_taken);
1036 __ test(eax, Immediate(kSmiTagMask));
1037 __ j(not_zero, deferred->enter(), not_taken);
1038 __ bind(deferred->exit());
ager@chromium.org7c537e22008-10-16 08:43:32 +00001039 frame_->Push(eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001040 break;
1041 }
1042
1043 case Token::SUB: {
1044 DeferredCode* deferred = NULL;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001045 frame_->Pop(eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001046 if (!reversed) {
1047 deferred = new DeferredInlinedSmiSub(this, int_value, overwrite_mode);
1048 __ sub(Operand(eax), Immediate(value));
1049 } else {
1050 deferred = new DeferredInlinedSmiSubReversed(this, edx, overwrite_mode);
1051 __ mov(edx, Operand(eax));
ager@chromium.org3bf7b912008-11-17 09:09:45 +00001052 __ mov(eax, Immediate(value));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001053 __ sub(eax, Operand(edx));
1054 }
1055 __ j(overflow, deferred->enter(), not_taken);
1056 __ test(eax, Immediate(kSmiTagMask));
1057 __ j(not_zero, deferred->enter(), not_taken);
1058 __ bind(deferred->exit());
ager@chromium.org7c537e22008-10-16 08:43:32 +00001059 frame_->Push(eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001060 break;
1061 }
1062
1063 case Token::SAR: {
1064 if (reversed) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001065 frame_->Pop(eax);
1066 frame_->Push(Immediate(value));
1067 frame_->Push(eax);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00001068 GenericBinaryOperation(op, type, overwrite_mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001069 } else {
1070 int shift_value = int_value & 0x1f; // only least significant 5 bits
1071 DeferredCode* deferred =
1072 new DeferredInlinedSmiOperation(this, Token::SAR, shift_value,
1073 overwrite_mode);
ager@chromium.org7c537e22008-10-16 08:43:32 +00001074 frame_->Pop(eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001075 __ test(eax, Immediate(kSmiTagMask));
1076 __ j(not_zero, deferred->enter(), not_taken);
1077 __ sar(eax, shift_value);
1078 __ and_(eax, ~kSmiTagMask);
1079 __ bind(deferred->exit());
ager@chromium.org7c537e22008-10-16 08:43:32 +00001080 frame_->Push(eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001081 }
1082 break;
1083 }
1084
1085 case Token::SHR: {
1086 if (reversed) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001087 frame_->Pop(eax);
1088 frame_->Push(Immediate(value));
1089 frame_->Push(eax);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00001090 GenericBinaryOperation(op, type, overwrite_mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001091 } else {
1092 int shift_value = int_value & 0x1f; // only least significant 5 bits
1093 DeferredCode* deferred =
1094 new DeferredInlinedSmiOperation(this, Token::SHR, shift_value,
1095 overwrite_mode);
ager@chromium.org7c537e22008-10-16 08:43:32 +00001096 frame_->Pop(eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001097 __ test(eax, Immediate(kSmiTagMask));
1098 __ mov(ebx, Operand(eax));
1099 __ j(not_zero, deferred->enter(), not_taken);
1100 __ sar(ebx, kSmiTagSize);
1101 __ shr(ebx, shift_value);
1102 __ test(ebx, Immediate(0xc0000000));
1103 __ j(not_zero, deferred->enter(), not_taken);
1104 // tag result and store it in TOS (eax)
1105 ASSERT(kSmiTagSize == times_2); // adjust code if not the case
ager@chromium.org3bf7b912008-11-17 09:09:45 +00001106 __ lea(eax, Operand(ebx, ebx, times_1, kSmiTag));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001107 __ bind(deferred->exit());
ager@chromium.org7c537e22008-10-16 08:43:32 +00001108 frame_->Push(eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001109 }
1110 break;
1111 }
1112
1113 case Token::SHL: {
1114 if (reversed) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001115 frame_->Pop(eax);
1116 frame_->Push(Immediate(value));
1117 frame_->Push(eax);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00001118 GenericBinaryOperation(op, type, overwrite_mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001119 } else {
1120 int shift_value = int_value & 0x1f; // only least significant 5 bits
1121 DeferredCode* deferred =
1122 new DeferredInlinedSmiOperation(this, Token::SHL, shift_value,
1123 overwrite_mode);
ager@chromium.org7c537e22008-10-16 08:43:32 +00001124 frame_->Pop(eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001125 __ test(eax, Immediate(kSmiTagMask));
1126 __ mov(ebx, Operand(eax));
1127 __ j(not_zero, deferred->enter(), not_taken);
1128 __ sar(ebx, kSmiTagSize);
1129 __ shl(ebx, shift_value);
1130 __ lea(ecx, Operand(ebx, 0x40000000));
1131 __ test(ecx, Immediate(0x80000000));
1132 __ j(not_zero, deferred->enter(), not_taken);
1133 // tag result and store it in TOS (eax)
1134 ASSERT(kSmiTagSize == times_2); // adjust code if not the case
ager@chromium.org3bf7b912008-11-17 09:09:45 +00001135 __ lea(eax, Operand(ebx, ebx, times_1, kSmiTag));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001136 __ bind(deferred->exit());
ager@chromium.org7c537e22008-10-16 08:43:32 +00001137 frame_->Push(eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001138 }
1139 break;
1140 }
1141
1142 case Token::BIT_OR:
1143 case Token::BIT_XOR:
1144 case Token::BIT_AND: {
1145 DeferredCode* deferred = NULL;
1146 if (!reversed) {
1147 deferred = new DeferredInlinedSmiOperation(this, op, int_value,
1148 overwrite_mode);
1149 } else {
1150 deferred = new DeferredInlinedSmiOperationReversed(this, op, int_value,
1151 overwrite_mode);
1152 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001153 frame_->Pop(eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001154 __ test(eax, Immediate(kSmiTagMask));
1155 __ j(not_zero, deferred->enter(), not_taken);
1156 if (op == Token::BIT_AND) {
1157 __ and_(Operand(eax), Immediate(value));
1158 } else if (op == Token::BIT_XOR) {
1159 __ xor_(Operand(eax), Immediate(value));
1160 } else {
1161 ASSERT(op == Token::BIT_OR);
1162 __ or_(Operand(eax), Immediate(value));
1163 }
1164 __ bind(deferred->exit());
ager@chromium.org7c537e22008-10-16 08:43:32 +00001165 frame_->Push(eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001166 break;
1167 }
1168
1169 default: {
1170 if (!reversed) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001171 frame_->Push(Immediate(value));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001172 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001173 frame_->Pop(eax);
1174 frame_->Push(Immediate(value));
1175 frame_->Push(eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001176 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00001177 GenericBinaryOperation(op, type, overwrite_mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001178 break;
1179 }
1180 }
1181}
1182
1183
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001184class CompareStub: public CodeStub {
1185 public:
1186 CompareStub(Condition cc, bool strict) : cc_(cc), strict_(strict) { }
1187
1188 void Generate(MacroAssembler* masm);
1189
1190 private:
1191 Condition cc_;
1192 bool strict_;
1193
1194 Major MajorKey() { return Compare; }
1195
1196 int MinorKey() {
1197 // Encode the three parameters in a unique 16 bit value.
1198 ASSERT(static_cast<int>(cc_) < (1 << 15));
1199 return (static_cast<int>(cc_) << 1) | (strict_ ? 1 : 0);
1200 }
1201
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001202#ifdef DEBUG
1203 void Print() {
1204 PrintF("CompareStub (cc %d), (strict %s)\n",
1205 static_cast<int>(cc_),
1206 strict_ ? "true" : "false");
1207 }
1208#endif
1209};
1210
1211
ager@chromium.org7c537e22008-10-16 08:43:32 +00001212void CodeGenerator::Comparison(Condition cc, bool strict) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001213 // Strict only makes sense for equality comparisons.
1214 ASSERT(!strict || cc == equal);
1215
mads.s.ager@gmail.com769cc962008-08-06 10:02:49 +00001216 // Implement '>' and '<=' by reversal to obtain ECMA-262 conversion order.
1217 if (cc == greater || cc == less_equal) {
1218 cc = ReverseCondition(cc);
ager@chromium.org7c537e22008-10-16 08:43:32 +00001219 frame_->Pop(edx);
1220 frame_->Pop(eax);
mads.s.ager@gmail.com769cc962008-08-06 10:02:49 +00001221 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001222 frame_->Pop(eax);
1223 frame_->Pop(edx);
mads.s.ager@gmail.com769cc962008-08-06 10:02:49 +00001224 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001225
ager@chromium.org7c537e22008-10-16 08:43:32 +00001226 // Check for the smi case.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001227 Label is_smi, done;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001228 __ mov(ecx, Operand(eax));
1229 __ or_(ecx, Operand(edx));
1230 __ test(ecx, Immediate(kSmiTagMask));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001231 __ j(zero, &is_smi, taken);
1232
1233 // When non-smi, call out to the compare stub. "parameters" setup by
1234 // calling code in edx and eax and "result" is returned in the flags.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001235 CompareStub stub(cc, strict);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001236 __ CallStub(&stub);
ager@chromium.org7c537e22008-10-16 08:43:32 +00001237 if (cc == equal) {
1238 __ test(eax, Operand(eax));
1239 } else {
1240 __ cmp(eax, 0);
1241 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001242 __ jmp(&done);
1243
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001244 // Test smi equality by pointer comparison.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001245 __ bind(&is_smi);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001246 __ cmp(edx, Operand(eax));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001247 // Fall through to |done|.
1248
1249 __ bind(&done);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001250 cc_reg_ = cc;
1251}
1252
1253
1254class SmiComparisonDeferred: public DeferredCode {
1255 public:
1256 SmiComparisonDeferred(CodeGenerator* generator,
1257 Condition cc,
1258 bool strict,
1259 int value)
1260 : DeferredCode(generator), cc_(cc), strict_(strict), value_(value) {
1261 set_comment("[ ComparisonDeferred");
1262 }
1263 virtual void Generate();
1264
1265 private:
1266 Condition cc_;
1267 bool strict_;
1268 int value_;
1269};
1270
1271
1272void SmiComparisonDeferred::Generate() {
1273 CompareStub stub(cc_, strict_);
1274 // Setup parameters and call stub.
1275 __ mov(edx, Operand(eax));
ager@chromium.org3bf7b912008-11-17 09:09:45 +00001276 __ Set(eax, Immediate(Smi::FromInt(value_)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001277 __ CallStub(&stub);
1278 __ cmp(eax, 0);
1279 // "result" is returned in the flags
1280}
1281
1282
ager@chromium.org7c537e22008-10-16 08:43:32 +00001283void CodeGenerator::SmiComparison(Condition cc,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001284 Handle<Object> value,
1285 bool strict) {
1286 // Strict only makes sense for equality comparisons.
1287 ASSERT(!strict || cc == equal);
1288
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001289 int int_value = Smi::cast(*value)->value();
1290 ASSERT(is_intn(int_value, kMaxSmiInlinedBits));
1291
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001292 SmiComparisonDeferred* deferred =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001293 new SmiComparisonDeferred(this, cc, strict, int_value);
ager@chromium.org7c537e22008-10-16 08:43:32 +00001294 frame_->Pop(eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001295 __ test(eax, Immediate(kSmiTagMask));
1296 __ j(not_zero, deferred->enter(), not_taken);
1297 // Test smi equality by pointer comparison.
1298 __ cmp(Operand(eax), Immediate(value));
1299 __ bind(deferred->exit());
1300 cc_reg_ = cc;
1301}
1302
1303
1304class CallFunctionStub: public CodeStub {
1305 public:
1306 explicit CallFunctionStub(int argc) : argc_(argc) { }
1307
1308 void Generate(MacroAssembler* masm);
1309
1310 private:
1311 int argc_;
1312
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001313#ifdef DEBUG
1314 void Print() { PrintF("CallFunctionStub (args %d)\n", argc_); }
1315#endif
1316
1317 Major MajorKey() { return CallFunction; }
1318 int MinorKey() { return argc_; }
1319};
1320
1321
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001322// Call the function just below TOS on the stack with the given
1323// arguments. The receiver is the TOS.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001324void CodeGenerator::CallWithArguments(ZoneList<Expression*>* args,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001325 int position) {
1326 // Push the arguments ("left-to-right") on the stack.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001327 for (int i = 0; i < args->length(); i++) {
1328 Load(args->at(i));
1329 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001330
1331 // Record the position for debugging purposes.
1332 __ RecordPosition(position);
1333
1334 // Use the shared code stub to call the function.
1335 CallFunctionStub call_function(args->length());
1336 __ CallStub(&call_function);
1337
1338 // Restore context and pop function from the stack.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001339 __ mov(esi, frame_->Context());
1340 __ mov(frame_->Top(), eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001341}
1342
1343
ager@chromium.org7c537e22008-10-16 08:43:32 +00001344void CodeGenerator::Branch(bool if_true, Label* L) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001345 ASSERT(has_cc());
1346 Condition cc = if_true ? cc_reg_ : NegateCondition(cc_reg_);
1347 __ j(cc, L);
1348 cc_reg_ = no_condition;
1349}
1350
1351
ager@chromium.org7c537e22008-10-16 08:43:32 +00001352void CodeGenerator::CheckStack() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001353 if (FLAG_check_stack) {
ager@chromium.org236ad962008-09-25 09:45:57 +00001354 Label stack_is_ok;
1355 StackCheckStub stub;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001356 ExternalReference stack_guard_limit =
1357 ExternalReference::address_of_stack_guard_limit();
1358 __ cmp(esp, Operand::StaticVariable(stack_guard_limit));
ager@chromium.org236ad962008-09-25 09:45:57 +00001359 __ j(above_equal, &stack_is_ok, taken);
1360 __ CallStub(&stub);
1361 __ bind(&stack_is_ok);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001362 }
1363}
1364
1365
ager@chromium.org7c537e22008-10-16 08:43:32 +00001366void CodeGenerator::VisitBlock(Block* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001367 Comment cmnt(masm_, "[ Block");
ager@chromium.org236ad962008-09-25 09:45:57 +00001368 RecordStatementPosition(node);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001369 node->set_break_stack_height(break_stack_height_);
1370 VisitStatements(node->statements());
1371 __ bind(node->break_target());
1372}
1373
1374
ager@chromium.org7c537e22008-10-16 08:43:32 +00001375void CodeGenerator::DeclareGlobals(Handle<FixedArray> pairs) {
1376 frame_->Push(Immediate(pairs));
1377 frame_->Push(esi);
1378 frame_->Push(Immediate(Smi::FromInt(is_eval() ? 1 : 0)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001379 __ CallRuntime(Runtime::kDeclareGlobals, 3);
1380 // Return value is ignored.
1381}
1382
1383
ager@chromium.org7c537e22008-10-16 08:43:32 +00001384void CodeGenerator::VisitDeclaration(Declaration* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001385 Comment cmnt(masm_, "[ Declaration");
1386 Variable* var = node->proxy()->var();
1387 ASSERT(var != NULL); // must have been resolved
1388 Slot* slot = var->slot();
1389
1390 // If it was not possible to allocate the variable at compile time,
1391 // we need to "declare" it at runtime to make sure it actually
1392 // exists in the local context.
1393 if (slot != NULL && slot->type() == Slot::LOOKUP) {
1394 // Variables with a "LOOKUP" slot were introduced as non-locals
1395 // during variable resolution and must have mode DYNAMIC.
1396 ASSERT(var->mode() == Variable::DYNAMIC);
1397 // For now, just do a runtime call.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001398 frame_->Push(esi);
1399 frame_->Push(Immediate(var->name()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001400 // Declaration nodes are always introduced in one of two modes.
1401 ASSERT(node->mode() == Variable::VAR || node->mode() == Variable::CONST);
1402 PropertyAttributes attr = node->mode() == Variable::VAR ? NONE : READ_ONLY;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001403 frame_->Push(Immediate(Smi::FromInt(attr)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001404 // Push initial value, if any.
1405 // Note: For variables we must not push an initial value (such as
1406 // 'undefined') because we may have a (legal) redeclaration and we
1407 // must not destroy the current value.
1408 if (node->mode() == Variable::CONST) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001409 frame_->Push(Immediate(Factory::the_hole_value()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001410 } else if (node->fun() != NULL) {
1411 Load(node->fun());
1412 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001413 frame_->Push(Immediate(0)); // no initial value!
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001414 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001415 __ CallRuntime(Runtime::kDeclareContextSlot, 4);
1416 // Ignore the return value (declarations are statements).
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001417 return;
1418 }
1419
1420 ASSERT(!var->is_global());
1421
1422 // If we have a function or a constant, we need to initialize the variable.
1423 Expression* val = NULL;
1424 if (node->mode() == Variable::CONST) {
1425 val = new Literal(Factory::the_hole_value());
1426 } else {
1427 val = node->fun(); // NULL if we don't have a function
1428 }
1429
1430 if (val != NULL) {
1431 // Set initial value.
1432 Reference target(this, node->proxy());
ager@chromium.org7c537e22008-10-16 08:43:32 +00001433 ASSERT(target.is_slot());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001434 Load(val);
ager@chromium.org7c537e22008-10-16 08:43:32 +00001435 target.SetValue(NOT_CONST_INIT);
1436 // Get rid of the assigned value (declarations are statements). It's
1437 // safe to pop the value lying on top of the reference before unloading
1438 // the reference itself (which preserves the top of stack) because we
1439 // know that it is a zero-sized reference.
1440 frame_->Pop();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001441 }
1442}
1443
1444
ager@chromium.org7c537e22008-10-16 08:43:32 +00001445void CodeGenerator::VisitExpressionStatement(ExpressionStatement* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001446 Comment cmnt(masm_, "[ ExpressionStatement");
ager@chromium.org236ad962008-09-25 09:45:57 +00001447 RecordStatementPosition(node);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001448 Expression* expression = node->expression();
1449 expression->MarkAsStatement();
1450 Load(expression);
ager@chromium.org7c537e22008-10-16 08:43:32 +00001451 // Remove the lingering expression result from the top of stack.
1452 frame_->Pop();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001453}
1454
1455
ager@chromium.org7c537e22008-10-16 08:43:32 +00001456void CodeGenerator::VisitEmptyStatement(EmptyStatement* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001457 Comment cmnt(masm_, "// EmptyStatement");
1458 // nothing to do
1459}
1460
1461
ager@chromium.org7c537e22008-10-16 08:43:32 +00001462void CodeGenerator::VisitIfStatement(IfStatement* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001463 Comment cmnt(masm_, "[ IfStatement");
1464 // Generate different code depending on which
1465 // parts of the if statement are present or not.
1466 bool has_then_stm = node->HasThenStatement();
1467 bool has_else_stm = node->HasElseStatement();
1468
ager@chromium.org236ad962008-09-25 09:45:57 +00001469 RecordStatementPosition(node);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001470 Label exit;
1471 if (has_then_stm && has_else_stm) {
1472 Label then;
1473 Label else_;
1474 // if (cond)
ager@chromium.org7c537e22008-10-16 08:43:32 +00001475 LoadCondition(node->condition(), NOT_INSIDE_TYPEOF, &then, &else_, true);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001476 Branch(false, &else_);
1477 // then
1478 __ bind(&then);
1479 Visit(node->then_statement());
1480 __ jmp(&exit);
1481 // else
1482 __ bind(&else_);
1483 Visit(node->else_statement());
1484
1485 } else if (has_then_stm) {
1486 ASSERT(!has_else_stm);
1487 Label then;
1488 // if (cond)
ager@chromium.org7c537e22008-10-16 08:43:32 +00001489 LoadCondition(node->condition(), NOT_INSIDE_TYPEOF, &then, &exit, true);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001490 Branch(false, &exit);
1491 // then
1492 __ bind(&then);
1493 Visit(node->then_statement());
1494
1495 } else if (has_else_stm) {
1496 ASSERT(!has_then_stm);
1497 Label else_;
1498 // if (!cond)
ager@chromium.org7c537e22008-10-16 08:43:32 +00001499 LoadCondition(node->condition(), NOT_INSIDE_TYPEOF, &exit, &else_, true);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001500 Branch(true, &exit);
1501 // else
1502 __ bind(&else_);
1503 Visit(node->else_statement());
1504
1505 } else {
1506 ASSERT(!has_then_stm && !has_else_stm);
1507 // if (cond)
ager@chromium.org7c537e22008-10-16 08:43:32 +00001508 LoadCondition(node->condition(), NOT_INSIDE_TYPEOF, &exit, &exit, false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001509 if (has_cc()) {
1510 cc_reg_ = no_condition;
1511 } else {
1512 // No cc value set up, that means the boolean was pushed.
1513 // Pop it again, since it is not going to be used.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001514 frame_->Pop();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001515 }
1516 }
1517
1518 // end
1519 __ bind(&exit);
1520}
1521
1522
ager@chromium.org7c537e22008-10-16 08:43:32 +00001523void CodeGenerator::CleanStack(int num_bytes) {
1524 ASSERT(num_bytes % kPointerSize == 0);
1525 frame_->Drop(num_bytes / kPointerSize);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001526}
1527
1528
ager@chromium.org7c537e22008-10-16 08:43:32 +00001529void CodeGenerator::VisitContinueStatement(ContinueStatement* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001530 Comment cmnt(masm_, "[ ContinueStatement");
ager@chromium.org236ad962008-09-25 09:45:57 +00001531 RecordStatementPosition(node);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001532 CleanStack(break_stack_height_ - node->target()->break_stack_height());
1533 __ jmp(node->target()->continue_target());
1534}
1535
1536
ager@chromium.org7c537e22008-10-16 08:43:32 +00001537void CodeGenerator::VisitBreakStatement(BreakStatement* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001538 Comment cmnt(masm_, "[ BreakStatement");
ager@chromium.org236ad962008-09-25 09:45:57 +00001539 RecordStatementPosition(node);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001540 CleanStack(break_stack_height_ - node->target()->break_stack_height());
1541 __ jmp(node->target()->break_target());
1542}
1543
1544
ager@chromium.org7c537e22008-10-16 08:43:32 +00001545void CodeGenerator::VisitReturnStatement(ReturnStatement* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001546 Comment cmnt(masm_, "[ ReturnStatement");
ager@chromium.org236ad962008-09-25 09:45:57 +00001547 RecordStatementPosition(node);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001548 Load(node->expression());
1549
1550 // Move the function result into eax
ager@chromium.org7c537e22008-10-16 08:43:32 +00001551 frame_->Pop(eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001552
1553 // If we're inside a try statement or the return instruction
1554 // sequence has been generated, we just jump to that
1555 // point. Otherwise, we generate the return instruction sequence and
1556 // bind the function return label.
1557 if (is_inside_try_ || function_return_.is_bound()) {
1558 __ jmp(&function_return_);
1559 } else {
1560 __ bind(&function_return_);
1561 if (FLAG_trace) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001562 frame_->Push(eax); // undo the pop(eax) from above
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001563 __ CallRuntime(Runtime::kTraceExit, 1);
1564 }
1565
1566 // Add a label for checking the size of the code used for returning.
1567 Label check_exit_codesize;
1568 __ bind(&check_exit_codesize);
1569
1570 // Leave the frame and return popping the arguments and the
1571 // receiver.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001572 frame_->Exit();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001573 __ ret((scope_->num_parameters() + 1) * kPointerSize);
1574
1575 // Check that the size of the code used for returning matches what is
1576 // expected by the debugger.
1577 ASSERT_EQ(Debug::kIa32JSReturnSequenceLength,
1578 __ SizeOfCodeGeneratedSince(&check_exit_codesize));
1579 }
1580}
1581
1582
ager@chromium.org7c537e22008-10-16 08:43:32 +00001583void CodeGenerator::VisitWithEnterStatement(WithEnterStatement* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001584 Comment cmnt(masm_, "[ WithEnterStatement");
ager@chromium.org236ad962008-09-25 09:45:57 +00001585 RecordStatementPosition(node);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001586 Load(node->expression());
kasper.lund7276f142008-07-30 08:49:36 +00001587 __ CallRuntime(Runtime::kPushContext, 1);
1588
1589 if (kDebug) {
1590 Label verified_true;
1591 // Verify eax and esi are the same in debug mode
1592 __ cmp(eax, Operand(esi));
1593 __ j(equal, &verified_true);
1594 __ int3();
1595 __ bind(&verified_true);
1596 }
1597
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001598 // Update context local.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001599 __ mov(frame_->Context(), esi);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001600}
1601
1602
ager@chromium.org7c537e22008-10-16 08:43:32 +00001603void CodeGenerator::VisitWithExitStatement(WithExitStatement* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001604 Comment cmnt(masm_, "[ WithExitStatement");
1605 // Pop context.
1606 __ mov(esi, ContextOperand(esi, Context::PREVIOUS_INDEX));
1607 // Update context local.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001608 __ mov(frame_->Context(), esi);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001609}
1610
ager@chromium.org7c537e22008-10-16 08:43:32 +00001611int CodeGenerator::FastCaseSwitchMaxOverheadFactor() {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001612 return kFastSwitchMaxOverheadFactor;
1613}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001614
ager@chromium.org7c537e22008-10-16 08:43:32 +00001615int CodeGenerator::FastCaseSwitchMinCaseCount() {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001616 return kFastSwitchMinCaseCount;
1617}
1618
1619// Generate a computed jump to a switch case.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001620void CodeGenerator::GenerateFastCaseSwitchJumpTable(
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001621 SwitchStatement* node,
1622 int min_index,
1623 int range,
1624 Label* fail_label,
1625 Vector<Label*> case_targets,
1626 Vector<Label> case_labels) {
ager@chromium.org236ad962008-09-25 09:45:57 +00001627 // Notice: Internal references, used by both the jmp instruction and
1628 // the table entries, need to be relocated if the buffer grows. This
1629 // prevents the forward use of Labels, since a displacement cannot
1630 // survive relocation, and it also cannot safely be distinguished
1631 // from a real address. Instead we put in zero-values as
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001632 // placeholders, and fill in the addresses after the labels have been
ager@chromium.org236ad962008-09-25 09:45:57 +00001633 // bound.
1634
ager@chromium.org870a0b62008-11-04 11:43:05 +00001635 frame_->Pop(eax); // supposed Smi
ager@chromium.org236ad962008-09-25 09:45:57 +00001636 // check range of value, if outside [0..length-1] jump to default/end label.
1637 ASSERT(kSmiTagSize == 1 && kSmiTag == 0);
ager@chromium.org870a0b62008-11-04 11:43:05 +00001638
1639 // Test whether input is a HeapNumber that is really a Smi
1640 Label is_smi;
1641 __ test(eax, Immediate(kSmiTagMask));
1642 __ j(equal, &is_smi);
1643 // It's a heap object, not a Smi or a Failure
1644 __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset));
1645 __ movzx_b(ebx, FieldOperand(ebx, Map::kInstanceTypeOffset));
1646 __ cmp(ebx, HEAP_NUMBER_TYPE);
1647 __ j(not_equal, fail_label);
1648 // eax points to a heap number.
1649 __ push(eax);
1650 __ CallRuntime(Runtime::kNumberToSmi, 1);
1651 __ bind(&is_smi);
1652
ager@chromium.org236ad962008-09-25 09:45:57 +00001653 if (min_index != 0) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001654 __ sub(Operand(eax), Immediate(min_index << kSmiTagSize));
ager@chromium.org236ad962008-09-25 09:45:57 +00001655 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001656 __ test(eax, Immediate(0x80000000 | kSmiTagMask)); // negative or not Smi
ager@chromium.org236ad962008-09-25 09:45:57 +00001657 __ j(not_equal, fail_label, not_taken);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001658 __ cmp(eax, range << kSmiTagSize);
ager@chromium.org236ad962008-09-25 09:45:57 +00001659 __ j(greater_equal, fail_label, not_taken);
1660
1661 // 0 is placeholder.
ager@chromium.org3bf7b912008-11-17 09:09:45 +00001662 __ jmp(Operand(eax, eax, times_1, 0x0, RelocInfo::INTERNAL_REFERENCE));
ager@chromium.org236ad962008-09-25 09:45:57 +00001663 // calculate address to overwrite later with actual address of table.
1664 int32_t jump_table_ref = __ pc_offset() - sizeof(int32_t);
1665
1666 __ Align(4);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001667 Label table_start;
ager@chromium.org236ad962008-09-25 09:45:57 +00001668 __ bind(&table_start);
1669 __ WriteInternalReference(jump_table_ref, table_start);
1670
1671 for (int i = 0; i < range; i++) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001672 // table entry, 0 is placeholder for case address
1673 __ dd(0x0, RelocInfo::INTERNAL_REFERENCE);
ager@chromium.org236ad962008-09-25 09:45:57 +00001674 }
1675
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001676 GenerateFastCaseSwitchCases(node, case_labels);
1677
1678 for (int i = 0, entry_pos = table_start.pos();
1679 i < range; i++, entry_pos += sizeof(uint32_t)) {
1680 __ WriteInternalReference(entry_pos, *case_targets[i]);
ager@chromium.org236ad962008-09-25 09:45:57 +00001681 }
ager@chromium.org236ad962008-09-25 09:45:57 +00001682}
1683
1684
ager@chromium.org7c537e22008-10-16 08:43:32 +00001685void CodeGenerator::VisitSwitchStatement(SwitchStatement* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001686 Comment cmnt(masm_, "[ SwitchStatement");
ager@chromium.org236ad962008-09-25 09:45:57 +00001687 RecordStatementPosition(node);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001688 node->set_break_stack_height(break_stack_height_);
1689
1690 Load(node->tag());
1691
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001692 if (TryGenerateFastCaseSwitchStatement(node)) {
ager@chromium.org236ad962008-09-25 09:45:57 +00001693 return;
1694 }
1695
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001696 Label next, fall_through, default_case;
1697 ZoneList<CaseClause*>* cases = node->cases();
1698 int length = cases->length();
1699
1700 for (int i = 0; i < length; i++) {
1701 CaseClause* clause = cases->at(i);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001702 Comment cmnt(masm_, "[ case clause");
1703
1704 if (clause->is_default()) {
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00001705 // Continue matching cases. The program will execute the default case's
1706 // statements if it does not match any of the cases.
1707 __ jmp(&next);
1708
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001709 // Bind the default case label, so we can branch to it when we
1710 // have compared against all other cases.
1711 ASSERT(default_case.is_unused()); // at most one default clause
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00001712 __ bind(&default_case);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001713 } else {
1714 __ bind(&next);
1715 next.Unuse();
ager@chromium.org7c537e22008-10-16 08:43:32 +00001716 __ mov(eax, frame_->Top());
1717 frame_->Push(eax); // duplicate TOS
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001718 Load(clause->label());
1719 Comparison(equal, true);
1720 Branch(false, &next);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001721 }
1722
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00001723 // Entering the case statement for the first time. Remove the switch value
1724 // from the stack.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001725 frame_->Pop(eax);
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00001726
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001727 // Generate code for the body.
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00001728 // This is also the target for the fall through from the previous case's
1729 // statements which has to skip over the matching code and the popping of
1730 // the switch value.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001731 __ bind(&fall_through);
1732 fall_through.Unuse();
1733 VisitStatements(clause->statements());
1734 __ jmp(&fall_through);
1735 }
1736
1737 __ bind(&next);
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00001738 // Reached the end of the case statements without matching any of the cases.
1739 if (default_case.is_bound()) {
1740 // A default case exists -> execute its statements.
1741 __ jmp(&default_case);
1742 } else {
1743 // Remove the switch value from the stack.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001744 frame_->Pop();
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00001745 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001746
1747 __ bind(&fall_through);
1748 __ bind(node->break_target());
1749}
1750
1751
ager@chromium.org7c537e22008-10-16 08:43:32 +00001752void CodeGenerator::VisitLoopStatement(LoopStatement* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001753 Comment cmnt(masm_, "[ LoopStatement");
ager@chromium.org236ad962008-09-25 09:45:57 +00001754 RecordStatementPosition(node);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001755 node->set_break_stack_height(break_stack_height_);
1756
1757 // simple condition analysis
1758 enum { ALWAYS_TRUE, ALWAYS_FALSE, DONT_KNOW } info = DONT_KNOW;
1759 if (node->cond() == NULL) {
1760 ASSERT(node->type() == LoopStatement::FOR_LOOP);
1761 info = ALWAYS_TRUE;
1762 } else {
1763 Literal* lit = node->cond()->AsLiteral();
1764 if (lit != NULL) {
1765 if (lit->IsTrue()) {
1766 info = ALWAYS_TRUE;
1767 } else if (lit->IsFalse()) {
1768 info = ALWAYS_FALSE;
1769 }
1770 }
1771 }
1772
1773 Label loop, entry;
1774
1775 // init
1776 if (node->init() != NULL) {
1777 ASSERT(node->type() == LoopStatement::FOR_LOOP);
1778 Visit(node->init());
1779 }
1780 if (node->type() != LoopStatement::DO_LOOP && info != ALWAYS_TRUE) {
1781 __ jmp(&entry);
1782 }
1783
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00001784 IncrementLoopNesting();
1785
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001786 // body
1787 __ bind(&loop);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001788 CheckStack(); // TODO(1222600): ignore if body contains calls.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001789 Visit(node->body());
1790
1791 // next
1792 __ bind(node->continue_target());
1793 if (node->next() != NULL) {
1794 // Record source position of the statement as this code which is after the
1795 // code for the body actually belongs to the loop statement and not the
1796 // body.
ager@chromium.org236ad962008-09-25 09:45:57 +00001797 RecordStatementPosition(node);
1798 __ RecordPosition(node->statement_pos());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001799 ASSERT(node->type() == LoopStatement::FOR_LOOP);
1800 Visit(node->next());
1801 }
1802
1803 // cond
1804 __ bind(&entry);
1805 switch (info) {
1806 case ALWAYS_TRUE:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001807 __ jmp(&loop);
1808 break;
1809 case ALWAYS_FALSE:
1810 break;
1811 case DONT_KNOW:
ager@chromium.org7c537e22008-10-16 08:43:32 +00001812 LoadCondition(node->cond(), NOT_INSIDE_TYPEOF, &loop,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001813 node->break_target(), true);
1814 Branch(true, &loop);
1815 break;
1816 }
1817
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00001818 DecrementLoopNesting();
1819
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001820 // exit
1821 __ bind(node->break_target());
1822}
1823
1824
ager@chromium.org7c537e22008-10-16 08:43:32 +00001825void CodeGenerator::VisitForInStatement(ForInStatement* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001826 Comment cmnt(masm_, "[ ForInStatement");
ager@chromium.org236ad962008-09-25 09:45:57 +00001827 RecordStatementPosition(node);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001828
1829 // We keep stuff on the stack while the body is executing.
1830 // Record it, so that a break/continue crossing this statement
1831 // can restore the stack.
1832 const int kForInStackSize = 5 * kPointerSize;
1833 break_stack_height_ += kForInStackSize;
1834 node->set_break_stack_height(break_stack_height_);
1835
1836 Label loop, next, entry, cleanup, exit, primitive, jsobject;
1837 Label end_del_check, fixed_array;
1838
1839 // Get the object to enumerate over (converted to JSObject).
1840 Load(node->enumerable());
1841
1842 // Both SpiderMonkey and kjs ignore null and undefined in contrast
1843 // to the specification. 12.6.4 mandates a call to ToObject.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001844 frame_->Pop(eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001845
1846 // eax: value to be iterated over
1847 __ cmp(eax, Factory::undefined_value());
1848 __ j(equal, &exit);
1849 __ cmp(eax, Factory::null_value());
1850 __ j(equal, &exit);
1851
1852 // Stack layout in body:
kasper.lund7276f142008-07-30 08:49:36 +00001853 // [iteration counter (smi)] <- slot 0
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001854 // [length of array] <- slot 1
1855 // [FixedArray] <- slot 2
1856 // [Map or 0] <- slot 3
1857 // [Object] <- slot 4
1858
1859 // Check if enumerable is already a JSObject
1860 // eax: value to be iterated over
1861 __ test(eax, Immediate(kSmiTagMask));
1862 __ j(zero, &primitive);
1863 __ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset));
1864 __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
ager@chromium.orgc27e4e72008-09-04 13:52:27 +00001865 __ cmp(ecx, FIRST_JS_OBJECT_TYPE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001866 __ j(above_equal, &jsobject);
1867
1868 __ bind(&primitive);
ager@chromium.org7c537e22008-10-16 08:43:32 +00001869 frame_->Push(eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001870 __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION);
1871 // function call returns the value in eax, which is where we want it below
1872
1873
1874 __ bind(&jsobject);
1875
1876 // Get the set of properties (as a FixedArray or Map).
1877 // eax: value to be iterated over
ager@chromium.org7c537e22008-10-16 08:43:32 +00001878 frame_->Push(eax); // push the object being iterated over (slot 4)
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001879
ager@chromium.org7c537e22008-10-16 08:43:32 +00001880 frame_->Push(eax); // push the Object (slot 4) for the runtime call
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001881 __ CallRuntime(Runtime::kGetPropertyNamesFast, 1);
1882
1883 // If we got a Map, we can do a fast modification check.
1884 // Otherwise, we got a FixedArray, and we have to do a slow check.
1885 // eax: map or fixed array (result from call to
1886 // Runtime::kGetPropertyNamesFast)
1887 __ mov(edx, Operand(eax));
1888 __ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset));
1889 __ cmp(ecx, Factory::meta_map());
1890 __ j(not_equal, &fixed_array);
1891
1892 // Get enum cache
1893 // eax: map (result from call to Runtime::kGetPropertyNamesFast)
1894 __ mov(ecx, Operand(eax));
1895 __ mov(ecx, FieldOperand(ecx, Map::kInstanceDescriptorsOffset));
1896 // Get the bridge array held in the enumeration index field.
1897 __ mov(ecx, FieldOperand(ecx, DescriptorArray::kEnumerationIndexOffset));
1898 // Get the cache from the bridge array.
1899 __ mov(edx, FieldOperand(ecx, DescriptorArray::kEnumCacheBridgeCacheOffset));
1900
ager@chromium.org7c537e22008-10-16 08:43:32 +00001901 frame_->Push(eax); // <- slot 3
1902 frame_->Push(edx); // <- slot 2
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001903 __ mov(eax, FieldOperand(edx, FixedArray::kLengthOffset));
1904 __ shl(eax, kSmiTagSize);
ager@chromium.org7c537e22008-10-16 08:43:32 +00001905 frame_->Push(eax); // <- slot 1
1906 frame_->Push(Immediate(Smi::FromInt(0))); // <- slot 0
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001907 __ jmp(&entry);
1908
1909
1910 __ bind(&fixed_array);
1911
1912 // eax: fixed array (result from call to Runtime::kGetPropertyNamesFast)
ager@chromium.org7c537e22008-10-16 08:43:32 +00001913 frame_->Push(Immediate(Smi::FromInt(0))); // <- slot 3
1914 frame_->Push(eax); // <- slot 2
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001915
1916 // Push the length of the array and the initial index onto the stack.
1917 __ mov(eax, FieldOperand(eax, FixedArray::kLengthOffset));
1918 __ shl(eax, kSmiTagSize);
ager@chromium.org7c537e22008-10-16 08:43:32 +00001919 frame_->Push(eax); // <- slot 1
1920 frame_->Push(Immediate(Smi::FromInt(0))); // <- slot 0
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001921 __ jmp(&entry);
1922
1923 // Body.
1924 __ bind(&loop);
1925 Visit(node->body());
1926
1927 // Next.
1928 __ bind(node->continue_target());
1929 __ bind(&next);
ager@chromium.org7c537e22008-10-16 08:43:32 +00001930 frame_->Pop(eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001931 __ add(Operand(eax), Immediate(Smi::FromInt(1)));
ager@chromium.org7c537e22008-10-16 08:43:32 +00001932 frame_->Push(eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001933
1934 // Condition.
1935 __ bind(&entry);
1936
ager@chromium.org7c537e22008-10-16 08:43:32 +00001937 __ mov(eax, frame_->Element(0)); // load the current count
1938 __ cmp(eax, frame_->Element(1)); // compare to the array length
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001939 __ j(above_equal, &cleanup);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001940
1941 // Get the i'th entry of the array.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001942 __ mov(edx, frame_->Element(2));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001943 __ mov(ebx, Operand(edx, eax, times_2,
1944 FixedArray::kHeaderSize - kHeapObjectTag));
1945
1946 // Get the expected map from the stack or a zero map in the
1947 // permanent slow case eax: current iteration count ebx: i'th entry
1948 // of the enum cache
ager@chromium.org7c537e22008-10-16 08:43:32 +00001949 __ mov(edx, frame_->Element(3));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001950 // Check if the expected map still matches that of the enumerable.
1951 // If not, we have to filter the key.
1952 // eax: current iteration count
1953 // ebx: i'th entry of the enum cache
1954 // edx: expected map value
ager@chromium.org7c537e22008-10-16 08:43:32 +00001955 __ mov(ecx, frame_->Element(4));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001956 __ mov(ecx, FieldOperand(ecx, HeapObject::kMapOffset));
1957 __ cmp(ecx, Operand(edx));
1958 __ j(equal, &end_del_check);
1959
1960 // Convert the entry to a string (or null if it isn't a property anymore).
ager@chromium.org7c537e22008-10-16 08:43:32 +00001961 frame_->Push(frame_->Element(4)); // push enumerable
1962 frame_->Push(ebx); // push entry
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001963 __ InvokeBuiltin(Builtins::FILTER_KEY, CALL_FUNCTION);
1964 __ mov(ebx, Operand(eax));
1965
1966 // If the property has been removed while iterating, we just skip it.
1967 __ cmp(ebx, Factory::null_value());
1968 __ j(equal, &next);
1969
1970
1971 __ bind(&end_del_check);
1972
1973 // Store the entry in the 'each' expression and take another spin in the loop.
1974 // edx: i'th entry of the enum cache (or string there of)
ager@chromium.org7c537e22008-10-16 08:43:32 +00001975 frame_->Push(ebx);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001976 { Reference each(this, node->each());
1977 if (!each.is_illegal()) {
1978 if (each.size() > 0) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001979 frame_->Push(frame_->Element(each.size()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001980 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001981 // If the reference was to a slot we rely on the convenient property
1982 // that it doesn't matter whether a value (eg, ebx pushed above) is
1983 // right on top of or right underneath a zero-sized reference.
1984 each.SetValue(NOT_CONST_INIT);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001985 if (each.size() > 0) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001986 // It's safe to pop the value lying on top of the reference before
1987 // unloading the reference itself (which preserves the top of stack,
1988 // ie, now the topmost value of the non-zero sized reference), since
1989 // we will discard the top of stack after unloading the reference
1990 // anyway.
1991 frame_->Pop();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001992 }
1993 }
1994 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001995 // Discard the i'th entry pushed above or else the remainder of the
1996 // reference, whichever is currently on top of the stack.
1997 frame_->Pop();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001998 CheckStack(); // TODO(1222600): ignore if body contains calls.
1999 __ jmp(&loop);
2000
2001 // Cleanup.
2002 __ bind(&cleanup);
2003 __ bind(node->break_target());
ager@chromium.org7c537e22008-10-16 08:43:32 +00002004 frame_->Drop(5);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002005
2006 // Exit.
2007 __ bind(&exit);
2008
2009 break_stack_height_ -= kForInStackSize;
2010}
2011
2012
ager@chromium.org7c537e22008-10-16 08:43:32 +00002013void CodeGenerator::VisitTryCatch(TryCatch* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002014 Comment cmnt(masm_, "[ TryCatch");
2015
2016 Label try_block, exit;
2017
2018 __ call(&try_block);
2019 // --- Catch block ---
ager@chromium.org7c537e22008-10-16 08:43:32 +00002020 frame_->Push(eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002021
2022 // Store the caught exception in the catch variable.
2023 { Reference ref(this, node->catch_var());
ager@chromium.org7c537e22008-10-16 08:43:32 +00002024 ASSERT(ref.is_slot());
2025 // Load the exception to the top of the stack. Here we make use of the
2026 // convenient property that it doesn't matter whether a value is
2027 // immediately on top of or underneath a zero-sized reference.
2028 ref.SetValue(NOT_CONST_INIT);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002029 }
2030
2031 // Remove the exception from the stack.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002032 frame_->Pop();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002033
2034 VisitStatements(node->catch_block()->statements());
2035 __ jmp(&exit);
2036
2037
2038 // --- Try block ---
2039 __ bind(&try_block);
2040
2041 __ PushTryHandler(IN_JAVASCRIPT, TRY_CATCH_HANDLER);
2042 // TODO(1222589): remove the reliance of PushTryHandler on a cached TOS
ager@chromium.org7c537e22008-10-16 08:43:32 +00002043 frame_->Push(eax); //
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002044
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002045 // Shadow the labels for all escapes from the try block, including
2046 // returns. During shadowing, the original label is hidden as the
2047 // LabelShadow and operations on the original actually affect the
2048 // shadowing label.
2049 //
2050 // We should probably try to unify the escaping labels and the return
2051 // label.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002052 int nof_escapes = node->escaping_labels()->length();
2053 List<LabelShadow*> shadows(1 + nof_escapes);
2054 shadows.Add(new LabelShadow(&function_return_));
2055 for (int i = 0; i < nof_escapes; i++) {
2056 shadows.Add(new LabelShadow(node->escaping_labels()->at(i)));
2057 }
2058
2059 // Generate code for the statements in the try block.
2060 bool was_inside_try = is_inside_try_;
2061 is_inside_try_ = true;
2062 VisitStatements(node->try_block()->statements());
2063 is_inside_try_ = was_inside_try;
2064
2065 // Stop the introduced shadowing and count the number of required unlinks.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002066 // After shadowing stops, the original labels are unshadowed and the
2067 // LabelShadows represent the formerly shadowing labels.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002068 int nof_unlinks = 0;
2069 for (int i = 0; i <= nof_escapes; i++) {
2070 shadows[i]->StopShadowing();
2071 if (shadows[i]->is_linked()) nof_unlinks++;
2072 }
2073
kasper.lund7276f142008-07-30 08:49:36 +00002074 // Get an external reference to the handler address.
2075 ExternalReference handler_address(Top::k_handler_address);
2076
2077 // Make sure that there's nothing left on the stack above the
2078 // handler structure.
2079 if (FLAG_debug_code) {
2080 __ mov(eax, Operand::StaticVariable(handler_address));
2081 __ lea(eax, Operand(eax, StackHandlerConstants::kAddressDisplacement));
2082 __ cmp(esp, Operand(eax));
2083 __ Assert(equal, "stack pointer should point to top handler");
2084 }
2085
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002086 // Unlink from try chain.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002087 frame_->Pop(eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002088 __ mov(Operand::StaticVariable(handler_address), eax); // TOS == next_sp
ager@chromium.org7c537e22008-10-16 08:43:32 +00002089 frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002090 // next_sp popped.
2091 if (nof_unlinks > 0) __ jmp(&exit);
2092
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002093 // Generate unlink code for the (formerly) shadowing labels that have been
2094 // jumped to.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002095 for (int i = 0; i <= nof_escapes; i++) {
2096 if (shadows[i]->is_linked()) {
2097 // Unlink from try chain; be careful not to destroy the TOS.
2098 __ bind(shadows[i]);
2099
2100 // Reload sp from the top handler, because some statements that we
2101 // break from (eg, for...in) may have left stuff on the stack.
2102 __ mov(edx, Operand::StaticVariable(handler_address));
2103 const int kNextOffset = StackHandlerConstants::kNextOffset +
2104 StackHandlerConstants::kAddressDisplacement;
2105 __ lea(esp, Operand(edx, kNextOffset));
2106
ager@chromium.org7c537e22008-10-16 08:43:32 +00002107 frame_->Pop(Operand::StaticVariable(handler_address));
2108 frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002109 // next_sp popped.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002110 __ jmp(shadows[i]->original_label());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002111 }
2112 }
2113
2114 __ bind(&exit);
2115}
2116
2117
ager@chromium.org7c537e22008-10-16 08:43:32 +00002118void CodeGenerator::VisitTryFinally(TryFinally* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002119 Comment cmnt(masm_, "[ TryFinally");
2120
2121 // State: Used to keep track of reason for entering the finally
2122 // block. Should probably be extended to hold information for
2123 // break/continue from within the try block.
2124 enum { FALLING, THROWING, JUMPING };
2125
2126 Label exit, unlink, try_block, finally_block;
2127
2128 __ call(&try_block);
2129
ager@chromium.org7c537e22008-10-16 08:43:32 +00002130 frame_->Push(eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002131 // In case of thrown exceptions, this is where we continue.
2132 __ Set(ecx, Immediate(Smi::FromInt(THROWING)));
2133 __ jmp(&finally_block);
2134
2135
2136 // --- Try block ---
2137 __ bind(&try_block);
2138
2139 __ PushTryHandler(IN_JAVASCRIPT, TRY_FINALLY_HANDLER);
2140 // TODO(1222589): remove the reliance of PushTryHandler on a cached TOS
ager@chromium.org7c537e22008-10-16 08:43:32 +00002141 frame_->Push(eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002142
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002143 // Shadow the labels for all escapes from the try block, including
2144 // returns. During shadowing, the original label is hidden as the
2145 // LabelShadow and operations on the original actually affect the
2146 // shadowing label.
2147 //
2148 // We should probably try to unify the escaping labels and the return
2149 // label.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002150 int nof_escapes = node->escaping_labels()->length();
2151 List<LabelShadow*> shadows(1 + nof_escapes);
2152 shadows.Add(new LabelShadow(&function_return_));
2153 for (int i = 0; i < nof_escapes; i++) {
2154 shadows.Add(new LabelShadow(node->escaping_labels()->at(i)));
2155 }
2156
2157 // Generate code for the statements in the try block.
2158 bool was_inside_try = is_inside_try_;
2159 is_inside_try_ = true;
2160 VisitStatements(node->try_block()->statements());
2161 is_inside_try_ = was_inside_try;
2162
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002163 // Stop the introduced shadowing and count the number of required unlinks.
2164 // After shadowing stops, the original labels are unshadowed and the
2165 // LabelShadows represent the formerly shadowing labels.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002166 int nof_unlinks = 0;
2167 for (int i = 0; i <= nof_escapes; i++) {
2168 shadows[i]->StopShadowing();
2169 if (shadows[i]->is_linked()) nof_unlinks++;
2170 }
2171
2172 // Set the state on the stack to FALLING.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002173 frame_->Push(Immediate(Factory::undefined_value())); // fake TOS
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002174 __ Set(ecx, Immediate(Smi::FromInt(FALLING)));
2175 if (nof_unlinks > 0) __ jmp(&unlink);
2176
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002177 // Generate code to set the state for the (formerly) shadowing labels that
2178 // have been jumped to.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002179 for (int i = 0; i <= nof_escapes; i++) {
2180 if (shadows[i]->is_linked()) {
2181 __ bind(shadows[i]);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002182 if (shadows[i]->original_label() == &function_return_) {
2183 // If this label shadowed the function return, materialize the
2184 // return value on the stack.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002185 frame_->Push(eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002186 } else {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002187 // Fake TOS for labels that shadowed breaks and continues.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002188 frame_->Push(Immediate(Factory::undefined_value()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002189 }
2190 __ Set(ecx, Immediate(Smi::FromInt(JUMPING + i)));
2191 __ jmp(&unlink);
2192 }
2193 }
2194
2195 // Unlink from try chain; be careful not to destroy the TOS.
2196 __ bind(&unlink);
2197 // Reload sp from the top handler, because some statements that we
2198 // break from (eg, for...in) may have left stuff on the stack.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002199 // Preserve the TOS in a register across stack manipulation.
2200 frame_->Pop(eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002201 ExternalReference handler_address(Top::k_handler_address);
2202 __ mov(edx, Operand::StaticVariable(handler_address));
2203 const int kNextOffset = StackHandlerConstants::kNextOffset +
2204 StackHandlerConstants::kAddressDisplacement;
2205 __ lea(esp, Operand(edx, kNextOffset));
2206
ager@chromium.org7c537e22008-10-16 08:43:32 +00002207 frame_->Pop(Operand::StaticVariable(handler_address));
2208 frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1);
2209 // Next_sp popped.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002210 frame_->Push(eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002211
2212 // --- Finally block ---
2213 __ bind(&finally_block);
2214
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002215 // Push the state on the stack.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002216 frame_->Push(ecx);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002217
2218 // We keep two elements on the stack - the (possibly faked) result
2219 // and the state - while evaluating the finally block. Record it, so
2220 // that a break/continue crossing this statement can restore the
2221 // stack.
2222 const int kFinallyStackSize = 2 * kPointerSize;
2223 break_stack_height_ += kFinallyStackSize;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002224
2225 // Generate code for the statements in the finally block.
2226 VisitStatements(node->finally_block()->statements());
2227
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002228 // Restore state and return value or faked TOS.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002229 frame_->Pop(ecx);
2230 frame_->Pop(eax);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002231 break_stack_height_ -= kFinallyStackSize;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002232
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002233 // Generate code to jump to the right destination for all used (formerly)
2234 // shadowing labels.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002235 for (int i = 0; i <= nof_escapes; i++) {
2236 if (shadows[i]->is_bound()) {
2237 __ cmp(Operand(ecx), Immediate(Smi::FromInt(JUMPING + i)));
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002238 __ j(equal, shadows[i]->original_label());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002239 }
2240 }
2241
2242 // Check if we need to rethrow the exception.
2243 __ cmp(Operand(ecx), Immediate(Smi::FromInt(THROWING)));
2244 __ j(not_equal, &exit);
2245
2246 // Rethrow exception.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002247 frame_->Push(eax); // undo pop from above
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002248 __ CallRuntime(Runtime::kReThrow, 1);
2249
2250 // Done.
2251 __ bind(&exit);
2252}
2253
2254
ager@chromium.org7c537e22008-10-16 08:43:32 +00002255void CodeGenerator::VisitDebuggerStatement(DebuggerStatement* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002256 Comment cmnt(masm_, "[ DebuggerStatement");
ager@chromium.org236ad962008-09-25 09:45:57 +00002257 RecordStatementPosition(node);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00002258 __ CallRuntime(Runtime::kDebugBreak, 0);
2259 // Ignore the return value.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002260}
2261
2262
ager@chromium.org7c537e22008-10-16 08:43:32 +00002263void CodeGenerator::InstantiateBoilerplate(Handle<JSFunction> boilerplate) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002264 ASSERT(boilerplate->IsBoilerplate());
2265
2266 // Push the boilerplate on the stack.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002267 frame_->Push(Immediate(boilerplate));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002268
2269 // Create a new closure.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002270 frame_->Push(esi);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002271 __ CallRuntime(Runtime::kNewClosure, 2);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002272 frame_->Push(eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002273}
2274
2275
ager@chromium.org7c537e22008-10-16 08:43:32 +00002276void CodeGenerator::VisitFunctionLiteral(FunctionLiteral* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002277 Comment cmnt(masm_, "[ FunctionLiteral");
2278
2279 // Build the function boilerplate and instantiate it.
2280 Handle<JSFunction> boilerplate = BuildBoilerplate(node);
kasper.lund212ac232008-07-16 07:07:30 +00002281 // Check for stack-overflow exception.
2282 if (HasStackOverflow()) return;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002283 InstantiateBoilerplate(boilerplate);
2284}
2285
2286
ager@chromium.org7c537e22008-10-16 08:43:32 +00002287void CodeGenerator::VisitFunctionBoilerplateLiteral(
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002288 FunctionBoilerplateLiteral* node) {
2289 Comment cmnt(masm_, "[ FunctionBoilerplateLiteral");
2290 InstantiateBoilerplate(node->boilerplate());
2291}
2292
2293
ager@chromium.org7c537e22008-10-16 08:43:32 +00002294void CodeGenerator::VisitConditional(Conditional* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002295 Comment cmnt(masm_, "[ Conditional");
2296 Label then, else_, exit;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002297 LoadCondition(node->condition(), NOT_INSIDE_TYPEOF, &then, &else_, true);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002298 Branch(false, &else_);
2299 __ bind(&then);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002300 Load(node->then_expression(), typeof_state());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002301 __ jmp(&exit);
2302 __ bind(&else_);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002303 Load(node->else_expression(), typeof_state());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002304 __ bind(&exit);
2305}
2306
2307
ager@chromium.org7c537e22008-10-16 08:43:32 +00002308void CodeGenerator::LoadFromSlot(Slot* slot, TypeofState typeof_state) {
2309 if (slot->type() == Slot::LOOKUP) {
2310 ASSERT(slot->var()->mode() == Variable::DYNAMIC);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002311
2312 // For now, just do a runtime call.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002313 frame_->Push(esi);
2314 frame_->Push(Immediate(slot->var()->name()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002315
ager@chromium.org7c537e22008-10-16 08:43:32 +00002316 if (typeof_state == INSIDE_TYPEOF) {
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00002317 __ CallRuntime(Runtime::kLoadContextSlotNoReferenceError, 2);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002318 } else {
2319 __ CallRuntime(Runtime::kLoadContextSlot, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002320 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002321 frame_->Push(eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002322
2323 } else {
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00002324 // Note: We would like to keep the assert below, but it fires because of
2325 // some nasty code in LoadTypeofExpression() which should be removed...
ager@chromium.org7c537e22008-10-16 08:43:32 +00002326 // ASSERT(slot->var()->mode() != Variable::DYNAMIC);
2327 if (slot->var()->mode() == Variable::CONST) {
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00002328 // Const slots may contain 'the hole' value (the constant hasn't been
2329 // initialized yet) which needs to be converted into the 'undefined'
2330 // value.
2331 Comment cmnt(masm_, "[ Load const");
ager@chromium.org7c537e22008-10-16 08:43:32 +00002332 Label exit;
2333 __ mov(eax, SlotOperand(slot, ecx));
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00002334 __ cmp(eax, Factory::the_hole_value());
ager@chromium.org7c537e22008-10-16 08:43:32 +00002335 __ j(not_equal, &exit);
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00002336 __ mov(eax, Factory::undefined_value());
ager@chromium.org7c537e22008-10-16 08:43:32 +00002337 __ bind(&exit);
2338 frame_->Push(eax);
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00002339 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002340 frame_->Push(SlotOperand(slot, ecx));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002341 }
2342 }
2343}
2344
2345
ager@chromium.org7c537e22008-10-16 08:43:32 +00002346void CodeGenerator::VisitSlot(Slot* node) {
2347 Comment cmnt(masm_, "[ Slot");
2348 LoadFromSlot(node, typeof_state());
2349}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002350
ager@chromium.org7c537e22008-10-16 08:43:32 +00002351
2352void CodeGenerator::VisitVariableProxy(VariableProxy* node) {
2353 Comment cmnt(masm_, "[ VariableProxy");
2354 Variable* var = node->var();
2355 Expression* expr = var->rewrite();
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00002356 if (expr != NULL) {
2357 Visit(expr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002358 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002359 ASSERT(var->is_global());
2360 Reference ref(this, node);
2361 ref.GetValue(typeof_state());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002362 }
2363}
2364
2365
ager@chromium.org7c537e22008-10-16 08:43:32 +00002366void CodeGenerator::VisitLiteral(Literal* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002367 Comment cmnt(masm_, "[ Literal");
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002368 if (node->handle()->IsSmi() && !IsInlineSmi(node)) {
2369 // To prevent long attacker-controlled byte sequences in code, larger
2370 // Smis are loaded in two steps.
2371 int bits = reinterpret_cast<int>(*node->handle());
2372 __ mov(eax, bits & 0x0000FFFF);
2373 __ xor_(eax, bits & 0xFFFF0000);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002374 frame_->Push(eax);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002375 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002376 frame_->Push(Immediate(node->handle()));
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002377 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002378}
2379
2380
2381class RegExpDeferred: public DeferredCode {
2382 public:
2383 RegExpDeferred(CodeGenerator* generator, RegExpLiteral* node)
2384 : DeferredCode(generator), node_(node) {
2385 set_comment("[ RegExpDeferred");
2386 }
2387 virtual void Generate();
2388 private:
2389 RegExpLiteral* node_;
2390};
2391
2392
2393void RegExpDeferred::Generate() {
2394 // If the entry is undefined we call the runtime system to computed
2395 // the literal.
2396
2397 // Literal array (0).
2398 __ push(ecx);
2399 // Literal index (1).
2400 __ push(Immediate(Smi::FromInt(node_->literal_index())));
2401 // RegExp pattern (2).
2402 __ push(Immediate(node_->pattern()));
2403 // RegExp flags (3).
2404 __ push(Immediate(node_->flags()));
2405 __ CallRuntime(Runtime::kMaterializeRegExpLiteral, 4);
2406 __ mov(ebx, Operand(eax)); // "caller" expects result in ebx
2407}
2408
2409
ager@chromium.org7c537e22008-10-16 08:43:32 +00002410void CodeGenerator::VisitRegExpLiteral(RegExpLiteral* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002411 Comment cmnt(masm_, "[ RegExp Literal");
2412 RegExpDeferred* deferred = new RegExpDeferred(this, node);
2413
2414 // Retrieve the literal array and check the allocated entry.
2415
2416 // Load the function of this activation.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002417 __ mov(ecx, frame_->Function());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002418
2419 // Load the literals array of the function.
2420 __ mov(ecx, FieldOperand(ecx, JSFunction::kLiteralsOffset));
2421
2422 // Load the literal at the ast saved index.
2423 int literal_offset =
2424 FixedArray::kHeaderSize + node->literal_index() * kPointerSize;
2425 __ mov(ebx, FieldOperand(ecx, literal_offset));
2426
2427 // Check whether we need to materialize the RegExp object.
2428 // If so, jump to the deferred code.
2429 __ cmp(ebx, Factory::undefined_value());
2430 __ j(equal, deferred->enter(), not_taken);
2431 __ bind(deferred->exit());
2432
2433 // Push the literal.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002434 frame_->Push(ebx);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002435}
2436
2437
2438// This deferred code stub will be used for creating the boilerplate
2439// by calling Runtime_CreateObjectLiteral.
2440// Each created boilerplate is stored in the JSFunction and they are
2441// therefore context dependent.
2442class ObjectLiteralDeferred: public DeferredCode {
2443 public:
ager@chromium.org236ad962008-09-25 09:45:57 +00002444 ObjectLiteralDeferred(CodeGenerator* generator,
2445 ObjectLiteral* node)
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002446 : DeferredCode(generator), node_(node) {
2447 set_comment("[ ObjectLiteralDeferred");
2448 }
2449 virtual void Generate();
2450 private:
2451 ObjectLiteral* node_;
2452};
2453
2454
2455void ObjectLiteralDeferred::Generate() {
v8.team.kasperl727e9952008-09-02 14:56:44 +00002456 // If the entry is undefined we call the runtime system to compute
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002457 // the literal.
2458
2459 // Literal array (0).
ager@chromium.org7c537e22008-10-16 08:43:32 +00002460 __ push(ecx);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002461 // Literal index (1).
2462 __ push(Immediate(Smi::FromInt(node_->literal_index())));
2463 // Constant properties (2).
2464 __ push(Immediate(node_->constant_properties()));
2465 __ CallRuntime(Runtime::kCreateObjectLiteralBoilerplate, 3);
2466 __ mov(ebx, Operand(eax));
2467}
2468
2469
ager@chromium.org7c537e22008-10-16 08:43:32 +00002470void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002471 Comment cmnt(masm_, "[ ObjectLiteral");
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002472 ObjectLiteralDeferred* deferred = new ObjectLiteralDeferred(this, node);
2473
2474 // Retrieve the literal array and check the allocated entry.
2475
2476 // Load the function of this activation.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002477 __ mov(ecx, frame_->Function());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002478
2479 // Load the literals array of the function.
2480 __ mov(ecx, FieldOperand(ecx, JSFunction::kLiteralsOffset));
2481
2482 // Load the literal at the ast saved index.
2483 int literal_offset =
2484 FixedArray::kHeaderSize + node->literal_index() * kPointerSize;
2485 __ mov(ebx, FieldOperand(ecx, literal_offset));
2486
2487 // Check whether we need to materialize the object literal boilerplate.
2488 // If so, jump to the deferred code.
2489 __ cmp(ebx, Factory::undefined_value());
2490 __ j(equal, deferred->enter(), not_taken);
2491 __ bind(deferred->exit());
2492
2493 // Push the literal.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002494 frame_->Push(ebx);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002495 // Clone the boilerplate object.
2496 __ CallRuntime(Runtime::kCloneObjectLiteralBoilerplate, 1);
2497 // Push the new cloned literal object as the result.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002498 frame_->Push(eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002499
ager@chromium.org236ad962008-09-25 09:45:57 +00002500
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002501 for (int i = 0; i < node->properties()->length(); i++) {
2502 ObjectLiteral::Property* property = node->properties()->at(i);
2503 switch (property->kind()) {
2504 case ObjectLiteral::Property::CONSTANT: break;
2505 case ObjectLiteral::Property::COMPUTED: {
2506 Handle<Object> key(property->key()->handle());
2507 Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
2508 if (key->IsSymbol()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002509 __ mov(eax, frame_->Top());
2510 frame_->Push(eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002511 Load(property->value());
ager@chromium.org7c537e22008-10-16 08:43:32 +00002512 frame_->Pop(eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002513 __ Set(ecx, Immediate(key));
ager@chromium.org236ad962008-09-25 09:45:57 +00002514 __ call(ic, RelocInfo::CODE_TARGET);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002515 frame_->Pop();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002516 // Ignore result.
2517 break;
2518 }
2519 // Fall through
2520 }
2521 case ObjectLiteral::Property::PROTOTYPE: {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002522 __ mov(eax, frame_->Top());
2523 frame_->Push(eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002524 Load(property->key());
2525 Load(property->value());
2526 __ CallRuntime(Runtime::kSetProperty, 3);
2527 // Ignore result.
2528 break;
2529 }
2530 case ObjectLiteral::Property::SETTER: {
2531 // Duplicate the resulting object on the stack. The runtime
2532 // function will pop the three arguments passed in.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002533 __ mov(eax, frame_->Top());
2534 frame_->Push(eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002535 Load(property->key());
ager@chromium.org7c537e22008-10-16 08:43:32 +00002536 frame_->Push(Immediate(Smi::FromInt(1)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002537 Load(property->value());
2538 __ CallRuntime(Runtime::kDefineAccessor, 4);
2539 // Ignore result.
2540 break;
2541 }
2542 case ObjectLiteral::Property::GETTER: {
2543 // Duplicate the resulting object on the stack. The runtime
2544 // function will pop the three arguments passed in.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002545 __ mov(eax, frame_->Top());
2546 frame_->Push(eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002547 Load(property->key());
ager@chromium.org7c537e22008-10-16 08:43:32 +00002548 frame_->Push(Immediate(Smi::FromInt(0)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002549 Load(property->value());
2550 __ CallRuntime(Runtime::kDefineAccessor, 4);
2551 // Ignore result.
2552 break;
2553 }
2554 default: UNREACHABLE();
2555 }
2556 }
2557}
2558
2559
ager@chromium.org7c537e22008-10-16 08:43:32 +00002560void CodeGenerator::VisitArrayLiteral(ArrayLiteral* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002561 Comment cmnt(masm_, "[ ArrayLiteral");
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002562
2563 // Call runtime to create the array literal.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002564 frame_->Push(Immediate(node->literals()));
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002565 // Load the function of this frame.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002566 __ mov(ecx, frame_->Function());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002567 // Load the literals array of the function.
2568 __ mov(ecx, FieldOperand(ecx, JSFunction::kLiteralsOffset));
ager@chromium.org7c537e22008-10-16 08:43:32 +00002569 frame_->Push(ecx);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002570 __ CallRuntime(Runtime::kCreateArrayLiteral, 2);
2571
2572 // Push the resulting array literal on the stack.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002573 frame_->Push(eax);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002574
2575 // Generate code to set the elements in the array that are not
2576 // literals.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002577 for (int i = 0; i < node->values()->length(); i++) {
2578 Expression* value = node->values()->at(i);
2579
2580 // If value is literal the property value is already
2581 // set in the boilerplate object.
2582 if (value->AsLiteral() == NULL) {
2583 // The property must be set by generated code.
2584 Load(value);
2585
2586 // Get the value off the stack.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002587 frame_->Pop(eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002588 // Fetch the object literal while leaving on the stack.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002589 __ mov(ecx, frame_->Top());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002590 // Get the elements array.
2591 __ mov(ecx, FieldOperand(ecx, JSObject::kElementsOffset));
2592
2593 // Write to the indexed properties array.
2594 int offset = i * kPointerSize + Array::kHeaderSize;
2595 __ mov(FieldOperand(ecx, offset), eax);
2596
2597 // Update the write barrier for the array address.
2598 __ RecordWrite(ecx, offset, eax, ebx);
2599 }
2600 }
2601}
2602
2603
ager@chromium.org7c537e22008-10-16 08:43:32 +00002604bool CodeGenerator::IsInlineSmi(Literal* literal) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002605 if (literal == NULL || !literal->handle()->IsSmi()) return false;
2606 int int_value = Smi::cast(*literal->handle())->value();
2607 return is_intn(int_value, kMaxSmiInlinedBits);
2608}
2609
2610
ager@chromium.org7c537e22008-10-16 08:43:32 +00002611void CodeGenerator::VisitAssignment(Assignment* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002612 Comment cmnt(masm_, "[ Assignment");
2613
ager@chromium.org236ad962008-09-25 09:45:57 +00002614 RecordStatementPosition(node);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002615 Reference target(this, node->target());
2616 if (target.is_illegal()) return;
2617
2618 if (node->op() == Token::ASSIGN ||
2619 node->op() == Token::INIT_VAR ||
2620 node->op() == Token::INIT_CONST) {
2621 Load(node->value());
2622
2623 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002624 target.GetValue(NOT_INSIDE_TYPEOF);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002625 Literal* literal = node->value()->AsLiteral();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002626 if (IsInlineSmi(literal)) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00002627 SmiOperation(node->binary_op(), node->type(), literal->handle(), false,
2628 NO_OVERWRITE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002629 } else {
2630 Load(node->value());
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00002631 GenericBinaryOperation(node->binary_op(), node->type());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002632 }
2633 }
2634
2635 Variable* var = node->target()->AsVariableProxy()->AsVariable();
2636 if (var != NULL &&
2637 var->mode() == Variable::CONST &&
2638 node->op() != Token::INIT_VAR && node->op() != Token::INIT_CONST) {
2639 // Assignment ignored - leave the value on the stack.
2640 } else {
2641 __ RecordPosition(node->position());
2642 if (node->op() == Token::INIT_CONST) {
2643 // Dynamic constant initializations must use the function context
2644 // and initialize the actual constant declared. Dynamic variable
2645 // initializations are simply assignments and use SetValue.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002646 target.SetValue(CONST_INIT);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002647 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002648 target.SetValue(NOT_CONST_INIT);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002649 }
2650 }
2651}
2652
2653
ager@chromium.org7c537e22008-10-16 08:43:32 +00002654void CodeGenerator::VisitThrow(Throw* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002655 Comment cmnt(masm_, "[ Throw");
2656
2657 Load(node->exception());
2658 __ RecordPosition(node->position());
2659 __ CallRuntime(Runtime::kThrow, 1);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002660 frame_->Push(eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002661}
2662
2663
ager@chromium.org7c537e22008-10-16 08:43:32 +00002664void CodeGenerator::VisitProperty(Property* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002665 Comment cmnt(masm_, "[ Property");
2666
ager@chromium.org7c537e22008-10-16 08:43:32 +00002667 Reference property(this, node);
2668 property.GetValue(typeof_state());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002669}
2670
2671
ager@chromium.org7c537e22008-10-16 08:43:32 +00002672void CodeGenerator::VisitCall(Call* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002673 Comment cmnt(masm_, "[ Call");
2674
2675 ZoneList<Expression*>* args = node->arguments();
2676
ager@chromium.org236ad962008-09-25 09:45:57 +00002677 RecordStatementPosition(node);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002678
2679 // Check if the function is a variable or a property.
2680 Expression* function = node->expression();
2681 Variable* var = function->AsVariableProxy()->AsVariable();
2682 Property* property = function->AsProperty();
2683
2684 // ------------------------------------------------------------------------
2685 // Fast-case: Use inline caching.
2686 // ---
2687 // According to ECMA-262, section 11.2.3, page 44, the function to call
2688 // must be resolved after the arguments have been evaluated. The IC code
2689 // automatically handles this by loading the arguments before the function
2690 // is resolved in cache misses (this also holds for megamorphic calls).
2691 // ------------------------------------------------------------------------
2692
2693 if (var != NULL && !var->is_this() && var->is_global()) {
2694 // ----------------------------------
2695 // JavaScript example: 'foo(1, 2, 3)' // foo is global
2696 // ----------------------------------
2697
2698 // Push the name of the function and the receiver onto the stack.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002699 frame_->Push(Immediate(var->name()));
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002700
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00002701 // Pass the global object as the receiver and let the IC stub
2702 // patch the stack to use the global proxy as 'this' in the
2703 // invoked function.
2704 LoadGlobal();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002705 // Load the arguments.
2706 for (int i = 0; i < args->length(); i++) {
2707 Load(args->at(i));
2708 }
2709
2710 // Setup the receiver register and call the IC initialization code.
ager@chromium.org3bf7b912008-11-17 09:09:45 +00002711 Handle<Code> stub = (loop_nesting() > 0)
2712 ? ComputeCallInitializeInLoop(args->length())
2713 : ComputeCallInitialize(args->length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002714 __ RecordPosition(node->position());
ager@chromium.org236ad962008-09-25 09:45:57 +00002715 __ call(stub, RelocInfo::CODE_TARGET_CONTEXT);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002716 __ mov(esi, frame_->Context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002717
2718 // Overwrite the function on the stack with the result.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002719 __ mov(frame_->Top(), eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002720
2721 } else if (var != NULL && var->slot() != NULL &&
2722 var->slot()->type() == Slot::LOOKUP) {
2723 // ----------------------------------
2724 // JavaScript example: 'with (obj) foo(1, 2, 3)' // foo is in obj
2725 // ----------------------------------
2726
2727 // Load the function
ager@chromium.org7c537e22008-10-16 08:43:32 +00002728 frame_->Push(esi);
2729 frame_->Push(Immediate(var->name()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002730 __ CallRuntime(Runtime::kLoadContextSlot, 2);
2731 // eax: slot value; edx: receiver
2732
2733 // Load the receiver.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002734 frame_->Push(eax);
2735 frame_->Push(edx);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002736
2737 // Call the function.
2738 CallWithArguments(args, node->position());
2739
2740 } else if (property != NULL) {
2741 // Check if the key is a literal string.
2742 Literal* literal = property->key()->AsLiteral();
2743
2744 if (literal != NULL && literal->handle()->IsSymbol()) {
2745 // ------------------------------------------------------------------
2746 // JavaScript example: 'object.foo(1, 2, 3)' or 'map["key"](1, 2, 3)'
2747 // ------------------------------------------------------------------
2748
2749 // Push the name of the function and the receiver onto the stack.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002750 frame_->Push(Immediate(literal->handle()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002751 Load(property->obj());
2752
2753 // Load the arguments.
2754 for (int i = 0; i < args->length(); i++) Load(args->at(i));
2755
2756 // Call the IC initialization code.
ager@chromium.org3bf7b912008-11-17 09:09:45 +00002757 Handle<Code> stub = (loop_nesting() > 0)
2758 ? ComputeCallInitializeInLoop(args->length())
2759 : ComputeCallInitialize(args->length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002760 __ RecordPosition(node->position());
ager@chromium.org236ad962008-09-25 09:45:57 +00002761 __ call(stub, RelocInfo::CODE_TARGET);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002762 __ mov(esi, frame_->Context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002763
2764 // Overwrite the function on the stack with the result.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002765 __ mov(frame_->Top(), eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002766
2767 } else {
2768 // -------------------------------------------
2769 // JavaScript example: 'array[index](1, 2, 3)'
2770 // -------------------------------------------
2771
2772 // Load the function to call from the property through a reference.
2773 Reference ref(this, property);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002774 ref.GetValue(NOT_INSIDE_TYPEOF);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002775
2776 // Pass receiver to called function.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002777 // The reference's size is non-negative.
2778 frame_->Push(frame_->Element(ref.size()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002779
2780 // Call the function.
2781 CallWithArguments(args, node->position());
2782 }
2783
2784 } else {
2785 // ----------------------------------
2786 // JavaScript example: 'foo(1, 2, 3)' // foo is not global
2787 // ----------------------------------
2788
2789 // Load the function.
2790 Load(function);
2791
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00002792 // Pass the global proxy as the receiver.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002793 LoadGlobalReceiver(eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002794
2795 // Call the function.
2796 CallWithArguments(args, node->position());
2797 }
2798}
2799
2800
ager@chromium.org7c537e22008-10-16 08:43:32 +00002801void CodeGenerator::VisitCallNew(CallNew* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002802 Comment cmnt(masm_, "[ CallNew");
2803
2804 // According to ECMA-262, section 11.2.2, page 44, the function
2805 // expression in new calls must be evaluated before the
2806 // arguments. This is different from ordinary calls, where the
2807 // actual function to call is resolved after the arguments have been
2808 // evaluated.
2809
2810 // Compute function to call and use the global object as the
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00002811 // receiver. There is no need to use the global proxy here because
2812 // it will always be replaced with a newly allocated object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002813 Load(node->expression());
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00002814 LoadGlobal();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002815
2816 // Push the arguments ("left-to-right") on the stack.
2817 ZoneList<Expression*>* args = node->arguments();
2818 for (int i = 0; i < args->length(); i++) Load(args->at(i));
2819
2820 // Constructors are called with the number of arguments in register
2821 // eax for now. Another option would be to have separate construct
2822 // call trampolines per different arguments counts encountered.
2823 __ Set(eax, Immediate(args->length()));
2824
2825 // Load the function into temporary function slot as per calling
2826 // convention.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002827 __ mov(edi, frame_->Element(args->length() + 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002828
2829 // Call the construct call builtin that handles allocation and
2830 // constructor invocation.
2831 __ RecordPosition(node->position());
2832 __ call(Handle<Code>(Builtins::builtin(Builtins::JSConstructCall)),
ager@chromium.org236ad962008-09-25 09:45:57 +00002833 RelocInfo::CONSTRUCT_CALL);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002834 // Discard the function and "push" the newly created object.
2835 __ mov(frame_->Top(), eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002836}
2837
2838
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002839void CodeGenerator::VisitCallEval(CallEval* node) {
2840 Comment cmnt(masm_, "[ CallEval");
2841
2842 // In a call to eval, we first call %ResolvePossiblyDirectEval to resolve
2843 // the function we need to call and the receiver of the call.
2844 // Then we call the resolved function using the given arguments.
2845
2846 ZoneList<Expression*>* args = node->arguments();
2847 Expression* function = node->expression();
2848
2849 RecordStatementPosition(node);
2850
2851 // Prepare stack for call to resolved function.
2852 Load(function);
2853 __ push(Immediate(Factory::undefined_value())); // Slot for receiver
2854 for (int i = 0; i < args->length(); i++) {
2855 Load(args->at(i));
2856 }
2857
2858 // Prepare stack for call to ResolvePossiblyDirectEval.
2859 __ push(Operand(esp, args->length() * kPointerSize + kPointerSize));
2860 if (args->length() > 0) {
2861 __ push(Operand(esp, args->length() * kPointerSize));
2862 } else {
2863 __ push(Immediate(Factory::undefined_value()));
2864 }
2865
2866 // Resolve the call.
2867 __ CallRuntime(Runtime::kResolvePossiblyDirectEval, 2);
2868
2869 // Touch up stack with the right values for the function and the receiver.
2870 __ mov(edx, FieldOperand(eax, FixedArray::kHeaderSize));
2871 __ mov(Operand(esp, (args->length() + 1) * kPointerSize), edx);
2872 __ mov(edx, FieldOperand(eax, FixedArray::kHeaderSize + kPointerSize));
2873 __ mov(Operand(esp, args->length() * kPointerSize), edx);
2874
2875 // Call the function.
2876 __ RecordPosition(node->position());
2877
2878 CallFunctionStub call_function(args->length());
2879 __ CallStub(&call_function);
2880
2881 // Restore context and pop function from the stack.
2882 __ mov(esi, frame_->Context());
2883 __ mov(frame_->Top(), eax);
2884}
2885
2886
ager@chromium.org7c537e22008-10-16 08:43:32 +00002887void CodeGenerator::GenerateIsSmi(ZoneList<Expression*>* args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002888 ASSERT(args->length() == 1);
2889 Load(args->at(0));
ager@chromium.org7c537e22008-10-16 08:43:32 +00002890 frame_->Pop(eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002891 __ test(eax, Immediate(kSmiTagMask));
2892 cc_reg_ = zero;
2893}
2894
2895
ager@chromium.org7c537e22008-10-16 08:43:32 +00002896void CodeGenerator::GenerateIsNonNegativeSmi(ZoneList<Expression*>* args) {
ager@chromium.orgc27e4e72008-09-04 13:52:27 +00002897 ASSERT(args->length() == 1);
2898 Load(args->at(0));
ager@chromium.org7c537e22008-10-16 08:43:32 +00002899 frame_->Pop(eax);
ager@chromium.orgc27e4e72008-09-04 13:52:27 +00002900 __ test(eax, Immediate(kSmiTagMask | 0x80000000));
2901 cc_reg_ = zero;
2902}
2903
2904
kasper.lund7276f142008-07-30 08:49:36 +00002905// This generates code that performs a charCodeAt() call or returns
2906// undefined in order to trigger the slow case, Runtime_StringCharCodeAt.
2907// It can handle flat and sliced strings, 8 and 16 bit characters and
2908// cons strings where the answer is found in the left hand branch of the
2909// cons. The slow case will flatten the string, which will ensure that
2910// the answer is in the left hand side the next time around.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002911void CodeGenerator::GenerateFastCharCodeAt(ZoneList<Expression*>* args) {
kasper.lund7276f142008-07-30 08:49:36 +00002912 ASSERT(args->length() == 2);
2913
2914 Label slow_case;
2915 Label end;
2916 Label not_a_flat_string;
2917 Label not_a_cons_string_either;
2918 Label try_again_with_new_string;
2919 Label ascii_string;
2920 Label got_char_code;
2921
2922 // Load the string into eax.
2923 Load(args->at(0));
ager@chromium.org7c537e22008-10-16 08:43:32 +00002924 frame_->Pop(eax);
kasper.lund7276f142008-07-30 08:49:36 +00002925 // If the receiver is a smi return undefined.
2926 ASSERT(kSmiTag == 0);
2927 __ test(eax, Immediate(kSmiTagMask));
2928 __ j(zero, &slow_case, not_taken);
2929
2930 // Load the index into ebx.
2931 Load(args->at(1));
ager@chromium.org7c537e22008-10-16 08:43:32 +00002932 frame_->Pop(ebx);
kasper.lund7276f142008-07-30 08:49:36 +00002933
2934 // Check for negative or non-smi index.
2935 ASSERT(kSmiTag == 0);
2936 __ test(ebx, Immediate(kSmiTagMask | 0x80000000));
2937 __ j(not_zero, &slow_case, not_taken);
2938 // Get rid of the smi tag on the index.
2939 __ sar(ebx, kSmiTagSize);
2940
2941 __ bind(&try_again_with_new_string);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00002942 // Get the type of the heap object into edi.
kasper.lund7276f142008-07-30 08:49:36 +00002943 __ mov(edx, FieldOperand(eax, HeapObject::kMapOffset));
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00002944 __ movzx_b(edi, FieldOperand(edx, Map::kInstanceTypeOffset));
kasper.lund7276f142008-07-30 08:49:36 +00002945 // We don't handle non-strings.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00002946 __ test(edi, Immediate(kIsNotStringMask));
kasper.lund7276f142008-07-30 08:49:36 +00002947 __ j(not_zero, &slow_case, not_taken);
2948
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00002949 // Here we make assumptions about the tag values and the shifts needed.
2950 // See the comment in objects.h.
2951 ASSERT(kLongStringTag == 0);
2952 ASSERT(kMediumStringTag + String::kLongLengthShift ==
2953 String::kMediumLengthShift);
2954 ASSERT(kShortStringTag + String::kLongLengthShift ==
2955 String::kShortLengthShift);
2956 __ mov(ecx, Operand(edi));
2957 __ and_(ecx, kStringSizeMask);
2958 __ add(Operand(ecx), Immediate(String::kLongLengthShift));
kasper.lund7276f142008-07-30 08:49:36 +00002959 // Get the length field.
2960 __ mov(edx, FieldOperand(eax, String::kLengthOffset));
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00002961 __ shr(edx); // ecx is implicit operand.
kasper.lund7276f142008-07-30 08:49:36 +00002962 // edx is now the length of the string.
2963
2964 // Check for index out of range.
2965 __ cmp(ebx, Operand(edx));
2966 __ j(greater_equal, &slow_case, not_taken);
2967
2968 // We need special handling for non-flat strings.
2969 ASSERT(kSeqStringTag == 0);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00002970 __ test(edi, Immediate(kStringRepresentationMask));
kasper.lund7276f142008-07-30 08:49:36 +00002971 __ j(not_zero, &not_a_flat_string, not_taken);
2972
2973 // Check for 1-byte or 2-byte string.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00002974 __ test(edi, Immediate(kStringEncodingMask));
kasper.lund7276f142008-07-30 08:49:36 +00002975 __ j(not_zero, &ascii_string, taken);
2976
2977 // 2-byte string.
2978 // Load the 2-byte character code.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002979 __ movzx_w(eax,
2980 FieldOperand(eax, ebx, times_2, SeqTwoByteString::kHeaderSize));
kasper.lund7276f142008-07-30 08:49:36 +00002981 __ jmp(&got_char_code);
2982
2983 // ASCII string.
2984 __ bind(&ascii_string);
2985 // Load the byte.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002986 __ movzx_b(eax, FieldOperand(eax, ebx, times_1, SeqAsciiString::kHeaderSize));
kasper.lund7276f142008-07-30 08:49:36 +00002987
2988 __ bind(&got_char_code);
2989 ASSERT(kSmiTag == 0);
2990 __ shl(eax, kSmiTagSize);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002991 frame_->Push(eax);
kasper.lund7276f142008-07-30 08:49:36 +00002992 __ jmp(&end);
2993
kasper.lund7276f142008-07-30 08:49:36 +00002994 // Handle non-flat strings.
2995 __ bind(&not_a_flat_string);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00002996 __ and_(edi, kStringRepresentationMask);
2997 __ cmp(edi, kConsStringTag);
kasper.lund7276f142008-07-30 08:49:36 +00002998 __ j(not_equal, &not_a_cons_string_either, not_taken);
2999
3000 // ConsString.
3001 // Get the first of the two strings.
3002 __ mov(eax, FieldOperand(eax, ConsString::kFirstOffset));
3003 __ jmp(&try_again_with_new_string);
3004
3005 __ bind(&not_a_cons_string_either);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003006 __ cmp(edi, kSlicedStringTag);
kasper.lund7276f142008-07-30 08:49:36 +00003007 __ j(not_equal, &slow_case, not_taken);
3008
3009 // SlicedString.
3010 // Add the offset to the index.
3011 __ add(ebx, FieldOperand(eax, SlicedString::kStartOffset));
3012 __ j(overflow, &slow_case);
3013 // Get the underlying string.
3014 __ mov(eax, FieldOperand(eax, SlicedString::kBufferOffset));
3015 __ jmp(&try_again_with_new_string);
3016
3017 __ bind(&slow_case);
ager@chromium.org7c537e22008-10-16 08:43:32 +00003018 frame_->Push(Immediate(Factory::undefined_value()));
kasper.lund7276f142008-07-30 08:49:36 +00003019
3020 __ bind(&end);
3021}
3022
3023
ager@chromium.org7c537e22008-10-16 08:43:32 +00003024void CodeGenerator::GenerateIsArray(ZoneList<Expression*>* args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003025 ASSERT(args->length() == 1);
3026 Load(args->at(0));
3027 Label answer;
3028 // We need the CC bits to come out as not_equal in the case where the
kasper.lund7276f142008-07-30 08:49:36 +00003029 // object is a smi. This can't be done with the usual test opcode so
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003030 // we copy the object to ecx and do some destructive ops on it that
3031 // result in the right CC bits.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003032 frame_->Pop(eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003033 __ mov(ecx, Operand(eax));
3034 __ and_(ecx, kSmiTagMask);
3035 __ xor_(ecx, kSmiTagMask);
3036 __ j(not_equal, &answer, not_taken);
3037 // It is a heap object - get map.
3038 __ mov(eax, FieldOperand(eax, HeapObject::kMapOffset));
3039 __ movzx_b(eax, FieldOperand(eax, Map::kInstanceTypeOffset));
3040 // Check if the object is a JS array or not.
3041 __ cmp(eax, JS_ARRAY_TYPE);
3042 __ bind(&answer);
3043 cc_reg_ = equal;
3044}
3045
3046
ager@chromium.org7c537e22008-10-16 08:43:32 +00003047void CodeGenerator::GenerateArgumentsLength(ZoneList<Expression*>* args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003048 ASSERT(args->length() == 0);
3049
3050 // Seed the result with the formal parameters count, which will be
3051 // used in case no arguments adaptor frame is found below the
3052 // current frame.
3053 __ Set(eax, Immediate(Smi::FromInt(scope_->num_parameters())));
3054
3055 // Call the shared stub to get to the arguments.length.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003056 ArgumentsAccessStub stub(ArgumentsAccessStub::READ_LENGTH);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003057 __ CallStub(&stub);
ager@chromium.org7c537e22008-10-16 08:43:32 +00003058 frame_->Push(eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003059}
3060
3061
ager@chromium.org7c537e22008-10-16 08:43:32 +00003062void CodeGenerator::GenerateValueOf(ZoneList<Expression*>* args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003063 ASSERT(args->length() == 1);
3064 Label leave;
3065 Load(args->at(0)); // Load the object.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003066 __ mov(eax, frame_->Top());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003067 // if (object->IsSmi()) return object.
3068 __ test(eax, Immediate(kSmiTagMask));
3069 __ j(zero, &leave, taken);
3070 // It is a heap object - get map.
3071 __ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset));
3072 __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
3073 // if (!object->IsJSValue()) return object.
3074 __ cmp(ecx, JS_VALUE_TYPE);
3075 __ j(not_equal, &leave, not_taken);
3076 __ mov(eax, FieldOperand(eax, JSValue::kValueOffset));
ager@chromium.org7c537e22008-10-16 08:43:32 +00003077 __ mov(frame_->Top(), eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003078 __ bind(&leave);
3079}
3080
3081
ager@chromium.org7c537e22008-10-16 08:43:32 +00003082void CodeGenerator::GenerateSetValueOf(ZoneList<Expression*>* args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003083 ASSERT(args->length() == 2);
3084 Label leave;
3085 Load(args->at(0)); // Load the object.
3086 Load(args->at(1)); // Load the value.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003087 __ mov(eax, frame_->Element(1));
3088 __ mov(ecx, frame_->Top());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003089 // if (object->IsSmi()) return object.
3090 __ test(eax, Immediate(kSmiTagMask));
3091 __ j(zero, &leave, taken);
3092 // It is a heap object - get map.
3093 __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset));
3094 __ movzx_b(ebx, FieldOperand(ebx, Map::kInstanceTypeOffset));
3095 // if (!object->IsJSValue()) return object.
3096 __ cmp(ebx, JS_VALUE_TYPE);
3097 __ j(not_equal, &leave, not_taken);
3098 // Store the value.
3099 __ mov(FieldOperand(eax, JSValue::kValueOffset), ecx);
3100 // Update the write barrier.
3101 __ RecordWrite(eax, JSValue::kValueOffset, ecx, ebx);
3102 // Leave.
3103 __ bind(&leave);
ager@chromium.org7c537e22008-10-16 08:43:32 +00003104 __ mov(ecx, frame_->Top());
3105 frame_->Pop();
3106 __ mov(frame_->Top(), ecx);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003107}
3108
3109
ager@chromium.org7c537e22008-10-16 08:43:32 +00003110void CodeGenerator::GenerateArgumentsAccess(ZoneList<Expression*>* args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003111 ASSERT(args->length() == 1);
3112
3113 // Load the key onto the stack and set register eax to the formal
3114 // parameters count for the currently executing function.
3115 Load(args->at(0));
3116 __ Set(eax, Immediate(Smi::FromInt(scope_->num_parameters())));
3117
3118 // Call the shared stub to get to arguments[key].
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003119 ArgumentsAccessStub stub(ArgumentsAccessStub::READ_ELEMENT);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003120 __ CallStub(&stub);
ager@chromium.org7c537e22008-10-16 08:43:32 +00003121 __ mov(frame_->Top(), eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003122}
3123
3124
ager@chromium.org7c537e22008-10-16 08:43:32 +00003125void CodeGenerator::GenerateObjectEquals(ZoneList<Expression*>* args) {
ager@chromium.org9258b6b2008-09-11 09:11:10 +00003126 ASSERT(args->length() == 2);
3127
3128 // Load the two objects into registers and perform the comparison.
3129 Load(args->at(0));
3130 Load(args->at(1));
ager@chromium.org7c537e22008-10-16 08:43:32 +00003131 frame_->Pop(eax);
3132 frame_->Pop(ecx);
ager@chromium.org9258b6b2008-09-11 09:11:10 +00003133 __ cmp(eax, Operand(ecx));
3134 cc_reg_ = equal;
3135}
3136
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003137
ager@chromium.org7c537e22008-10-16 08:43:32 +00003138void CodeGenerator::VisitCallRuntime(CallRuntime* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003139 if (CheckForInlineRuntimeCall(node)) return;
3140
3141 ZoneList<Expression*>* args = node->arguments();
3142 Comment cmnt(masm_, "[ CallRuntime");
3143 Runtime::Function* function = node->function();
3144
3145 if (function == NULL) {
3146 // Prepare stack for calling JS runtime function.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003147 frame_->Push(Immediate(node->name()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003148 // Push the builtins object found in the current global object.
3149 __ mov(edx, GlobalObject());
ager@chromium.org7c537e22008-10-16 08:43:32 +00003150 frame_->Push(FieldOperand(edx, GlobalObject::kBuiltinsOffset));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003151 }
3152
3153 // Push the arguments ("left-to-right").
3154 for (int i = 0; i < args->length(); i++)
3155 Load(args->at(i));
3156
3157 if (function != NULL) {
3158 // Call the C runtime function.
3159 __ CallRuntime(function, args->length());
ager@chromium.org7c537e22008-10-16 08:43:32 +00003160 frame_->Push(eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003161 } else {
3162 // Call the JS runtime function.
3163 Handle<Code> stub = ComputeCallInitialize(args->length());
3164 __ Set(eax, Immediate(args->length()));
ager@chromium.org236ad962008-09-25 09:45:57 +00003165 __ call(stub, RelocInfo::CODE_TARGET);
ager@chromium.org7c537e22008-10-16 08:43:32 +00003166 __ mov(esi, frame_->Context());
3167 __ mov(frame_->Top(), eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003168 }
3169}
3170
3171
ager@chromium.org7c537e22008-10-16 08:43:32 +00003172void CodeGenerator::VisitUnaryOperation(UnaryOperation* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003173 Comment cmnt(masm_, "[ UnaryOperation");
3174
3175 Token::Value op = node->op();
3176
3177 if (op == Token::NOT) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00003178 LoadCondition(node->expression(), NOT_INSIDE_TYPEOF,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003179 false_target(), true_target(), true);
3180 cc_reg_ = NegateCondition(cc_reg_);
3181
3182 } else if (op == Token::DELETE) {
3183 Property* property = node->expression()->AsProperty();
3184 if (property != NULL) {
3185 Load(property->obj());
3186 Load(property->key());
3187 __ InvokeBuiltin(Builtins::DELETE, CALL_FUNCTION);
ager@chromium.org7c537e22008-10-16 08:43:32 +00003188 frame_->Push(eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003189 return;
3190 }
3191
3192 Variable* variable = node->expression()->AsVariableProxy()->AsVariable();
3193 if (variable != NULL) {
3194 Slot* slot = variable->slot();
3195 if (variable->is_global()) {
3196 LoadGlobal();
ager@chromium.org7c537e22008-10-16 08:43:32 +00003197 frame_->Push(Immediate(variable->name()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003198 __ InvokeBuiltin(Builtins::DELETE, CALL_FUNCTION);
ager@chromium.org7c537e22008-10-16 08:43:32 +00003199 frame_->Push(eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003200 return;
3201
3202 } else if (slot != NULL && slot->type() == Slot::LOOKUP) {
3203 // lookup the context holding the named variable
ager@chromium.org7c537e22008-10-16 08:43:32 +00003204 frame_->Push(esi);
3205 frame_->Push(Immediate(variable->name()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003206 __ CallRuntime(Runtime::kLookupContext, 2);
3207 // eax: context
ager@chromium.org7c537e22008-10-16 08:43:32 +00003208 frame_->Push(eax);
3209 frame_->Push(Immediate(variable->name()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003210 __ InvokeBuiltin(Builtins::DELETE, CALL_FUNCTION);
ager@chromium.org7c537e22008-10-16 08:43:32 +00003211 frame_->Push(eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003212 return;
3213 }
3214
3215 // Default: Result of deleting non-global, not dynamically
3216 // introduced variables is false.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003217 frame_->Push(Immediate(Factory::false_value()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003218
3219 } else {
3220 // Default: Result of deleting expressions is true.
3221 Load(node->expression()); // may have side-effects
ager@chromium.org7c537e22008-10-16 08:43:32 +00003222 __ Set(frame_->Top(), Immediate(Factory::true_value()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003223 }
3224
3225 } else if (op == Token::TYPEOF) {
3226 // Special case for loading the typeof expression; see comment on
3227 // LoadTypeofExpression().
3228 LoadTypeofExpression(node->expression());
3229 __ CallRuntime(Runtime::kTypeof, 1);
ager@chromium.org7c537e22008-10-16 08:43:32 +00003230 frame_->Push(eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003231
3232 } else {
3233 Load(node->expression());
3234 switch (op) {
3235 case Token::NOT:
3236 case Token::DELETE:
3237 case Token::TYPEOF:
3238 UNREACHABLE(); // handled above
3239 break;
3240
3241 case Token::SUB: {
3242 UnarySubStub stub;
3243 // TODO(1222589): remove dependency of TOS being cached inside stub
ager@chromium.org7c537e22008-10-16 08:43:32 +00003244 frame_->Pop(eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003245 __ CallStub(&stub);
ager@chromium.org7c537e22008-10-16 08:43:32 +00003246 frame_->Push(eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003247 break;
3248 }
3249
3250 case Token::BIT_NOT: {
kasper.lund7276f142008-07-30 08:49:36 +00003251 // Smi check.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003252 Label smi_label;
3253 Label continue_label;
ager@chromium.org7c537e22008-10-16 08:43:32 +00003254 frame_->Pop(eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003255 __ test(eax, Immediate(kSmiTagMask));
3256 __ j(zero, &smi_label, taken);
3257
ager@chromium.org7c537e22008-10-16 08:43:32 +00003258 frame_->Push(eax); // undo popping of TOS
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003259 __ InvokeBuiltin(Builtins::BIT_NOT, CALL_FUNCTION);
3260
3261 __ jmp(&continue_label);
3262 __ bind(&smi_label);
3263 __ not_(eax);
kasper.lund7276f142008-07-30 08:49:36 +00003264 __ and_(eax, ~kSmiTagMask); // Remove inverted smi-tag.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003265 __ bind(&continue_label);
ager@chromium.org7c537e22008-10-16 08:43:32 +00003266 frame_->Push(eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003267 break;
3268 }
3269
3270 case Token::VOID:
ager@chromium.org7c537e22008-10-16 08:43:32 +00003271 __ mov(frame_->Top(), Factory::undefined_value());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003272 break;
3273
mads.s.ager@gmail.com769cc962008-08-06 10:02:49 +00003274 case Token::ADD: {
3275 // Smi check.
3276 Label continue_label;
ager@chromium.org7c537e22008-10-16 08:43:32 +00003277 frame_->Pop(eax);
mads.s.ager@gmail.com769cc962008-08-06 10:02:49 +00003278 __ test(eax, Immediate(kSmiTagMask));
3279 __ j(zero, &continue_label);
3280
ager@chromium.org7c537e22008-10-16 08:43:32 +00003281 frame_->Push(eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003282 __ InvokeBuiltin(Builtins::TO_NUMBER, CALL_FUNCTION);
mads.s.ager@gmail.com769cc962008-08-06 10:02:49 +00003283
3284 __ bind(&continue_label);
ager@chromium.org7c537e22008-10-16 08:43:32 +00003285 frame_->Push(eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003286 break;
mads.s.ager@gmail.com769cc962008-08-06 10:02:49 +00003287 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003288
3289 default:
3290 UNREACHABLE();
3291 }
3292 }
3293}
3294
3295
3296class CountOperationDeferred: public DeferredCode {
3297 public:
3298 CountOperationDeferred(CodeGenerator* generator,
3299 bool is_postfix,
3300 bool is_increment,
3301 int result_offset)
3302 : DeferredCode(generator),
3303 is_postfix_(is_postfix),
3304 is_increment_(is_increment),
3305 result_offset_(result_offset) {
3306 set_comment("[ CountOperationDeferred");
3307 }
3308
3309 virtual void Generate();
3310
3311 private:
3312 bool is_postfix_;
3313 bool is_increment_;
3314 int result_offset_;
3315};
3316
3317
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003318class RevertToNumberStub: public CodeStub {
3319 public:
3320 explicit RevertToNumberStub(bool is_increment)
3321 : is_increment_(is_increment) { }
3322
3323 private:
3324 bool is_increment_;
3325
3326 Major MajorKey() { return RevertToNumber; }
3327 int MinorKey() { return is_increment_ ? 1 : 0; }
3328 void Generate(MacroAssembler* masm);
3329
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003330#ifdef DEBUG
3331 void Print() {
3332 PrintF("RevertToNumberStub (is_increment %s)\n",
3333 is_increment_ ? "true" : "false");
3334 }
3335#endif
3336};
3337
3338
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003339class CounterOpStub: public CodeStub {
3340 public:
3341 CounterOpStub(int result_offset, bool is_postfix, bool is_increment)
3342 : result_offset_(result_offset),
3343 is_postfix_(is_postfix),
3344 is_increment_(is_increment) { }
3345
3346 private:
3347 int result_offset_;
3348 bool is_postfix_;
3349 bool is_increment_;
3350
3351 Major MajorKey() { return CounterOp; }
3352 int MinorKey() {
3353 return ((result_offset_ << 2) |
3354 (is_postfix_ ? 2 : 0) |
3355 (is_increment_ ? 1 : 0));
3356 }
3357 void Generate(MacroAssembler* masm);
3358
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003359#ifdef DEBUG
3360 void Print() {
3361 PrintF("CounterOpStub (result_offset %d), (is_postfix %s),"
3362 " (is_increment %s)\n",
3363 result_offset_,
3364 is_postfix_ ? "true" : "false",
3365 is_increment_ ? "true" : "false");
3366 }
3367#endif
3368};
3369
3370
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003371void CountOperationDeferred::Generate() {
3372 if (is_postfix_) {
3373 RevertToNumberStub to_number_stub(is_increment_);
3374 __ CallStub(&to_number_stub);
3375 }
3376 CounterOpStub stub(result_offset_, is_postfix_, is_increment_);
3377 __ CallStub(&stub);
3378}
3379
3380
ager@chromium.org7c537e22008-10-16 08:43:32 +00003381void CodeGenerator::VisitCountOperation(CountOperation* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003382 Comment cmnt(masm_, "[ CountOperation");
3383
3384 bool is_postfix = node->is_postfix();
3385 bool is_increment = node->op() == Token::INC;
3386
3387 Variable* var = node->expression()->AsVariableProxy()->AsVariable();
3388 bool is_const = (var != NULL && var->mode() == Variable::CONST);
3389
3390 // Postfix: Make room for the result.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003391 if (is_postfix) {
3392 frame_->Push(Immediate(0));
3393 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003394
3395 { Reference target(this, node->expression());
3396 if (target.is_illegal()) return;
ager@chromium.org7c537e22008-10-16 08:43:32 +00003397 target.GetValue(NOT_INSIDE_TYPEOF);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003398
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003399 CountOperationDeferred* deferred =
ager@chromium.org7c537e22008-10-16 08:43:32 +00003400 new CountOperationDeferred(this, is_postfix, is_increment,
3401 target.size() * kPointerSize);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003402
ager@chromium.org7c537e22008-10-16 08:43:32 +00003403 frame_->Pop(eax); // Load TOS into eax for calculations below
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003404
3405 // Postfix: Store the old value as the result.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003406 if (is_postfix) {
3407 __ mov(frame_->Element(target.size()), eax);
3408 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003409
3410 // Perform optimistic increment/decrement.
3411 if (is_increment) {
3412 __ add(Operand(eax), Immediate(Smi::FromInt(1)));
3413 } else {
3414 __ sub(Operand(eax), Immediate(Smi::FromInt(1)));
3415 }
3416
3417 // If the count operation didn't overflow and the result is a
3418 // valid smi, we're done. Otherwise, we jump to the deferred
3419 // slow-case code.
3420 __ j(overflow, deferred->enter(), not_taken);
3421 __ test(eax, Immediate(kSmiTagMask));
3422 __ j(not_zero, deferred->enter(), not_taken);
3423
3424 // Store the new value in the target if not const.
3425 __ bind(deferred->exit());
ager@chromium.org7c537e22008-10-16 08:43:32 +00003426 frame_->Push(eax); // Push the new value to TOS
3427 if (!is_const) target.SetValue(NOT_CONST_INIT);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003428 }
3429
3430 // Postfix: Discard the new value and use the old.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003431 if (is_postfix) {
3432 frame_->Pop();
3433 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003434}
3435
3436
ager@chromium.org7c537e22008-10-16 08:43:32 +00003437void CodeGenerator::VisitBinaryOperation(BinaryOperation* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003438 Comment cmnt(masm_, "[ BinaryOperation");
3439 Token::Value op = node->op();
3440
3441 // According to ECMA-262 section 11.11, page 58, the binary logical
3442 // operators must yield the result of one of the two expressions
3443 // before any ToBoolean() conversions. This means that the value
3444 // produced by a && or || operator is not necessarily a boolean.
3445
3446 // NOTE: If the left hand side produces a materialized value (not in
3447 // the CC register), we force the right hand side to do the
3448 // same. This is necessary because we may have to branch to the exit
3449 // after evaluating the left hand side (due to the shortcut
3450 // semantics), but the compiler must (statically) know if the result
3451 // of compiling the binary operation is materialized or not.
3452
3453 if (op == Token::AND) {
3454 Label is_true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00003455 LoadCondition(node->left(), NOT_INSIDE_TYPEOF, &is_true,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003456 false_target(), false);
3457 if (has_cc()) {
3458 Branch(false, false_target());
3459
3460 // Evaluate right side expression.
3461 __ bind(&is_true);
ager@chromium.org7c537e22008-10-16 08:43:32 +00003462 LoadCondition(node->right(), NOT_INSIDE_TYPEOF, true_target(),
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003463 false_target(), false);
3464
3465 } else {
3466 Label pop_and_continue, exit;
3467
3468 // Avoid popping the result if it converts to 'false' using the
3469 // standard ToBoolean() conversion as described in ECMA-262,
3470 // section 9.2, page 30.
3471 // Duplicate the TOS value. The duplicate will be popped by ToBoolean.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003472 __ mov(eax, frame_->Top());
3473 frame_->Push(eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003474 ToBoolean(&pop_and_continue, &exit);
3475 Branch(false, &exit);
3476
3477 // Pop the result of evaluating the first part.
3478 __ bind(&pop_and_continue);
ager@chromium.org7c537e22008-10-16 08:43:32 +00003479 frame_->Pop();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003480
3481 // Evaluate right side expression.
3482 __ bind(&is_true);
3483 Load(node->right());
3484
3485 // Exit (always with a materialized value).
3486 __ bind(&exit);
3487 }
3488
3489 } else if (op == Token::OR) {
3490 Label is_false;
ager@chromium.org7c537e22008-10-16 08:43:32 +00003491 LoadCondition(node->left(), NOT_INSIDE_TYPEOF, true_target(),
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003492 &is_false, false);
3493 if (has_cc()) {
3494 Branch(true, true_target());
3495
3496 // Evaluate right side expression.
3497 __ bind(&is_false);
ager@chromium.org7c537e22008-10-16 08:43:32 +00003498 LoadCondition(node->right(), NOT_INSIDE_TYPEOF, true_target(),
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003499 false_target(), false);
3500
3501 } else {
3502 Label pop_and_continue, exit;
3503
3504 // Avoid popping the result if it converts to 'true' using the
3505 // standard ToBoolean() conversion as described in ECMA-262,
3506 // section 9.2, page 30.
3507 // Duplicate the TOS value. The duplicate will be popped by ToBoolean.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003508 __ mov(eax, frame_->Top());
3509 frame_->Push(eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003510 ToBoolean(&exit, &pop_and_continue);
3511 Branch(true, &exit);
3512
3513 // Pop the result of evaluating the first part.
3514 __ bind(&pop_and_continue);
ager@chromium.org7c537e22008-10-16 08:43:32 +00003515 frame_->Pop();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003516
3517 // Evaluate right side expression.
3518 __ bind(&is_false);
3519 Load(node->right());
3520
3521 // Exit (always with a materialized value).
3522 __ bind(&exit);
3523 }
3524
3525 } else {
kasper.lund7276f142008-07-30 08:49:36 +00003526 // NOTE: The code below assumes that the slow cases (calls to runtime)
3527 // never return a constant/immutable object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003528 OverwriteMode overwrite_mode = NO_OVERWRITE;
3529 if (node->left()->AsBinaryOperation() != NULL &&
3530 node->left()->AsBinaryOperation()->ResultOverwriteAllowed()) {
3531 overwrite_mode = OVERWRITE_LEFT;
3532 } else if (node->right()->AsBinaryOperation() != NULL &&
3533 node->right()->AsBinaryOperation()->ResultOverwriteAllowed()) {
3534 overwrite_mode = OVERWRITE_RIGHT;
3535 }
3536
3537 // Optimize for the case where (at least) one of the expressions
3538 // is a literal small integer.
3539 Literal* lliteral = node->left()->AsLiteral();
3540 Literal* rliteral = node->right()->AsLiteral();
3541
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003542 if (IsInlineSmi(rliteral)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003543 Load(node->left());
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00003544 SmiOperation(node->op(), node->type(), rliteral->handle(), false,
3545 overwrite_mode);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003546 } else if (IsInlineSmi(lliteral)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003547 Load(node->right());
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00003548 SmiOperation(node->op(), node->type(), lliteral->handle(), true,
3549 overwrite_mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003550 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003551 Load(node->left());
3552 Load(node->right());
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00003553 GenericBinaryOperation(node->op(), node->type(), overwrite_mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003554 }
3555 }
3556}
3557
3558
ager@chromium.org7c537e22008-10-16 08:43:32 +00003559void CodeGenerator::VisitThisFunction(ThisFunction* node) {
3560 frame_->Push(frame_->Function());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003561}
3562
3563
ager@chromium.org7c537e22008-10-16 08:43:32 +00003564class InstanceofStub: public CodeStub {
3565 public:
3566 InstanceofStub() { }
3567
3568 void Generate(MacroAssembler* masm);
3569
3570 private:
3571 Major MajorKey() { return Instanceof; }
3572 int MinorKey() { return 0; }
3573};
3574
3575
3576void CodeGenerator::VisitCompareOperation(CompareOperation* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003577 Comment cmnt(masm_, "[ CompareOperation");
3578
3579 // Get the expressions from the node.
3580 Expression* left = node->left();
3581 Expression* right = node->right();
3582 Token::Value op = node->op();
3583
ager@chromium.org3bf7b912008-11-17 09:09:45 +00003584 // To make null checks efficient, we check if either left or right is the
3585 // literal 'null'. If so, we optimize the code by inlining a null check
3586 // instead of calling the (very) general runtime routine for checking
3587 // equality.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003588 if (op == Token::EQ || op == Token::EQ_STRICT) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003589 bool left_is_null =
ager@chromium.org3bf7b912008-11-17 09:09:45 +00003590 left->AsLiteral() != NULL && left->AsLiteral()->IsNull();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003591 bool right_is_null =
ager@chromium.org3bf7b912008-11-17 09:09:45 +00003592 right->AsLiteral() != NULL && right->AsLiteral()->IsNull();
3593 // The 'null' value can only be equal to 'null' or 'undefined'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003594 if (left_is_null || right_is_null) {
3595 Load(left_is_null ? right : left);
ager@chromium.org7c537e22008-10-16 08:43:32 +00003596 frame_->Pop(eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003597 __ cmp(eax, Factory::null_value());
3598
ager@chromium.org3bf7b912008-11-17 09:09:45 +00003599 // The 'null' value is only equal to 'undefined' if using non-strict
3600 // comparisons.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003601 if (op != Token::EQ_STRICT) {
ager@chromium.org3bf7b912008-11-17 09:09:45 +00003602 __ j(equal, true_target());
3603
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003604 __ cmp(eax, Factory::undefined_value());
ager@chromium.org3bf7b912008-11-17 09:09:45 +00003605 __ j(equal, true_target());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003606
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003607 __ test(eax, Immediate(kSmiTagMask));
ager@chromium.org3bf7b912008-11-17 09:09:45 +00003608 __ j(equal, false_target());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003609
ager@chromium.org3bf7b912008-11-17 09:09:45 +00003610 // It can be an undetectable object.
3611 __ mov(eax, FieldOperand(eax, HeapObject::kMapOffset));
3612 __ movzx_b(eax, FieldOperand(eax, Map::kBitFieldOffset));
3613 __ and_(eax, 1 << Map::kIsUndetectable);
3614 __ cmp(eax, 1 << Map::kIsUndetectable);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003615 }
3616
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003617 cc_reg_ = equal;
3618 return;
3619 }
3620 }
3621
ager@chromium.org3bf7b912008-11-17 09:09:45 +00003622 // To make typeof testing for natives implemented in JavaScript really
3623 // efficient, we generate special code for expressions of the form:
3624 // 'typeof <expression> == <string>'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003625 UnaryOperation* operation = left->AsUnaryOperation();
3626 if ((op == Token::EQ || op == Token::EQ_STRICT) &&
3627 (operation != NULL && operation->op() == Token::TYPEOF) &&
3628 (right->AsLiteral() != NULL &&
3629 right->AsLiteral()->handle()->IsString())) {
3630 Handle<String> check(String::cast(*right->AsLiteral()->handle()));
3631
ager@chromium.org3bf7b912008-11-17 09:09:45 +00003632 // Load the operand and move it to register edx.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003633 LoadTypeofExpression(operation->expression());
ager@chromium.org7c537e22008-10-16 08:43:32 +00003634 frame_->Pop(edx);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003635
3636 if (check->Equals(Heap::number_symbol())) {
3637 __ test(edx, Immediate(kSmiTagMask));
3638 __ j(zero, true_target());
3639 __ mov(edx, FieldOperand(edx, HeapObject::kMapOffset));
3640 __ cmp(edx, Factory::heap_number_map());
3641 cc_reg_ = equal;
3642
3643 } else if (check->Equals(Heap::string_symbol())) {
3644 __ test(edx, Immediate(kSmiTagMask));
3645 __ j(zero, false_target());
3646
3647 __ mov(edx, FieldOperand(edx, HeapObject::kMapOffset));
3648
ager@chromium.org3bf7b912008-11-17 09:09:45 +00003649 // It can be an undetectable string object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003650 __ movzx_b(ecx, FieldOperand(edx, Map::kBitFieldOffset));
3651 __ and_(ecx, 1 << Map::kIsUndetectable);
3652 __ cmp(ecx, 1 << Map::kIsUndetectable);
3653 __ j(equal, false_target());
3654
3655 __ movzx_b(ecx, FieldOperand(edx, Map::kInstanceTypeOffset));
3656 __ cmp(ecx, FIRST_NONSTRING_TYPE);
3657 cc_reg_ = less;
3658
3659 } else if (check->Equals(Heap::boolean_symbol())) {
3660 __ cmp(edx, Factory::true_value());
3661 __ j(equal, true_target());
3662 __ cmp(edx, Factory::false_value());
3663 cc_reg_ = equal;
3664
3665 } else if (check->Equals(Heap::undefined_symbol())) {
3666 __ cmp(edx, Factory::undefined_value());
3667 __ j(equal, true_target());
3668
3669 __ test(edx, Immediate(kSmiTagMask));
3670 __ j(zero, false_target());
3671
ager@chromium.org3bf7b912008-11-17 09:09:45 +00003672 // It can be an undetectable object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003673 __ mov(edx, FieldOperand(edx, HeapObject::kMapOffset));
3674 __ movzx_b(ecx, FieldOperand(edx, Map::kBitFieldOffset));
3675 __ and_(ecx, 1 << Map::kIsUndetectable);
3676 __ cmp(ecx, 1 << Map::kIsUndetectable);
3677
3678 cc_reg_ = equal;
3679
3680 } else if (check->Equals(Heap::function_symbol())) {
3681 __ test(edx, Immediate(kSmiTagMask));
3682 __ j(zero, false_target());
3683 __ mov(edx, FieldOperand(edx, HeapObject::kMapOffset));
3684 __ movzx_b(edx, FieldOperand(edx, Map::kInstanceTypeOffset));
3685 __ cmp(edx, JS_FUNCTION_TYPE);
3686 cc_reg_ = equal;
3687
3688 } else if (check->Equals(Heap::object_symbol())) {
3689 __ test(edx, Immediate(kSmiTagMask));
3690 __ j(zero, false_target());
3691
3692 __ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset));
3693 __ cmp(edx, Factory::null_value());
3694 __ j(equal, true_target());
3695
ager@chromium.org3bf7b912008-11-17 09:09:45 +00003696 // It can be an undetectable object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003697 __ movzx_b(edx, FieldOperand(ecx, Map::kBitFieldOffset));
3698 __ and_(edx, 1 << Map::kIsUndetectable);
3699 __ cmp(edx, 1 << Map::kIsUndetectable);
3700 __ j(equal, false_target());
3701
3702 __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
3703 __ cmp(ecx, FIRST_JS_OBJECT_TYPE);
3704 __ j(less, false_target());
3705 __ cmp(ecx, LAST_JS_OBJECT_TYPE);
3706 cc_reg_ = less_equal;
3707
3708 } else {
ager@chromium.org3bf7b912008-11-17 09:09:45 +00003709 // Uncommon case: typeof testing against a string literal that is
3710 // never returned from the typeof operator.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003711 __ jmp(false_target());
3712 }
3713 return;
3714 }
3715
3716 Condition cc = no_condition;
3717 bool strict = false;
3718 switch (op) {
3719 case Token::EQ_STRICT:
3720 strict = true;
3721 // Fall through
3722 case Token::EQ:
3723 cc = equal;
3724 break;
3725 case Token::LT:
3726 cc = less;
3727 break;
3728 case Token::GT:
3729 cc = greater;
3730 break;
3731 case Token::LTE:
3732 cc = less_equal;
3733 break;
3734 case Token::GTE:
3735 cc = greater_equal;
3736 break;
3737 case Token::IN: {
3738 Load(left);
3739 Load(right);
3740 __ InvokeBuiltin(Builtins::IN, CALL_FUNCTION);
ager@chromium.org7c537e22008-10-16 08:43:32 +00003741 frame_->Push(eax); // push the result
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003742 return;
3743 }
3744 case Token::INSTANCEOF: {
3745 Load(left);
3746 Load(right);
ager@chromium.org7c537e22008-10-16 08:43:32 +00003747 InstanceofStub stub;
3748 __ CallStub(&stub);
3749 __ test(eax, Operand(eax));
3750 cc_reg_ = zero;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003751 return;
3752 }
3753 default:
3754 UNREACHABLE();
3755 }
3756
3757 // Optimize for the case where (at least) one of the expressions
3758 // is a literal small integer.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003759 if (IsInlineSmi(left->AsLiteral())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003760 Load(right);
3761 SmiComparison(ReverseCondition(cc), left->AsLiteral()->handle(), strict);
3762 return;
3763 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003764 if (IsInlineSmi(right->AsLiteral())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003765 Load(left);
3766 SmiComparison(cc, right->AsLiteral()->handle(), strict);
3767 return;
3768 }
3769
3770 Load(left);
3771 Load(right);
3772 Comparison(cc, strict);
3773}
3774
3775
ager@chromium.org7c537e22008-10-16 08:43:32 +00003776void CodeGenerator::RecordStatementPosition(Node* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003777 if (FLAG_debug_info) {
3778 int pos = node->statement_pos();
ager@chromium.org236ad962008-09-25 09:45:57 +00003779 if (pos != RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003780 __ RecordStatementPosition(pos);
3781 }
3782 }
3783}
3784
3785
ager@chromium.org7c537e22008-10-16 08:43:32 +00003786#undef __
3787#define __ masm->
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003788
ager@chromium.org7c537e22008-10-16 08:43:32 +00003789Handle<String> Reference::GetName() {
3790 ASSERT(type_ == NAMED);
3791 Property* property = expression_->AsProperty();
3792 if (property == NULL) {
3793 // Global variable reference treated as a named property reference.
3794 VariableProxy* proxy = expression_->AsVariableProxy();
3795 ASSERT(proxy->AsVariable() != NULL);
3796 ASSERT(proxy->AsVariable()->is_global());
3797 return proxy->name();
3798 } else {
3799 MacroAssembler* masm = cgen_->masm();
3800 __ RecordPosition(property->position());
3801 Literal* raw_name = property->key()->AsLiteral();
3802 ASSERT(raw_name != NULL);
3803 return Handle<String>(String::cast(*raw_name->handle()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003804 }
3805}
3806
3807
ager@chromium.org7c537e22008-10-16 08:43:32 +00003808void Reference::GetValue(TypeofState typeof_state) {
3809 ASSERT(!is_illegal());
3810 ASSERT(!cgen_->has_cc());
3811 MacroAssembler* masm = cgen_->masm();
3812 VirtualFrame* frame = cgen_->frame();
3813 switch (type_) {
3814 case SLOT: {
3815 Comment cmnt(masm, "[ Load from Slot");
3816 Slot* slot = expression_->AsVariableProxy()->AsVariable()->slot();
3817 ASSERT(slot != NULL);
3818 cgen_->LoadFromSlot(slot, typeof_state);
3819 break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003820 }
3821
ager@chromium.org7c537e22008-10-16 08:43:32 +00003822 case NAMED: {
3823 // TODO(1241834): Make sure that this it is safe to ignore the
3824 // distinction between expressions in a typeof and not in a typeof. If
3825 // there is a chance that reference errors can be thrown below, we
3826 // must distinguish between the two kinds of loads (typeof expression
3827 // loads must not throw a reference error).
3828 Comment cmnt(masm, "[ Load from named Property");
3829 Handle<String> name(GetName());
3830 Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize));
3831 // Setup the name register.
3832 __ mov(ecx, name);
3833
3834 Variable* var = expression_->AsVariableProxy()->AsVariable();
3835 if (var != NULL) {
3836 ASSERT(var->is_global());
3837 __ call(ic, RelocInfo::CODE_TARGET_CONTEXT);
3838 } else {
3839 __ call(ic, RelocInfo::CODE_TARGET);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003840 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00003841 frame->Push(eax); // IC call leaves result in eax, push it out
3842 break;
3843 }
3844
3845 case KEYED: {
3846 // TODO(1241834): Make sure that this it is safe to ignore the
3847 // distinction between expressions in a typeof and not in a typeof.
3848 Comment cmnt(masm, "[ Load from keyed Property");
3849 Property* property = expression_->AsProperty();
3850 ASSERT(property != NULL);
3851 __ RecordPosition(property->position());
3852 Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
3853
3854 Variable* var = expression_->AsVariableProxy()->AsVariable();
3855 if (var != NULL) {
3856 ASSERT(var->is_global());
3857 __ call(ic, RelocInfo::CODE_TARGET_CONTEXT);
3858 } else {
3859 __ call(ic, RelocInfo::CODE_TARGET);
3860 }
3861 frame->Push(eax); // IC call leaves result in eax, push it out
3862 break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003863 }
3864
3865 default:
3866 UNREACHABLE();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003867 }
3868}
3869
3870
ager@chromium.org7c537e22008-10-16 08:43:32 +00003871void Reference::SetValue(InitState init_state) {
3872 ASSERT(!is_illegal());
3873 ASSERT(!cgen_->has_cc());
3874 MacroAssembler* masm = cgen_->masm();
3875 VirtualFrame* frame = cgen_->frame();
3876 switch (type_) {
3877 case SLOT: {
3878 Comment cmnt(masm, "[ Store to Slot");
3879 Slot* slot = expression_->AsVariableProxy()->AsVariable()->slot();
3880 ASSERT(slot != NULL);
3881 if (slot->type() == Slot::LOOKUP) {
3882 ASSERT(slot->var()->mode() == Variable::DYNAMIC);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003883
ager@chromium.org7c537e22008-10-16 08:43:32 +00003884 // For now, just do a runtime call.
3885 frame->Push(esi);
3886 frame->Push(Immediate(slot->var()->name()));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003887
ager@chromium.org7c537e22008-10-16 08:43:32 +00003888 if (init_state == CONST_INIT) {
3889 // Same as the case for a normal store, but ignores attribute
3890 // (e.g. READ_ONLY) of context slot so that we can initialize
3891 // const properties (introduced via eval("const foo = (some
3892 // expr);")). Also, uses the current function context instead of
3893 // the top context.
3894 //
3895 // Note that we must declare the foo upon entry of eval(), via a
3896 // context slot declaration, but we cannot initialize it at the
3897 // same time, because the const declaration may be at the end of
3898 // the eval code (sigh...) and the const variable may have been
3899 // used before (where its value is 'undefined'). Thus, we can only
3900 // do the initialization when we actually encounter the expression
3901 // and when the expression operands are defined and valid, and
3902 // thus we need the split into 2 operations: declaration of the
3903 // context slot followed by initialization.
3904 __ CallRuntime(Runtime::kInitializeConstContextSlot, 3);
3905 } else {
3906 __ CallRuntime(Runtime::kStoreContextSlot, 3);
3907 }
3908 // Storing a variable must keep the (new) value on the expression
3909 // stack. This is necessary for compiling chained assignment
3910 // expressions.
3911 frame->Push(eax);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003912
ager@chromium.org7c537e22008-10-16 08:43:32 +00003913 } else {
3914 ASSERT(slot->var()->mode() != Variable::DYNAMIC);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003915
ager@chromium.org7c537e22008-10-16 08:43:32 +00003916 Label exit;
3917 if (init_state == CONST_INIT) {
3918 ASSERT(slot->var()->mode() == Variable::CONST);
3919 // Only the first const initialization must be executed (the slot
3920 // still contains 'the hole' value). When the assignment is
3921 // executed, the code is identical to a normal store (see below).
3922 Comment cmnt(masm, "[ Init const");
3923 __ mov(eax, cgen_->SlotOperand(slot, ecx));
3924 __ cmp(eax, Factory::the_hole_value());
3925 __ j(not_equal, &exit);
3926 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003927
ager@chromium.org7c537e22008-10-16 08:43:32 +00003928 // We must execute the store. Storing a variable must keep the
3929 // (new) value on the stack. This is necessary for compiling
3930 // assignment expressions.
3931 //
3932 // Note: We will reach here even with slot->var()->mode() ==
3933 // Variable::CONST because of const declarations which will
3934 // initialize consts to 'the hole' value and by doing so, end up
3935 // calling this code.
3936 frame->Pop(eax);
3937 __ mov(cgen_->SlotOperand(slot, ecx), eax);
3938 frame->Push(eax); // RecordWrite may destroy the value in eax.
3939 if (slot->type() == Slot::CONTEXT) {
3940 // ecx is loaded with context when calling SlotOperand above.
3941 int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize;
3942 __ RecordWrite(ecx, offset, eax, ebx);
3943 }
3944 // If we definitely did not jump over the assignment, we do not need
3945 // to bind the exit label. Doing so can defeat peephole
3946 // optimization.
3947 if (init_state == CONST_INIT) __ bind(&exit);
3948 }
3949 break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003950 }
3951
ager@chromium.org7c537e22008-10-16 08:43:32 +00003952 case NAMED: {
3953 Comment cmnt(masm, "[ Store to named Property");
3954 // Call the appropriate IC code.
3955 Handle<String> name(GetName());
3956 Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
3957 // TODO(1222589): Make the IC grab the values from the stack.
3958 frame->Pop(eax);
3959 // Setup the name register.
3960 __ mov(ecx, name);
3961 __ call(ic, RelocInfo::CODE_TARGET);
3962 frame->Push(eax); // IC call leaves result in eax, push it out
3963 break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003964 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00003965
3966 case KEYED: {
3967 Comment cmnt(masm, "[ Store to keyed Property");
3968 Property* property = expression_->AsProperty();
3969 ASSERT(property != NULL);
3970 __ RecordPosition(property->position());
3971 // Call IC code.
3972 Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize));
3973 // TODO(1222589): Make the IC grab the values from the stack.
3974 frame->Pop(eax);
3975 __ call(ic, RelocInfo::CODE_TARGET);
3976 frame->Push(eax); // IC call leaves result in eax, push it out
3977 break;
3978 }
3979
3980 default:
3981 UNREACHABLE();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003982 }
3983}
3984
3985
3986// NOTE: The stub does not handle the inlined cases (Smis, Booleans, undefined).
3987void ToBooleanStub::Generate(MacroAssembler* masm) {
3988 Label false_result, true_result, not_string;
3989 __ mov(eax, Operand(esp, 1 * kPointerSize));
3990
3991 // 'null' => false.
3992 __ cmp(eax, Factory::null_value());
3993 __ j(equal, &false_result);
3994
3995 // Get the map and type of the heap object.
3996 __ mov(edx, FieldOperand(eax, HeapObject::kMapOffset));
3997 __ movzx_b(ecx, FieldOperand(edx, Map::kInstanceTypeOffset));
3998
3999 // Undetectable => false.
4000 __ movzx_b(ebx, FieldOperand(edx, Map::kBitFieldOffset));
4001 __ and_(ebx, 1 << Map::kIsUndetectable);
4002 __ j(not_zero, &false_result);
4003
4004 // JavaScript object => true.
4005 __ cmp(ecx, FIRST_JS_OBJECT_TYPE);
4006 __ j(above_equal, &true_result);
4007
4008 // String value => false iff empty.
4009 __ cmp(ecx, FIRST_NONSTRING_TYPE);
4010 __ j(above_equal, &not_string);
4011 __ and_(ecx, kStringSizeMask);
4012 __ cmp(ecx, kShortStringTag);
4013 __ j(not_equal, &true_result); // Empty string is always short.
4014 __ mov(edx, FieldOperand(eax, String::kLengthOffset));
4015 __ shr(edx, String::kShortLengthShift);
4016 __ j(zero, &false_result);
4017 __ jmp(&true_result);
4018
4019 __ bind(&not_string);
4020 // HeapNumber => false iff +0, -0, or NaN.
4021 __ cmp(edx, Factory::heap_number_map());
4022 __ j(not_equal, &true_result);
4023 __ fldz();
4024 __ fld_d(FieldOperand(eax, HeapNumber::kValueOffset));
4025 __ fucompp();
4026 __ push(eax);
4027 __ fnstsw_ax();
4028 __ sahf();
4029 __ pop(eax);
4030 __ j(zero, &false_result);
4031 // Fall through to |true_result|.
4032
4033 // Return 1/0 for true/false in eax.
4034 __ bind(&true_result);
4035 __ mov(eax, 1);
4036 __ ret(1 * kPointerSize);
4037 __ bind(&false_result);
4038 __ mov(eax, 0);
4039 __ ret(1 * kPointerSize);
4040}
4041
4042
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004043void GenericBinaryOpStub::GenerateSmiCode(MacroAssembler* masm, Label* slow) {
4044 // Perform fast-case smi code for the operation (eax <op> ebx) and
4045 // leave result in register eax.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004046
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004047 // Prepare the smi check of both operands by or'ing them together
4048 // before checking against the smi mask.
4049 __ mov(ecx, Operand(ebx));
4050 __ or_(ecx, Operand(eax));
4051
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004052 switch (op_) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004053 case Token::ADD:
4054 __ add(eax, Operand(ebx)); // add optimistically
4055 __ j(overflow, slow, not_taken);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004056 break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004057
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004058 case Token::SUB:
4059 __ sub(eax, Operand(ebx)); // subtract optimistically
4060 __ j(overflow, slow, not_taken);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004061 break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004062
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004063 case Token::DIV:
4064 case Token::MOD:
4065 // Sign extend eax into edx:eax.
4066 __ cdq();
4067 // Check for 0 divisor.
4068 __ test(ebx, Operand(ebx));
4069 __ j(zero, slow, not_taken);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004070 break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004071
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004072 default:
4073 // Fall-through to smi check.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004074 break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004075 }
4076
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004077 // Perform the actual smi check.
4078 ASSERT(kSmiTag == 0); // adjust zero check if not the case
4079 __ test(ecx, Immediate(kSmiTagMask));
4080 __ j(not_zero, slow, not_taken);
4081
4082 switch (op_) {
4083 case Token::ADD:
4084 case Token::SUB:
4085 // Do nothing here.
4086 break;
4087
4088 case Token::MUL:
4089 // If the smi tag is 0 we can just leave the tag on one operand.
4090 ASSERT(kSmiTag == 0); // adjust code below if not the case
4091 // Remove tag from one of the operands (but keep sign).
4092 __ sar(eax, kSmiTagSize);
4093 // Do multiplication.
4094 __ imul(eax, Operand(ebx)); // multiplication of smis; result in eax
4095 // Go slow on overflows.
4096 __ j(overflow, slow, not_taken);
4097 // Check for negative zero result.
4098 __ NegativeZeroTest(eax, ecx, slow); // use ecx = x | y
4099 break;
4100
4101 case Token::DIV:
4102 // Divide edx:eax by ebx.
4103 __ idiv(ebx);
4104 // Check for the corner case of dividing the most negative smi
4105 // by -1. We cannot use the overflow flag, since it is not set
4106 // by idiv instruction.
4107 ASSERT(kSmiTag == 0 && kSmiTagSize == 1);
4108 __ cmp(eax, 0x40000000);
4109 __ j(equal, slow);
4110 // Check for negative zero result.
4111 __ NegativeZeroTest(eax, ecx, slow); // use ecx = x | y
4112 // Check that the remainder is zero.
4113 __ test(edx, Operand(edx));
4114 __ j(not_zero, slow);
4115 // Tag the result and store it in register eax.
4116 ASSERT(kSmiTagSize == times_2); // adjust code if not the case
ager@chromium.org3bf7b912008-11-17 09:09:45 +00004117 __ lea(eax, Operand(eax, eax, times_1, kSmiTag));
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004118 break;
4119
4120 case Token::MOD:
4121 // Divide edx:eax by ebx.
4122 __ idiv(ebx);
4123 // Check for negative zero result.
4124 __ NegativeZeroTest(edx, ecx, slow); // use ecx = x | y
4125 // Move remainder to register eax.
4126 __ mov(eax, Operand(edx));
4127 break;
4128
4129 case Token::BIT_OR:
4130 __ or_(eax, Operand(ebx));
4131 break;
4132
4133 case Token::BIT_AND:
4134 __ and_(eax, Operand(ebx));
4135 break;
4136
4137 case Token::BIT_XOR:
4138 __ xor_(eax, Operand(ebx));
4139 break;
4140
4141 case Token::SHL:
4142 case Token::SHR:
4143 case Token::SAR:
4144 // Move the second operand into register ecx.
4145 __ mov(ecx, Operand(ebx));
4146 // Remove tags from operands (but keep sign).
4147 __ sar(eax, kSmiTagSize);
4148 __ sar(ecx, kSmiTagSize);
4149 // Perform the operation.
4150 switch (op_) {
4151 case Token::SAR:
4152 __ sar(eax);
4153 // No checks of result necessary
4154 break;
4155 case Token::SHR:
4156 __ shr(eax);
4157 // Check that the *unsigned* result fits in a smi.
4158 // Neither of the two high-order bits can be set:
4159 // - 0x80000000: high bit would be lost when smi tagging.
4160 // - 0x40000000: this number would convert to negative when
4161 // Smi tagging these two cases can only happen with shifts
4162 // by 0 or 1 when handed a valid smi.
4163 __ test(eax, Immediate(0xc0000000));
4164 __ j(not_zero, slow, not_taken);
4165 break;
4166 case Token::SHL:
4167 __ shl(eax);
4168 // Check that the *signed* result fits in a smi.
4169 __ lea(ecx, Operand(eax, 0x40000000));
4170 __ test(ecx, Immediate(0x80000000));
4171 __ j(not_zero, slow, not_taken);
4172 break;
4173 default:
4174 UNREACHABLE();
4175 }
4176 // Tag the result and store it in register eax.
4177 ASSERT(kSmiTagSize == times_2); // adjust code if not the case
ager@chromium.org3bf7b912008-11-17 09:09:45 +00004178 __ lea(eax, Operand(eax, eax, times_1, kSmiTag));
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004179 break;
4180
4181 default:
4182 UNREACHABLE();
4183 break;
4184 }
4185}
4186
4187
4188void GenericBinaryOpStub::Generate(MacroAssembler* masm) {
4189 Label call_runtime;
4190
4191 if (flags_ == SMI_CODE_IN_STUB) {
4192 // The fast case smi code wasn't inlined in the stub caller
4193 // code. Generate it here to speed up common operations.
4194 Label slow;
4195 __ mov(ebx, Operand(esp, 1 * kPointerSize)); // get y
4196 __ mov(eax, Operand(esp, 2 * kPointerSize)); // get x
4197 GenerateSmiCode(masm, &slow);
4198 __ ret(2 * kPointerSize); // remove both operands
4199
4200 // Too bad. The fast case smi code didn't succeed.
4201 __ bind(&slow);
4202 }
4203
4204 // Setup registers.
4205 __ mov(eax, Operand(esp, 1 * kPointerSize)); // get y
4206 __ mov(edx, Operand(esp, 2 * kPointerSize)); // get x
4207
4208 // Floating point case.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004209 switch (op_) {
4210 case Token::ADD:
4211 case Token::SUB:
4212 case Token::MUL:
4213 case Token::DIV: {
4214 // eax: y
4215 // edx: x
4216 FloatingPointHelper::CheckFloatOperands(masm, &call_runtime, ebx);
4217 // Fast-case: Both operands are numbers.
4218 // Allocate a heap number, if needed.
4219 Label skip_allocation;
4220 switch (mode_) {
4221 case OVERWRITE_LEFT:
4222 __ mov(eax, Operand(edx));
4223 // Fall through!
4224 case OVERWRITE_RIGHT:
4225 // If the argument in eax is already an object, we skip the
4226 // allocation of a heap number.
4227 __ test(eax, Immediate(kSmiTagMask));
4228 __ j(not_zero, &skip_allocation, not_taken);
4229 // Fall through!
4230 case NO_OVERWRITE:
4231 FloatingPointHelper::AllocateHeapNumber(masm,
4232 &call_runtime,
4233 ecx,
4234 edx);
4235 __ bind(&skip_allocation);
4236 break;
4237 default: UNREACHABLE();
4238 }
4239 FloatingPointHelper::LoadFloatOperands(masm, ecx);
4240
4241 switch (op_) {
4242 case Token::ADD: __ faddp(1); break;
4243 case Token::SUB: __ fsubp(1); break;
4244 case Token::MUL: __ fmulp(1); break;
4245 case Token::DIV: __ fdivp(1); break;
4246 default: UNREACHABLE();
4247 }
4248 __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
4249 __ ret(2 * kPointerSize);
4250 }
4251 case Token::MOD: {
4252 // For MOD we go directly to runtime in the non-smi case.
4253 break;
4254 }
4255 case Token::BIT_OR:
4256 case Token::BIT_AND:
4257 case Token::BIT_XOR:
4258 case Token::SAR:
4259 case Token::SHL:
4260 case Token::SHR: {
4261 FloatingPointHelper::CheckFloatOperands(masm, &call_runtime, ebx);
4262 FloatingPointHelper::LoadFloatOperands(masm, ecx);
4263
4264 Label non_int32_operands, non_smi_result, skip_allocation;
4265 // Reserve space for converted numbers.
4266 __ sub(Operand(esp), Immediate(2 * kPointerSize));
4267
4268 // Check if right operand is int32.
4269 __ fist_s(Operand(esp, 1 * kPointerSize));
4270 __ fild_s(Operand(esp, 1 * kPointerSize));
4271 __ fucompp();
4272 __ fnstsw_ax();
4273 __ sahf();
4274 __ j(not_zero, &non_int32_operands);
4275 __ j(parity_even, &non_int32_operands);
4276
4277 // Check if left operand is int32.
4278 __ fist_s(Operand(esp, 0 * kPointerSize));
4279 __ fild_s(Operand(esp, 0 * kPointerSize));
4280 __ fucompp();
4281 __ fnstsw_ax();
4282 __ sahf();
4283 __ j(not_zero, &non_int32_operands);
4284 __ j(parity_even, &non_int32_operands);
4285
4286 // Get int32 operands and perform bitop.
4287 __ pop(eax);
4288 __ pop(ecx);
4289 switch (op_) {
4290 case Token::BIT_OR: __ or_(eax, Operand(ecx)); break;
4291 case Token::BIT_AND: __ and_(eax, Operand(ecx)); break;
4292 case Token::BIT_XOR: __ xor_(eax, Operand(ecx)); break;
4293 case Token::SAR: __ sar(eax); break;
4294 case Token::SHL: __ shl(eax); break;
4295 case Token::SHR: __ shr(eax); break;
4296 default: UNREACHABLE();
4297 }
4298
4299 // Check if result is non-negative and fits in a smi.
4300 __ test(eax, Immediate(0xc0000000));
4301 __ j(not_zero, &non_smi_result);
4302
4303 // Tag smi result and return.
4304 ASSERT(kSmiTagSize == times_2); // adjust code if not the case
ager@chromium.org3bf7b912008-11-17 09:09:45 +00004305 __ lea(eax, Operand(eax, eax, times_1, kSmiTag));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004306 __ ret(2 * kPointerSize);
4307
4308 // All ops except SHR return a signed int32 that we load in a HeapNumber.
4309 if (op_ != Token::SHR) {
4310 __ bind(&non_smi_result);
4311 // Allocate a heap number if needed.
4312 __ mov(ebx, Operand(eax)); // ebx: result
4313 switch (mode_) {
4314 case OVERWRITE_LEFT:
4315 case OVERWRITE_RIGHT:
4316 // If the operand was an object, we skip the
4317 // allocation of a heap number.
4318 __ mov(eax, Operand(esp, mode_ == OVERWRITE_RIGHT ?
4319 1 * kPointerSize : 2 * kPointerSize));
4320 __ test(eax, Immediate(kSmiTagMask));
4321 __ j(not_zero, &skip_allocation, not_taken);
4322 // Fall through!
4323 case NO_OVERWRITE:
4324 FloatingPointHelper::AllocateHeapNumber(masm, &call_runtime,
4325 ecx, edx);
4326 __ bind(&skip_allocation);
4327 break;
4328 default: UNREACHABLE();
4329 }
4330 // Store the result in the HeapNumber and return.
4331 __ mov(Operand(esp, 1 * kPointerSize), ebx);
4332 __ fild_s(Operand(esp, 1 * kPointerSize));
4333 __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
4334 __ ret(2 * kPointerSize);
4335 }
4336 __ bind(&non_int32_operands);
4337 // Restore stacks and operands before calling runtime.
4338 __ ffree(0);
4339 __ add(Operand(esp), Immediate(2 * kPointerSize));
4340
4341 // SHR should return uint32 - go to runtime for non-smi/negative result.
4342 if (op_ == Token::SHR) __ bind(&non_smi_result);
4343 __ mov(eax, Operand(esp, 1 * kPointerSize));
4344 __ mov(edx, Operand(esp, 2 * kPointerSize));
4345 break;
4346 }
4347 default: UNREACHABLE(); break;
4348 }
4349
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004350 // If all else fails, use the runtime system to get the correct
4351 // result.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004352 __ bind(&call_runtime);
4353 switch (op_) {
4354 case Token::ADD:
4355 __ InvokeBuiltin(Builtins::ADD, JUMP_FUNCTION);
4356 break;
4357 case Token::SUB:
4358 __ InvokeBuiltin(Builtins::SUB, JUMP_FUNCTION);
4359 break;
4360 case Token::MUL:
4361 __ InvokeBuiltin(Builtins::MUL, JUMP_FUNCTION);
4362 break;
4363 case Token::DIV:
4364 __ InvokeBuiltin(Builtins::DIV, JUMP_FUNCTION);
4365 break;
4366 case Token::MOD:
4367 __ InvokeBuiltin(Builtins::MOD, JUMP_FUNCTION);
4368 break;
4369 case Token::BIT_OR:
4370 __ InvokeBuiltin(Builtins::BIT_OR, JUMP_FUNCTION);
4371 break;
4372 case Token::BIT_AND:
4373 __ InvokeBuiltin(Builtins::BIT_AND, JUMP_FUNCTION);
4374 break;
4375 case Token::BIT_XOR:
4376 __ InvokeBuiltin(Builtins::BIT_XOR, JUMP_FUNCTION);
4377 break;
4378 case Token::SAR:
4379 __ InvokeBuiltin(Builtins::SAR, JUMP_FUNCTION);
4380 break;
4381 case Token::SHL:
4382 __ InvokeBuiltin(Builtins::SHL, JUMP_FUNCTION);
4383 break;
4384 case Token::SHR:
4385 __ InvokeBuiltin(Builtins::SHR, JUMP_FUNCTION);
4386 break;
4387 default:
4388 UNREACHABLE();
4389 }
4390}
4391
4392
4393void FloatingPointHelper::AllocateHeapNumber(MacroAssembler* masm,
4394 Label* need_gc,
4395 Register scratch1,
4396 Register scratch2) {
4397 ExternalReference allocation_top =
4398 ExternalReference::new_space_allocation_top_address();
4399 ExternalReference allocation_limit =
4400 ExternalReference::new_space_allocation_limit_address();
4401 __ mov(Operand(scratch1), Immediate(allocation_top));
4402 __ mov(eax, Operand(scratch1, 0));
4403 __ lea(scratch2, Operand(eax, HeapNumber::kSize)); // scratch2: new top
4404 __ cmp(scratch2, Operand::StaticVariable(allocation_limit));
4405 __ j(above, need_gc, not_taken);
4406
4407 __ mov(Operand(scratch1, 0), scratch2); // store new top
4408 __ mov(Operand(eax, HeapObject::kMapOffset),
4409 Immediate(Factory::heap_number_map()));
4410 // Tag old top and use as result.
4411 __ add(Operand(eax), Immediate(kHeapObjectTag));
4412}
4413
4414
4415void FloatingPointHelper::LoadFloatOperands(MacroAssembler* masm,
4416 Register scratch) {
4417 Label load_smi_1, load_smi_2, done_load_1, done;
4418 __ mov(scratch, Operand(esp, 2 * kPointerSize));
4419 __ test(scratch, Immediate(kSmiTagMask));
4420 __ j(zero, &load_smi_1, not_taken);
4421 __ fld_d(FieldOperand(scratch, HeapNumber::kValueOffset));
4422 __ bind(&done_load_1);
4423
4424 __ mov(scratch, Operand(esp, 1 * kPointerSize));
4425 __ test(scratch, Immediate(kSmiTagMask));
4426 __ j(zero, &load_smi_2, not_taken);
4427 __ fld_d(FieldOperand(scratch, HeapNumber::kValueOffset));
4428 __ jmp(&done);
4429
4430 __ bind(&load_smi_1);
4431 __ sar(scratch, kSmiTagSize);
4432 __ push(scratch);
4433 __ fild_s(Operand(esp, 0));
4434 __ pop(scratch);
4435 __ jmp(&done_load_1);
4436
4437 __ bind(&load_smi_2);
4438 __ sar(scratch, kSmiTagSize);
4439 __ push(scratch);
4440 __ fild_s(Operand(esp, 0));
4441 __ pop(scratch);
4442
4443 __ bind(&done);
4444}
4445
4446
4447void FloatingPointHelper::CheckFloatOperands(MacroAssembler* masm,
4448 Label* non_float,
4449 Register scratch) {
4450 Label test_other, done;
4451 // Test if both operands are floats or smi -> scratch=k_is_float;
4452 // Otherwise scratch = k_not_float.
4453 __ test(edx, Immediate(kSmiTagMask));
4454 __ j(zero, &test_other, not_taken); // argument in edx is OK
4455 __ mov(scratch, FieldOperand(edx, HeapObject::kMapOffset));
4456 __ cmp(scratch, Factory::heap_number_map());
4457 __ j(not_equal, non_float); // argument in edx is not a number -> NaN
4458
4459 __ bind(&test_other);
4460 __ test(eax, Immediate(kSmiTagMask));
4461 __ j(zero, &done); // argument in eax is OK
4462 __ mov(scratch, FieldOperand(eax, HeapObject::kMapOffset));
4463 __ cmp(scratch, Factory::heap_number_map());
4464 __ j(not_equal, non_float); // argument in eax is not a number -> NaN
4465
4466 // Fall-through: Both operands are numbers.
4467 __ bind(&done);
4468}
4469
4470
4471void UnarySubStub::Generate(MacroAssembler* masm) {
4472 Label undo;
4473 Label slow;
4474 Label done;
4475 Label try_float;
4476
4477 // Check whether the value is a smi.
4478 __ test(eax, Immediate(kSmiTagMask));
4479 __ j(not_zero, &try_float, not_taken);
4480
4481 // Enter runtime system if the value of the expression is zero
4482 // to make sure that we switch between 0 and -0.
4483 __ test(eax, Operand(eax));
4484 __ j(zero, &slow, not_taken);
4485
4486 // The value of the expression is a smi that is not zero. Try
4487 // optimistic subtraction '0 - value'.
4488 __ mov(edx, Operand(eax));
4489 __ Set(eax, Immediate(0));
4490 __ sub(eax, Operand(edx));
4491 __ j(overflow, &undo, not_taken);
4492
4493 // If result is a smi we are done.
4494 __ test(eax, Immediate(kSmiTagMask));
4495 __ j(zero, &done, taken);
4496
4497 // Restore eax and enter runtime system.
4498 __ bind(&undo);
4499 __ mov(eax, Operand(edx));
4500
4501 // Enter runtime system.
4502 __ bind(&slow);
4503 __ pop(ecx); // pop return address
4504 __ push(eax);
4505 __ push(ecx); // push return address
4506 __ InvokeBuiltin(Builtins::UNARY_MINUS, JUMP_FUNCTION);
4507
4508 // Try floating point case.
4509 __ bind(&try_float);
4510 __ mov(edx, FieldOperand(eax, HeapObject::kMapOffset));
4511 __ cmp(edx, Factory::heap_number_map());
4512 __ j(not_equal, &slow);
4513 __ mov(edx, Operand(eax));
4514 // edx: operand
4515 FloatingPointHelper::AllocateHeapNumber(masm, &undo, ebx, ecx);
4516 // eax: allocated 'empty' number
4517 __ fld_d(FieldOperand(edx, HeapNumber::kValueOffset));
4518 __ fchs();
4519 __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
4520
4521 __ bind(&done);
4522
4523 __ StubReturn(1);
4524}
4525
4526
ager@chromium.org7c537e22008-10-16 08:43:32 +00004527void ArgumentsAccessStub::GenerateReadLength(MacroAssembler* masm) {
4528 // Check if the calling frame is an arguments adaptor frame.
4529 Label adaptor;
4530 __ mov(edx, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
4531 __ mov(ecx, Operand(edx, StandardFrameConstants::kContextOffset));
4532 __ cmp(ecx, ArgumentsAdaptorFrame::SENTINEL);
4533 __ j(equal, &adaptor);
4534
4535 // Nothing to do: The formal number of parameters has already been
4536 // passed in register eax by calling function. Just return it.
4537 __ ret(0);
4538
4539 // Arguments adaptor case: Read the arguments length from the
4540 // adaptor frame and return it.
4541 __ bind(&adaptor);
4542 __ mov(eax, Operand(edx, ArgumentsAdaptorFrameConstants::kLengthOffset));
4543 __ ret(0);
4544}
4545
4546
4547void ArgumentsAccessStub::GenerateReadElement(MacroAssembler* masm) {
4548 // The displacement is used for skipping the frame pointer on the
4549 // stack. It is the offset of the last parameter (if any) relative
4550 // to the frame pointer.
4551 static const int kDisplacement = 1 * kPointerSize;
4552
4553 // Check that the key is a smi.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004554 Label slow;
ager@chromium.org7c537e22008-10-16 08:43:32 +00004555 __ mov(ebx, Operand(esp, 1 * kPointerSize)); // skip return address
4556 __ test(ebx, Immediate(kSmiTagMask));
4557 __ j(not_zero, &slow, not_taken);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004558
4559 // Check if the calling frame is an arguments adaptor frame.
4560 Label adaptor;
4561 __ mov(edx, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
4562 __ mov(ecx, Operand(edx, StandardFrameConstants::kContextOffset));
4563 __ cmp(ecx, ArgumentsAdaptorFrame::SENTINEL);
ager@chromium.org7c537e22008-10-16 08:43:32 +00004564 __ j(equal, &adaptor);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004565
ager@chromium.org7c537e22008-10-16 08:43:32 +00004566 // Check index against formal parameters count limit passed in
4567 // through register eax. Use unsigned comparison to get negative
4568 // check for free.
4569 __ cmp(ebx, Operand(eax));
4570 __ j(above_equal, &slow, not_taken);
4571
4572 // Read the argument from the stack and return it.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004573 ASSERT(kSmiTagSize == 1 && kSmiTag == 0); // shifting code depends on this
ager@chromium.org7c537e22008-10-16 08:43:32 +00004574 __ lea(edx, Operand(ebp, eax, times_2, 0));
4575 __ neg(ebx);
4576 __ mov(eax, Operand(edx, ebx, times_2, kDisplacement));
4577 __ ret(0);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004578
ager@chromium.org7c537e22008-10-16 08:43:32 +00004579 // Arguments adaptor case: Check index against actual arguments
4580 // limit found in the arguments adaptor frame. Use unsigned
4581 // comparison to get negative check for free.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004582 __ bind(&adaptor);
ager@chromium.org7c537e22008-10-16 08:43:32 +00004583 __ mov(ecx, Operand(edx, ArgumentsAdaptorFrameConstants::kLengthOffset));
4584 __ cmp(ebx, Operand(ecx));
4585 __ j(above_equal, &slow, not_taken);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004586
ager@chromium.org7c537e22008-10-16 08:43:32 +00004587 // Read the argument from the stack and return it.
4588 ASSERT(kSmiTagSize == 1 && kSmiTag == 0); // shifting code depends on this
4589 __ lea(edx, Operand(edx, ecx, times_2, 0));
4590 __ neg(ebx);
4591 __ mov(eax, Operand(edx, ebx, times_2, kDisplacement));
4592 __ ret(0);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004593
4594 // Slow-case: Handle non-smi or out-of-bounds access to arguments
4595 // by calling the runtime system.
ager@chromium.org7c537e22008-10-16 08:43:32 +00004596 __ bind(&slow);
4597 __ TailCallRuntime(ExternalReference(Runtime::kGetArgumentsProperty), 1);
4598}
4599
4600
4601void ArgumentsAccessStub::GenerateNewObject(MacroAssembler* masm) {
4602 // The displacement is used for skipping the return address and the
4603 // frame pointer on the stack. It is the offset of the last
4604 // parameter (if any) relative to the frame pointer.
4605 static const int kDisplacement = 2 * kPointerSize;
4606
4607 // Check if the calling frame is an arguments adaptor frame.
4608 Label runtime;
4609 __ mov(edx, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
4610 __ mov(ecx, Operand(edx, StandardFrameConstants::kContextOffset));
4611 __ cmp(ecx, ArgumentsAdaptorFrame::SENTINEL);
4612 __ j(not_equal, &runtime);
4613
4614 // Patch the arguments.length and the parameters pointer.
4615 __ mov(ecx, Operand(edx, ArgumentsAdaptorFrameConstants::kLengthOffset));
4616 __ mov(Operand(esp, 1 * kPointerSize), ecx);
4617 __ lea(edx, Operand(edx, ecx, times_2, kDisplacement));
4618 __ mov(Operand(esp, 2 * kPointerSize), edx);
4619
4620 // Do the runtime call to allocate the arguments object.
4621 __ bind(&runtime);
4622 __ TailCallRuntime(ExternalReference(Runtime::kNewArgumentsFast), 3);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004623}
4624
4625
4626void CompareStub::Generate(MacroAssembler* masm) {
4627 Label call_builtin, done;
ager@chromium.org7c537e22008-10-16 08:43:32 +00004628
4629 // If we're doing a strict equality comparison, we generate code
4630 // to do fast comparison for objects and oddballs. Numbers and
4631 // strings still go through the usual slow-case code.
4632 if (strict_) {
4633 Label slow;
4634 __ test(eax, Immediate(kSmiTagMask));
4635 __ j(zero, &slow);
4636
4637 // Get the type of the first operand.
4638 __ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset));
4639 __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
4640
4641 // If the first object is an object, we do pointer comparison.
4642 ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
4643 Label non_object;
4644 __ cmp(ecx, FIRST_JS_OBJECT_TYPE);
4645 __ j(less, &non_object);
4646 __ sub(eax, Operand(edx));
4647 __ ret(0);
4648
4649 // Check for oddballs: true, false, null, undefined.
4650 __ bind(&non_object);
4651 __ cmp(ecx, ODDBALL_TYPE);
4652 __ j(not_equal, &slow);
4653
4654 // If the oddball isn't undefined, we do pointer comparison. For
4655 // the undefined value, we have to be careful and check for
4656 // 'undetectable' objects too.
4657 Label undefined;
4658 __ cmp(Operand(eax), Immediate(Factory::undefined_value()));
4659 __ j(equal, &undefined);
4660 __ sub(eax, Operand(edx));
4661 __ ret(0);
4662
4663 // Undefined case: If the other operand isn't undefined too, we
4664 // have to check if it's 'undetectable'.
4665 Label check_undetectable;
4666 __ bind(&undefined);
4667 __ cmp(Operand(edx), Immediate(Factory::undefined_value()));
4668 __ j(not_equal, &check_undetectable);
4669 __ Set(eax, Immediate(0));
4670 __ ret(0);
4671
4672 // Check for undetectability of the other operand.
4673 Label not_strictly_equal;
4674 __ bind(&check_undetectable);
4675 __ test(edx, Immediate(kSmiTagMask));
4676 __ j(zero, &not_strictly_equal);
4677 __ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset));
4678 __ movzx_b(ecx, FieldOperand(ecx, Map::kBitFieldOffset));
4679 __ and_(ecx, 1 << Map::kIsUndetectable);
4680 __ cmp(ecx, 1 << Map::kIsUndetectable);
4681 __ j(not_equal, &not_strictly_equal);
4682 __ Set(eax, Immediate(0));
4683 __ ret(0);
4684
4685 // No cigar: Objects aren't strictly equal. Register eax contains
4686 // a non-smi value so it can't be 0. Just return.
4687 ASSERT(kHeapObjectTag != 0);
4688 __ bind(&not_strictly_equal);
4689 __ ret(0);
4690
4691 // Fall through to the general case.
4692 __ bind(&slow);
4693 }
4694
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004695 // Save the return address (and get it off the stack).
4696 __ pop(ecx);
4697
4698 // Push arguments.
4699 __ push(eax);
4700 __ push(edx);
4701 __ push(ecx);
4702
4703 // Inlined floating point compare.
4704 // Call builtin if operands are not floating point or smi.
4705 FloatingPointHelper::CheckFloatOperands(masm, &call_builtin, ebx);
4706 FloatingPointHelper::LoadFloatOperands(masm, ecx);
4707 __ FCmp();
4708
4709 // Jump to builtin for NaN.
4710 __ j(parity_even, &call_builtin, not_taken);
4711
4712 // TODO(1243847): Use cmov below once CpuFeatures are properly hooked up.
4713 Label below_lbl, above_lbl;
4714 // use edx, eax to convert unsigned to signed comparison
4715 __ j(below, &below_lbl, not_taken);
4716 __ j(above, &above_lbl, not_taken);
4717
4718 __ xor_(eax, Operand(eax)); // equal
4719 __ ret(2 * kPointerSize);
4720
4721 __ bind(&below_lbl);
4722 __ mov(eax, -1);
4723 __ ret(2 * kPointerSize);
4724
4725 __ bind(&above_lbl);
4726 __ mov(eax, 1);
4727 __ ret(2 * kPointerSize); // eax, edx were pushed
4728
4729 __ bind(&call_builtin);
4730 // must swap argument order
4731 __ pop(ecx);
4732 __ pop(edx);
4733 __ pop(eax);
4734 __ push(edx);
4735 __ push(eax);
4736
4737 // Figure out which native to call and setup the arguments.
4738 Builtins::JavaScript builtin;
4739 if (cc_ == equal) {
4740 builtin = strict_ ? Builtins::STRICT_EQUALS : Builtins::EQUALS;
4741 } else {
4742 builtin = Builtins::COMPARE;
4743 int ncr; // NaN compare result
4744 if (cc_ == less || cc_ == less_equal) {
4745 ncr = GREATER;
4746 } else {
4747 ASSERT(cc_ == greater || cc_ == greater_equal); // remaining cases
4748 ncr = LESS;
4749 }
4750 __ push(Immediate(Smi::FromInt(ncr)));
4751 }
4752
4753 // Restore return address on the stack.
4754 __ push(ecx);
4755
4756 // Call the native; it returns -1 (less), 0 (equal), or 1 (greater)
4757 // tagged as a small integer.
4758 __ InvokeBuiltin(builtin, JUMP_FUNCTION);
4759}
4760
4761
4762void StackCheckStub::Generate(MacroAssembler* masm) {
4763 // Because builtins always remove the receiver from the stack, we
4764 // have to fake one to avoid underflowing the stack. The receiver
4765 // must be inserted below the return address on the stack so we
4766 // temporarily store that in a register.
4767 __ pop(eax);
4768 __ push(Immediate(Smi::FromInt(0)));
4769 __ push(eax);
4770
4771 // Do tail-call to runtime routine.
4772 __ TailCallRuntime(ExternalReference(Runtime::kStackGuard), 1);
4773}
4774
4775
4776void CallFunctionStub::Generate(MacroAssembler* masm) {
4777 Label slow;
4778
4779 // Get the function to call from the stack.
4780 // +2 ~ receiver, return address
4781 __ mov(edi, Operand(esp, (argc_ + 2) * kPointerSize));
4782
4783 // Check that the function really is a JavaScript function.
4784 __ test(edi, Immediate(kSmiTagMask));
4785 __ j(zero, &slow, not_taken);
4786 // Get the map.
4787 __ mov(ecx, FieldOperand(edi, HeapObject::kMapOffset));
4788 __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
4789 __ cmp(ecx, JS_FUNCTION_TYPE);
4790 __ j(not_equal, &slow, not_taken);
4791
4792 // Fast-case: Just invoke the function.
4793 ParameterCount actual(argc_);
4794 __ InvokeFunction(edi, actual, JUMP_FUNCTION);
4795
4796 // Slow-case: Non-function called.
4797 __ bind(&slow);
4798 __ Set(eax, Immediate(argc_));
4799 __ Set(ebx, Immediate(0));
4800 __ GetBuiltinEntry(edx, Builtins::CALL_NON_FUNCTION);
4801 Handle<Code> adaptor(Builtins::builtin(Builtins::ArgumentsAdaptorTrampoline));
4802 __ jmp(adaptor, RelocInfo::CODE_TARGET);
4803}
4804
4805
4806void RevertToNumberStub::Generate(MacroAssembler* masm) {
4807 // Revert optimistic increment/decrement.
4808 if (is_increment_) {
4809 __ sub(Operand(eax), Immediate(Smi::FromInt(1)));
4810 } else {
4811 __ add(Operand(eax), Immediate(Smi::FromInt(1)));
4812 }
4813
4814 __ pop(ecx);
4815 __ push(eax);
4816 __ push(ecx);
4817 __ InvokeBuiltin(Builtins::TO_NUMBER, JUMP_FUNCTION);
4818 // Code never returns due to JUMP_FUNCTION.
4819}
4820
4821
4822void CounterOpStub::Generate(MacroAssembler* masm) {
4823 // Store to the result on the stack (skip return address) before
4824 // performing the count operation.
4825 if (is_postfix_) {
4826 __ mov(Operand(esp, result_offset_ + kPointerSize), eax);
4827 }
4828
4829 // Revert optimistic increment/decrement but only for prefix
4830 // counts. For postfix counts it has already been reverted before
4831 // the conversion to numbers.
4832 if (!is_postfix_) {
4833 if (is_increment_) {
4834 __ sub(Operand(eax), Immediate(Smi::FromInt(1)));
4835 } else {
4836 __ add(Operand(eax), Immediate(Smi::FromInt(1)));
4837 }
4838 }
4839
4840 // Compute the new value by calling the right JavaScript native.
4841 __ pop(ecx);
4842 __ push(eax);
4843 __ push(ecx);
4844 Builtins::JavaScript builtin = is_increment_ ? Builtins::INC : Builtins::DEC;
4845 __ InvokeBuiltin(builtin, JUMP_FUNCTION);
4846 // Code never returns due to JUMP_FUNCTION.
4847}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004848
4849
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004850void CEntryStub::GenerateThrowTOS(MacroAssembler* masm) {
4851 ASSERT(StackHandlerConstants::kSize == 6 * kPointerSize); // adjust this code
4852 ExternalReference handler_address(Top::k_handler_address);
4853 __ mov(edx, Operand::StaticVariable(handler_address));
4854 __ mov(ecx, Operand(edx, -1 * kPointerSize)); // get next in chain
4855 __ mov(Operand::StaticVariable(handler_address), ecx);
4856 __ mov(esp, Operand(edx));
4857 __ pop(edi);
4858 __ pop(ebp);
4859 __ pop(edx); // remove code pointer
4860 __ pop(edx); // remove state
4861
4862 // Before returning we restore the context from the frame pointer if not NULL.
4863 // The frame pointer is NULL in the exception handler of a JS entry frame.
4864 __ xor_(esi, Operand(esi)); // tentatively set context pointer to NULL
4865 Label skip;
4866 __ cmp(ebp, 0);
4867 __ j(equal, &skip, not_taken);
4868 __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
4869 __ bind(&skip);
4870
4871 __ ret(0);
4872}
4873
4874
4875void CEntryStub::GenerateCore(MacroAssembler* masm,
4876 Label* throw_normal_exception,
4877 Label* throw_out_of_memory_exception,
ager@chromium.org236ad962008-09-25 09:45:57 +00004878 StackFrame::Type frame_type,
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00004879 bool do_gc,
4880 bool always_allocate_scope) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004881 // eax: result parameter for PerformGC, if any
4882 // ebx: pointer to C function (C callee-saved)
4883 // ebp: frame pointer (restored after C call)
4884 // esp: stack pointer (restored after C call)
mads.s.ager31e71382008-08-13 09:32:07 +00004885 // edi: number of arguments including receiver (C callee-saved)
ager@chromium.org236ad962008-09-25 09:45:57 +00004886 // esi: pointer to the first argument (C callee-saved)
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004887
4888 if (do_gc) {
4889 __ mov(Operand(esp, 0 * kPointerSize), eax); // Result.
ager@chromium.org236ad962008-09-25 09:45:57 +00004890 __ call(FUNCTION_ADDR(Runtime::PerformGC), RelocInfo::RUNTIME_ENTRY);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004891 }
4892
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00004893 ExternalReference scope_depth =
4894 ExternalReference::heap_always_allocate_scope_depth();
4895 if (always_allocate_scope) {
4896 __ inc(Operand::StaticVariable(scope_depth));
4897 }
4898
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004899 // Call C function.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004900 __ mov(Operand(esp, 0 * kPointerSize), edi); // argc.
ager@chromium.org236ad962008-09-25 09:45:57 +00004901 __ mov(Operand(esp, 1 * kPointerSize), esi); // argv.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004902 __ call(Operand(ebx));
4903 // Result is in eax or edx:eax - do not destroy these registers!
4904
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00004905 if (always_allocate_scope) {
4906 __ dec(Operand::StaticVariable(scope_depth));
4907 }
4908
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004909 // Check for failure result.
4910 Label failure_returned;
4911 ASSERT(((kFailureTag + 1) & kFailureTagMask) == 0);
4912 __ lea(ecx, Operand(eax, 1));
4913 // Lower 2 bits of ecx are 0 iff eax has failure tag.
4914 __ test(ecx, Immediate(kFailureTagMask));
4915 __ j(zero, &failure_returned, not_taken);
4916
ager@chromium.org236ad962008-09-25 09:45:57 +00004917 // Exit the JavaScript to C++ exit frame.
4918 __ LeaveExitFrame(frame_type);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004919 __ ret(0);
4920
ager@chromium.org236ad962008-09-25 09:45:57 +00004921 // Handling of failure.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004922 __ bind(&failure_returned);
4923
4924 Label retry;
4925 // If the returned exception is RETRY_AFTER_GC continue at retry label
4926 ASSERT(Failure::RETRY_AFTER_GC == 0);
4927 __ test(eax, Immediate(((1 << kFailureTypeTagSize) - 1) << kFailureTagSize));
4928 __ j(zero, &retry, taken);
4929
4930 Label continue_exception;
4931 // If the returned failure is EXCEPTION then promote Top::pending_exception().
4932 __ cmp(eax, reinterpret_cast<int32_t>(Failure::Exception()));
4933 __ j(not_equal, &continue_exception);
4934
4935 // Retrieve the pending exception and clear the variable.
4936 ExternalReference pending_exception_address(Top::k_pending_exception_address);
4937 __ mov(eax, Operand::StaticVariable(pending_exception_address));
4938 __ mov(edx,
4939 Operand::StaticVariable(ExternalReference::the_hole_value_location()));
4940 __ mov(Operand::StaticVariable(pending_exception_address), edx);
4941
4942 __ bind(&continue_exception);
4943 // Special handling of out of memory exception.
4944 __ cmp(eax, reinterpret_cast<int32_t>(Failure::OutOfMemoryException()));
4945 __ j(equal, throw_out_of_memory_exception);
4946
4947 // Handle normal exception.
4948 __ jmp(throw_normal_exception);
4949
4950 // Retry.
4951 __ bind(&retry);
4952}
4953
4954
4955void CEntryStub::GenerateThrowOutOfMemory(MacroAssembler* masm) {
4956 // Fetch top stack handler.
4957 ExternalReference handler_address(Top::k_handler_address);
4958 __ mov(edx, Operand::StaticVariable(handler_address));
4959
4960 // Unwind the handlers until the ENTRY handler is found.
4961 Label loop, done;
4962 __ bind(&loop);
4963 // Load the type of the current stack handler.
4964 const int kStateOffset = StackHandlerConstants::kAddressDisplacement +
4965 StackHandlerConstants::kStateOffset;
4966 __ cmp(Operand(edx, kStateOffset), Immediate(StackHandler::ENTRY));
4967 __ j(equal, &done);
4968 // Fetch the next handler in the list.
4969 const int kNextOffset = StackHandlerConstants::kAddressDisplacement +
4970 StackHandlerConstants::kNextOffset;
4971 __ mov(edx, Operand(edx, kNextOffset));
4972 __ jmp(&loop);
4973 __ bind(&done);
4974
4975 // Set the top handler address to next handler past the current ENTRY handler.
4976 __ mov(eax, Operand(edx, kNextOffset));
4977 __ mov(Operand::StaticVariable(handler_address), eax);
4978
4979 // Set external caught exception to false.
4980 __ mov(eax, false);
4981 ExternalReference external_caught(Top::k_external_caught_exception_address);
4982 __ mov(Operand::StaticVariable(external_caught), eax);
4983
4984 // Set pending exception and eax to out of memory exception.
4985 __ mov(eax, reinterpret_cast<int32_t>(Failure::OutOfMemoryException()));
4986 ExternalReference pending_exception(Top::k_pending_exception_address);
4987 __ mov(Operand::StaticVariable(pending_exception), eax);
4988
4989 // Restore the stack to the address of the ENTRY handler
4990 __ mov(esp, Operand(edx));
4991
4992 // Clear the context pointer;
4993 __ xor_(esi, Operand(esi));
4994
4995 // Restore registers from handler.
4996 __ pop(edi); // PP
4997 __ pop(ebp); // FP
4998 __ pop(edx); // Code
4999 __ pop(edx); // State
5000
5001 __ ret(0);
5002}
5003
5004
5005void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) {
mads.s.ager31e71382008-08-13 09:32:07 +00005006 // eax: number of arguments including receiver
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005007 // ebx: pointer to C function (C callee-saved)
5008 // ebp: frame pointer (restored after C call)
5009 // esp: stack pointer (restored after C call)
5010 // esi: current context (C callee-saved)
5011 // edi: caller's parameter pointer pp (C callee-saved)
5012
5013 // NOTE: Invocations of builtins may return failure objects
5014 // instead of a proper result. The builtin entry handles
5015 // this by performing a garbage collection and retrying the
5016 // builtin once.
5017
ager@chromium.org236ad962008-09-25 09:45:57 +00005018 StackFrame::Type frame_type = is_debug_break ?
5019 StackFrame::EXIT_DEBUG :
5020 StackFrame::EXIT;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005021
ager@chromium.org236ad962008-09-25 09:45:57 +00005022 // Enter the exit frame that transitions from JavaScript to C++.
5023 __ EnterExitFrame(frame_type);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005024
5025 // eax: result parameter for PerformGC, if any (setup below)
5026 // ebx: pointer to builtin function (C callee-saved)
5027 // ebp: frame pointer (restored after C call)
5028 // esp: stack pointer (restored after C call)
mads.s.ager31e71382008-08-13 09:32:07 +00005029 // edi: number of arguments including receiver (C callee-saved)
ager@chromium.org236ad962008-09-25 09:45:57 +00005030 // esi: argv pointer (C callee-saved)
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005031
5032 Label throw_out_of_memory_exception;
5033 Label throw_normal_exception;
5034
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005035 // Call into the runtime system. Collect garbage before the call if
5036 // running with --gc-greedy set.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005037 if (FLAG_gc_greedy) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005038 Failure* failure = Failure::RetryAfterGC(0);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005039 __ mov(eax, Immediate(reinterpret_cast<int32_t>(failure)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005040 }
5041 GenerateCore(masm, &throw_normal_exception,
5042 &throw_out_of_memory_exception,
ager@chromium.org236ad962008-09-25 09:45:57 +00005043 frame_type,
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005044 FLAG_gc_greedy,
ager@chromium.org236ad962008-09-25 09:45:57 +00005045 false);
ager@chromium.org236ad962008-09-25 09:45:57 +00005046
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005047 // Do space-specific GC and retry runtime call.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005048 GenerateCore(masm,
5049 &throw_normal_exception,
5050 &throw_out_of_memory_exception,
ager@chromium.org236ad962008-09-25 09:45:57 +00005051 frame_type,
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005052 true,
5053 false);
5054
5055 // Do full GC and retry runtime call one final time.
5056 Failure* failure = Failure::InternalError();
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005057 __ mov(eax, Immediate(reinterpret_cast<int32_t>(failure)));
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005058 GenerateCore(masm,
5059 &throw_normal_exception,
5060 &throw_out_of_memory_exception,
5061 frame_type,
5062 true,
ager@chromium.org236ad962008-09-25 09:45:57 +00005063 true);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005064
5065 __ bind(&throw_out_of_memory_exception);
5066 GenerateThrowOutOfMemory(masm);
5067 // control flow for generated will not return.
5068
5069 __ bind(&throw_normal_exception);
5070 GenerateThrowTOS(masm);
5071}
5072
5073
5074void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) {
5075 Label invoke, exit;
5076
5077 // Setup frame.
5078 __ push(ebp);
5079 __ mov(ebp, Operand(esp));
5080
5081 // Save callee-saved registers (C calling conventions).
5082 int marker = is_construct ? StackFrame::ENTRY_CONSTRUCT : StackFrame::ENTRY;
5083 // Push something that is not an arguments adaptor.
5084 __ push(Immediate(~ArgumentsAdaptorFrame::SENTINEL));
5085 __ push(Immediate(Smi::FromInt(marker))); // @ function offset
5086 __ push(edi);
5087 __ push(esi);
5088 __ push(ebx);
5089
5090 // Save copies of the top frame descriptor on the stack.
5091 ExternalReference c_entry_fp(Top::k_c_entry_fp_address);
5092 __ push(Operand::StaticVariable(c_entry_fp));
5093
5094 // Call a faked try-block that does the invoke.
5095 __ call(&invoke);
5096
5097 // Caught exception: Store result (exception) in the pending
5098 // exception field in the JSEnv and return a failure sentinel.
5099 ExternalReference pending_exception(Top::k_pending_exception_address);
5100 __ mov(Operand::StaticVariable(pending_exception), eax);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005101 __ mov(eax, reinterpret_cast<int32_t>(Failure::Exception()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005102 __ jmp(&exit);
5103
5104 // Invoke: Link this frame into the handler chain.
5105 __ bind(&invoke);
5106 __ PushTryHandler(IN_JS_ENTRY, JS_ENTRY_HANDLER);
5107 __ push(eax); // flush TOS
5108
5109 // Clear any pending exceptions.
5110 __ mov(edx,
5111 Operand::StaticVariable(ExternalReference::the_hole_value_location()));
5112 __ mov(Operand::StaticVariable(pending_exception), edx);
5113
5114 // Fake a receiver (NULL).
5115 __ push(Immediate(0)); // receiver
5116
5117 // Invoke the function by calling through JS entry trampoline
5118 // builtin and pop the faked function when we return. Notice that we
5119 // cannot store a reference to the trampoline code directly in this
5120 // stub, because the builtin stubs may not have been generated yet.
5121 if (is_construct) {
5122 ExternalReference construct_entry(Builtins::JSConstructEntryTrampoline);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005123 __ mov(edx, Immediate(construct_entry));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005124 } else {
5125 ExternalReference entry(Builtins::JSEntryTrampoline);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005126 __ mov(edx, Immediate(entry));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005127 }
5128 __ mov(edx, Operand(edx, 0)); // deref address
5129 __ lea(edx, FieldOperand(edx, Code::kHeaderSize));
5130 __ call(Operand(edx));
5131
5132 // Unlink this frame from the handler chain.
5133 __ pop(Operand::StaticVariable(ExternalReference(Top::k_handler_address)));
5134 // Pop next_sp.
5135 __ add(Operand(esp), Immediate(StackHandlerConstants::kSize - kPointerSize));
5136
5137 // Restore the top frame descriptor from the stack.
5138 __ bind(&exit);
5139 __ pop(Operand::StaticVariable(ExternalReference(Top::k_c_entry_fp_address)));
5140
5141 // Restore callee-saved registers (C calling conventions).
5142 __ pop(ebx);
5143 __ pop(esi);
5144 __ pop(edi);
5145 __ add(Operand(esp), Immediate(2 * kPointerSize)); // remove markers
5146
5147 // Restore frame pointer and return.
5148 __ pop(ebp);
5149 __ ret(0);
5150}
5151
5152
ager@chromium.org7c537e22008-10-16 08:43:32 +00005153void InstanceofStub::Generate(MacroAssembler* masm) {
5154 // Get the object - go slow case if it's a smi.
5155 Label slow;
5156 __ mov(eax, Operand(esp, 2 * kPointerSize)); // 2 ~ return address, function
5157 __ test(eax, Immediate(kSmiTagMask));
5158 __ j(zero, &slow, not_taken);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005159
ager@chromium.org7c537e22008-10-16 08:43:32 +00005160 // Check that the left hand is a JS object.
5161 __ mov(eax, FieldOperand(eax, HeapObject::kMapOffset)); // ebx - object map
5162 __ movzx_b(ecx, FieldOperand(eax, Map::kInstanceTypeOffset)); // ecx - type
5163 __ cmp(ecx, FIRST_JS_OBJECT_TYPE);
5164 __ j(less, &slow, not_taken);
5165 __ cmp(ecx, LAST_JS_OBJECT_TYPE);
5166 __ j(greater, &slow, not_taken);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005167
ager@chromium.org7c537e22008-10-16 08:43:32 +00005168 // Get the prototype of the function.
5169 __ mov(edx, Operand(esp, 1 * kPointerSize)); // 1 ~ return address
5170 __ TryGetFunctionPrototype(edx, ebx, ecx, &slow);
5171
5172 // Check that the function prototype is a JS object.
5173 __ mov(ecx, FieldOperand(ebx, HeapObject::kMapOffset));
5174 __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
5175 __ cmp(ecx, FIRST_JS_OBJECT_TYPE);
5176 __ j(less, &slow, not_taken);
5177 __ cmp(ecx, LAST_JS_OBJECT_TYPE);
5178 __ j(greater, &slow, not_taken);
5179
5180 // Register mapping: eax is object map and ebx is function prototype.
5181 __ mov(ecx, FieldOperand(eax, Map::kPrototypeOffset));
5182
5183 // Loop through the prototype chain looking for the function prototype.
5184 Label loop, is_instance, is_not_instance;
5185 __ bind(&loop);
5186 __ cmp(ecx, Operand(ebx));
5187 __ j(equal, &is_instance);
5188 __ cmp(Operand(ecx), Immediate(Factory::null_value()));
5189 __ j(equal, &is_not_instance);
5190 __ mov(ecx, FieldOperand(ecx, HeapObject::kMapOffset));
5191 __ mov(ecx, FieldOperand(ecx, Map::kPrototypeOffset));
5192 __ jmp(&loop);
5193
5194 __ bind(&is_instance);
5195 __ Set(eax, Immediate(0));
5196 __ ret(2 * kPointerSize);
5197
5198 __ bind(&is_not_instance);
5199 __ Set(eax, Immediate(Smi::FromInt(1)));
5200 __ ret(2 * kPointerSize);
5201
5202 // Slow-case: Go through the JavaScript implementation.
5203 __ bind(&slow);
5204 __ InvokeBuiltin(Builtins::INSTANCE_OF, JUMP_FUNCTION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005205}
5206
5207
ager@chromium.org7c537e22008-10-16 08:43:32 +00005208#undef __
5209
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005210} } // namespace v8::internal