blob: 8ec1c9505a1942d3930bbe1422b713be341c8dec [file] [log] [blame]
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001// Copyright 2010 the V8 project authors. All rights reserved.
ager@chromium.org5ec48922009-05-05 07:25:34 +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
kasperl@chromium.org71affb52009-05-26 05:44:31 +000028#include "v8.h"
ager@chromium.org5aa501c2009-06-23 07:57:28 +000029
30#include "bootstrapper.h"
31#include "codegen-inl.h"
ager@chromium.orgc4c92722009-11-18 14:12:51 +000032#include "compiler.h"
ager@chromium.org5aa501c2009-06-23 07:57:28 +000033#include "debug.h"
34#include "ic-inl.h"
35#include "parser.h"
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000036#include "regexp-macro-assembler.h"
kasperl@chromium.org71affb52009-05-26 05:44:31 +000037#include "register-allocator-inl.h"
ager@chromium.org5aa501c2009-06-23 07:57:28 +000038#include "scopes.h"
sgjesse@chromium.org833cdd72010-02-26 10:06:16 +000039#include "virtual-frame-inl.h"
ager@chromium.org5aa501c2009-06-23 07:57:28 +000040
kasperl@chromium.org71affb52009-05-26 05:44:31 +000041namespace v8 {
42namespace internal {
43
ager@chromium.org5aa501c2009-06-23 07:57:28 +000044#define __ ACCESS_MASM(masm_)
45
ager@chromium.orge2902be2009-06-08 12:21:35 +000046// -------------------------------------------------------------------------
47// Platform-specific DeferredCode functions.
48
ager@chromium.org5aa501c2009-06-23 07:57:28 +000049void DeferredCode::SaveRegisters() {
50 for (int i = 0; i < RegisterAllocator::kNumRegisters; i++) {
51 int action = registers_[i];
52 if (action == kPush) {
53 __ push(RegisterAllocator::ToRegister(i));
54 } else if (action != kIgnore && (action & kSyncedFlag) == 0) {
55 __ movq(Operand(rbp, action), RegisterAllocator::ToRegister(i));
56 }
57 }
58}
ager@chromium.orge2902be2009-06-08 12:21:35 +000059
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +000060
ager@chromium.org5aa501c2009-06-23 07:57:28 +000061void DeferredCode::RestoreRegisters() {
62 // Restore registers in reverse order due to the stack.
63 for (int i = RegisterAllocator::kNumRegisters - 1; i >= 0; i--) {
64 int action = registers_[i];
65 if (action == kPush) {
66 __ pop(RegisterAllocator::ToRegister(i));
67 } else if (action != kIgnore) {
68 action &= ~kSyncedFlag;
69 __ movq(RegisterAllocator::ToRegister(i), Operand(rbp, action));
70 }
71 }
72}
73
ager@chromium.orge2902be2009-06-08 12:21:35 +000074
ager@chromium.orgeadaf222009-06-16 09:43:10 +000075// -------------------------------------------------------------------------
76// CodeGenState implementation.
77
78CodeGenState::CodeGenState(CodeGenerator* owner)
79 : owner_(owner),
ager@chromium.orgeadaf222009-06-16 09:43:10 +000080 destination_(NULL),
81 previous_(NULL) {
82 owner_->set_state(this);
83}
84
85
86CodeGenState::CodeGenState(CodeGenerator* owner,
ager@chromium.orgeadaf222009-06-16 09:43:10 +000087 ControlDestination* destination)
88 : owner_(owner),
ager@chromium.orgeadaf222009-06-16 09:43:10 +000089 destination_(destination),
90 previous_(owner->state()) {
91 owner_->set_state(this);
92}
93
94
95CodeGenState::~CodeGenState() {
96 ASSERT(owner_->state() == this);
97 owner_->set_state(previous_);
98}
99
100
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +0000101// -------------------------------------------------------------------------
102// Deferred code objects
103//
104// These subclasses of DeferredCode add pieces of code to the end of generated
105// code. They are branched to from the generated code, and
106// keep some slower code out of the main body of the generated code.
107// Many of them call a code stub or a runtime function.
108
109class DeferredInlineSmiAdd: public DeferredCode {
110 public:
111 DeferredInlineSmiAdd(Register dst,
112 Smi* value,
113 OverwriteMode overwrite_mode)
114 : dst_(dst), value_(value), overwrite_mode_(overwrite_mode) {
115 set_comment("[ DeferredInlineSmiAdd");
116 }
117
118 virtual void Generate();
119
120 private:
121 Register dst_;
122 Smi* value_;
123 OverwriteMode overwrite_mode_;
124};
125
126
127// The result of value + src is in dst. It either overflowed or was not
128// smi tagged. Undo the speculative addition and call the appropriate
129// specialized stub for add. The result is left in dst.
130class DeferredInlineSmiAddReversed: public DeferredCode {
131 public:
132 DeferredInlineSmiAddReversed(Register dst,
133 Smi* value,
134 OverwriteMode overwrite_mode)
135 : dst_(dst), value_(value), overwrite_mode_(overwrite_mode) {
136 set_comment("[ DeferredInlineSmiAddReversed");
137 }
138
139 virtual void Generate();
140
141 private:
142 Register dst_;
143 Smi* value_;
144 OverwriteMode overwrite_mode_;
145};
146
147
148class DeferredInlineSmiSub: public DeferredCode {
149 public:
150 DeferredInlineSmiSub(Register dst,
151 Smi* value,
152 OverwriteMode overwrite_mode)
153 : dst_(dst), value_(value), overwrite_mode_(overwrite_mode) {
154 set_comment("[ DeferredInlineSmiSub");
155 }
156
157 virtual void Generate();
158
159 private:
160 Register dst_;
161 Smi* value_;
162 OverwriteMode overwrite_mode_;
163};
164
165
166// Call the appropriate binary operation stub to compute src op value
167// and leave the result in dst.
168class DeferredInlineSmiOperation: public DeferredCode {
169 public:
170 DeferredInlineSmiOperation(Token::Value op,
171 Register dst,
172 Register src,
173 Smi* value,
174 OverwriteMode overwrite_mode)
175 : op_(op),
176 dst_(dst),
177 src_(src),
178 value_(value),
179 overwrite_mode_(overwrite_mode) {
180 set_comment("[ DeferredInlineSmiOperation");
181 }
182
183 virtual void Generate();
184
185 private:
186 Token::Value op_;
187 Register dst_;
188 Register src_;
189 Smi* value_;
190 OverwriteMode overwrite_mode_;
191};
192
193
194class FloatingPointHelper : public AllStatic {
195 public:
196 // Code pattern for loading a floating point value. Input value must
197 // be either a smi or a heap number object (fp value). Requirements:
198 // operand on TOS+1. Returns operand as floating point number on FPU
199 // stack.
200 static void LoadFloatOperand(MacroAssembler* masm, Register scratch);
201
202 // Code pattern for loading a floating point value. Input value must
203 // be either a smi or a heap number object (fp value). Requirements:
204 // operand in src register. Returns operand as floating point number
205 // in XMM register
206 static void LoadFloatOperand(MacroAssembler* masm,
207 Register src,
208 XMMRegister dst);
209
210 // Code pattern for loading floating point values. Input values must
211 // be either smi or heap number objects (fp values). Requirements:
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000212 // operand_1 in rdx, operand_2 in rax; Returns operands as
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +0000213 // floating point numbers in XMM registers.
214 static void LoadFloatOperands(MacroAssembler* masm,
215 XMMRegister dst1,
216 XMMRegister dst2);
217
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000218 // Similar to LoadFloatOperands, assumes that the operands are smis.
219 static void LoadFloatOperandsFromSmis(MacroAssembler* masm,
220 XMMRegister dst1,
221 XMMRegister dst2);
222
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +0000223 // Code pattern for loading floating point values onto the fp stack.
224 // Input values must be either smi or heap number objects (fp values).
225 // Requirements:
226 // Register version: operands in registers lhs and rhs.
227 // Stack version: operands on TOS+1 and TOS+2.
228 // Returns operands as floating point numbers on fp stack.
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +0000229 static void LoadFloatOperands(MacroAssembler* masm,
230 Register lhs,
231 Register rhs);
232
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +0000233 // Test if operands are smi or number objects (fp). Requirements:
234 // operand_1 in rax, operand_2 in rdx; falls through on float or smi
235 // operands, jumps to the non_float label otherwise.
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +0000236 static void CheckNumberOperands(MacroAssembler* masm,
237 Label* non_float);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000238
239 // Takes the operands in rdx and rax and loads them as integers in rax
240 // and rcx.
241 static void LoadAsIntegers(MacroAssembler* masm,
242 bool use_sse3,
243 Label* operand_conversion_failure);
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +0000244};
245
246
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000247// -----------------------------------------------------------------------------
248// CodeGenerator implementation.
ager@chromium.orge2902be2009-06-08 12:21:35 +0000249
ager@chromium.org5c838252010-02-19 08:53:10 +0000250CodeGenerator::CodeGenerator(MacroAssembler* masm)
251 : deferred_(8),
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000252 masm_(masm),
ager@chromium.org5c838252010-02-19 08:53:10 +0000253 info_(NULL),
kasperl@chromium.org71affb52009-05-26 05:44:31 +0000254 frame_(NULL),
255 allocator_(NULL),
256 state_(NULL),
257 loop_nesting_(0),
258 function_return_is_shadowed_(false),
259 in_spilled_code_(false) {
260}
261
ager@chromium.orge2902be2009-06-08 12:21:35 +0000262
ager@chromium.org3e875802009-06-29 08:26:34 +0000263void CodeGenerator::DeclareGlobals(Handle<FixedArray> pairs) {
264 // Call the runtime to declare the globals. The inevitable call
265 // will sync frame elements to memory anyway, so we do it eagerly to
266 // allow us to push the arguments directly into place.
267 frame_->SyncRange(0, frame_->element_count() - 1);
268
269 __ movq(kScratchRegister, pairs, RelocInfo::EMBEDDED_OBJECT);
ager@chromium.org3811b432009-10-28 14:53:37 +0000270 frame_->EmitPush(rsi); // The context is the first argument.
ager@chromium.org3e875802009-06-29 08:26:34 +0000271 frame_->EmitPush(kScratchRegister);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +0000272 frame_->EmitPush(Smi::FromInt(is_eval() ? 1 : 0));
ager@chromium.org3e875802009-06-29 08:26:34 +0000273 Result ignored = frame_->CallRuntime(Runtime::kDeclareGlobals, 3);
274 // Return value is ignored.
kasperl@chromium.org71affb52009-05-26 05:44:31 +0000275}
276
ager@chromium.org3e875802009-06-29 08:26:34 +0000277
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000278void CodeGenerator::Generate(CompilationInfo* info) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000279 // Record the position for debugging purposes.
ager@chromium.org5c838252010-02-19 08:53:10 +0000280 CodeForFunctionPosition(info->function());
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000281 Comment cmnt(masm_, "[ function compiled by virtual frame code generator");
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000282
283 // Initialize state.
ager@chromium.org5c838252010-02-19 08:53:10 +0000284 info_ = info;
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000285 ASSERT(allocator_ == NULL);
286 RegisterAllocator register_allocator(this);
287 allocator_ = &register_allocator;
288 ASSERT(frame_ == NULL);
289 frame_ = new VirtualFrame();
290 set_in_spilled_code(false);
291
292 // Adjust for function-level loop nesting.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000293 loop_nesting_ += info->loop_nesting();
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000294
295 JumpTarget::set_compiling_deferred_code(false);
296
297#ifdef DEBUG
298 if (strlen(FLAG_stop_at) > 0 &&
ager@chromium.org5c838252010-02-19 08:53:10 +0000299 info->function()->name()->IsEqualTo(CStrVector(FLAG_stop_at))) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000300 frame_->SpillAll();
301 __ int3();
302 }
303#endif
304
305 // New scope to get automatic timing calculation.
306 { // NOLINT
307 HistogramTimerScope codegen_timer(&Counters::code_generation);
308 CodeGenState state(this);
309
310 // Entry:
311 // Stack: receiver, arguments, return address.
ager@chromium.org3e875802009-06-29 08:26:34 +0000312 // rbp: caller's frame pointer
313 // rsp: stack pointer
314 // rdi: called JS function
315 // rsi: callee's context
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000316 allocator_->Initialize();
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000317
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000318 if (info->mode() == CompilationInfo::PRIMARY) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000319 frame_->Enter();
320
321 // Allocate space for locals and initialize them.
322 frame_->AllocateStackSlots();
323
324 // Allocate the local context if needed.
ager@chromium.org5c838252010-02-19 08:53:10 +0000325 int heap_slots = scope()->num_heap_slots();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000326 if (heap_slots > 0) {
327 Comment cmnt(masm_, "[ allocate local context");
328 // Allocate local context.
329 // Get outer context and create a new context based on it.
330 frame_->PushFunction();
331 Result context;
332 if (heap_slots <= FastNewContextStub::kMaximumSlots) {
333 FastNewContextStub stub(heap_slots);
334 context = frame_->CallStub(&stub, 1);
335 } else {
336 context = frame_->CallRuntime(Runtime::kNewContext, 1);
337 }
338
339 // Update context local.
340 frame_->SaveContextRegister();
341
342 // Verify that the runtime call result and rsi agree.
343 if (FLAG_debug_code) {
344 __ cmpq(context.reg(), rsi);
345 __ Assert(equal, "Runtime::NewContext should end up in rsi");
346 }
347 }
348
349 // TODO(1241774): Improve this code:
350 // 1) only needed if we have a context
351 // 2) no need to recompute context ptr every single time
352 // 3) don't copy parameter operand code from SlotOperand!
353 {
354 Comment cmnt2(masm_, "[ copy context parameters into .context");
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000355 // Note that iteration order is relevant here! If we have the same
356 // parameter twice (e.g., function (x, y, x)), and that parameter
357 // needs to be copied into the context, it must be the last argument
358 // passed to the parameter that needs to be copied. This is a rare
359 // case so we don't check for it, instead we rely on the copying
360 // order: such a parameter is copied repeatedly into the same
361 // context location and thus the last value is what is seen inside
362 // the function.
ager@chromium.org5c838252010-02-19 08:53:10 +0000363 for (int i = 0; i < scope()->num_parameters(); i++) {
364 Variable* par = scope()->parameter(i);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000365 Slot* slot = par->slot();
366 if (slot != NULL && slot->type() == Slot::CONTEXT) {
367 // The use of SlotOperand below is safe in unspilled code
368 // because the slot is guaranteed to be a context slot.
369 //
370 // There are no parameters in the global scope.
ager@chromium.org5c838252010-02-19 08:53:10 +0000371 ASSERT(!scope()->is_global_scope());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000372 frame_->PushParameterAt(i);
373 Result value = frame_->Pop();
374 value.ToRegister();
375
376 // SlotOperand loads context.reg() with the context object
377 // stored to, used below in RecordWrite.
378 Result context = allocator_->Allocate();
379 ASSERT(context.is_valid());
380 __ movq(SlotOperand(slot, context.reg()), value.reg());
381 int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize;
382 Result scratch = allocator_->Allocate();
383 ASSERT(scratch.is_valid());
384 frame_->Spill(context.reg());
385 frame_->Spill(value.reg());
386 __ RecordWrite(context.reg(), offset, value.reg(), scratch.reg());
387 }
388 }
389 }
390
391 // Store the arguments object. This must happen after context
392 // initialization because the arguments object may be stored in
393 // the context.
394 if (ArgumentsMode() != NO_ARGUMENTS_ALLOCATION) {
395 StoreArgumentsObject(true);
396 }
397
398 // Initialize ThisFunction reference if present.
ager@chromium.org5c838252010-02-19 08:53:10 +0000399 if (scope()->is_function_scope() && scope()->function() != NULL) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000400 frame_->Push(Factory::the_hole_value());
ager@chromium.org5c838252010-02-19 08:53:10 +0000401 StoreToSlot(scope()->function()->slot(), NOT_CONST_INIT);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000402 }
403 } else {
404 // When used as the secondary compiler for splitting, rbp, rsi,
405 // and rdi have been pushed on the stack. Adjust the virtual
406 // frame to match this state.
407 frame_->Adjust(3);
408 allocator_->Unuse(rdi);
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000409
410 // Bind all the bailout labels to the beginning of the function.
411 List<CompilationInfo::Bailout*>* bailouts = info->bailouts();
412 for (int i = 0; i < bailouts->length(); i++) {
413 __ bind(bailouts->at(i)->label());
414 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000415 }
416
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000417 // Initialize the function return target after the locals are set
418 // up, because it needs the expected frame height from the frame.
419 function_return_.set_direction(JumpTarget::BIDIRECTIONAL);
420 function_return_is_shadowed_ = false;
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000421
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000422 // Generate code to 'execute' declarations and initialize functions
423 // (source elements). In case of an illegal redeclaration we need to
424 // handle that instead of processing the declarations.
ager@chromium.org5c838252010-02-19 08:53:10 +0000425 if (scope()->HasIllegalRedeclaration()) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000426 Comment cmnt(masm_, "[ illegal redeclarations");
ager@chromium.org5c838252010-02-19 08:53:10 +0000427 scope()->VisitIllegalRedeclaration(this);
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000428 } else {
429 Comment cmnt(masm_, "[ declarations");
ager@chromium.org5c838252010-02-19 08:53:10 +0000430 ProcessDeclarations(scope()->declarations());
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000431 // Bail out if a stack-overflow exception occurred when processing
432 // declarations.
433 if (HasStackOverflow()) return;
434 }
435
436 if (FLAG_trace) {
437 frame_->CallRuntime(Runtime::kTraceEnter, 0);
438 // Ignore the return value.
439 }
ager@chromium.org3e875802009-06-29 08:26:34 +0000440 CheckStack();
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000441
442 // Compile the body of the function in a vanilla state. Don't
443 // bother compiling all the code if the scope has an illegal
444 // redeclaration.
ager@chromium.org5c838252010-02-19 08:53:10 +0000445 if (!scope()->HasIllegalRedeclaration()) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000446 Comment cmnt(masm_, "[ function body");
447#ifdef DEBUG
448 bool is_builtin = Bootstrapper::IsActive();
449 bool should_trace =
450 is_builtin ? FLAG_trace_builtin_calls : FLAG_trace_calls;
451 if (should_trace) {
452 frame_->CallRuntime(Runtime::kDebugTrace, 0);
453 // Ignore the return value.
454 }
455#endif
ager@chromium.org5c838252010-02-19 08:53:10 +0000456 VisitStatements(info->function()->body());
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000457
ager@chromium.org3e875802009-06-29 08:26:34 +0000458 // Handle the return from the function.
459 if (has_valid_frame()) {
460 // If there is a valid frame, control flow can fall off the end of
461 // the body. In that case there is an implicit return statement.
462 ASSERT(!function_return_is_shadowed_);
ager@chromium.org5c838252010-02-19 08:53:10 +0000463 CodeForReturnPosition(info->function());
ager@chromium.org3e875802009-06-29 08:26:34 +0000464 frame_->PrepareForReturn();
465 Result undefined(Factory::undefined_value());
466 if (function_return_.is_bound()) {
467 function_return_.Jump(&undefined);
468 } else {
469 function_return_.Bind(&undefined);
470 GenerateReturnSequence(&undefined);
471 }
472 } else if (function_return_.is_linked()) {
473 // If the return target has dangling jumps to it, then we have not
474 // yet generated the return sequence. This can happen when (a)
475 // control does not flow off the end of the body so we did not
476 // compile an artificial return statement just above, and (b) there
477 // are return statements in the body but (c) they are all shadowed.
478 Result return_value;
479 function_return_.Bind(&return_value);
480 GenerateReturnSequence(&return_value);
481 }
482 }
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000483 }
ager@chromium.org3e875802009-06-29 08:26:34 +0000484
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000485 // Adjust for function-level loop nesting.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000486 loop_nesting_ -= info->loop_nesting();
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000487
488 // Code generation state must be reset.
489 ASSERT(state_ == NULL);
490 ASSERT(loop_nesting() == 0);
491 ASSERT(!function_return_is_shadowed_);
492 function_return_.Unuse();
493 DeleteFrame();
494
495 // Process any deferred code using the register allocator.
496 if (!HasStackOverflow()) {
497 HistogramTimerScope deferred_timer(&Counters::deferred_code_generation);
498 JumpTarget::set_compiling_deferred_code(true);
499 ProcessDeferred();
500 JumpTarget::set_compiling_deferred_code(false);
501 }
502
503 // There is no need to delete the register allocator, it is a
504 // stack-allocated local.
505 allocator_ = NULL;
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000506}
507
508void CodeGenerator::GenerateReturnSequence(Result* return_value) {
509 // The return value is a live (but not currently reference counted)
510 // reference to rax. This is safe because the current frame does not
511 // contain a reference to rax (it is prepared for the return by spilling
512 // all registers).
513 if (FLAG_trace) {
514 frame_->Push(return_value);
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000515 *return_value = frame_->CallRuntime(Runtime::kTraceExit, 1);
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000516 }
517 return_value->ToRegister(rax);
518
519 // Add a label for checking the size of the code used for returning.
sgjesse@chromium.org911335c2009-08-19 12:59:44 +0000520#ifdef DEBUG
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000521 Label check_exit_codesize;
522 masm_->bind(&check_exit_codesize);
sgjesse@chromium.org911335c2009-08-19 12:59:44 +0000523#endif
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000524
525 // Leave the frame and return popping the arguments and the
526 // receiver.
527 frame_->Exit();
ager@chromium.org5c838252010-02-19 08:53:10 +0000528 masm_->ret((scope()->num_parameters() + 1) * kPointerSize);
ager@chromium.org4af710e2009-09-15 12:20:11 +0000529#ifdef ENABLE_DEBUGGER_SUPPORT
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +0000530 // Add padding that will be overwritten by a debugger breakpoint.
sgjesse@chromium.org911335c2009-08-19 12:59:44 +0000531 // frame_->Exit() generates "movq rsp, rbp; pop rbp; ret k"
532 // with length 7 (3 + 1 + 3).
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000533 const int kPadding = Assembler::kJSReturnSequenceLength - 7;
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +0000534 for (int i = 0; i < kPadding; ++i) {
535 masm_->int3();
536 }
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000537 // Check that the size of the code used for returning matches what is
538 // expected by the debugger.
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000539 ASSERT_EQ(Assembler::kJSReturnSequenceLength,
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +0000540 masm_->SizeOfCodeGeneratedSince(&check_exit_codesize));
ager@chromium.org4af710e2009-09-15 12:20:11 +0000541#endif
542 DeleteFrame();
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000543}
544
545
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000546#ifdef DEBUG
547bool CodeGenerator::HasValidEntryRegisters() {
548 return (allocator()->count(rax) == (frame()->is_used(rax) ? 1 : 0))
549 && (allocator()->count(rbx) == (frame()->is_used(rbx) ? 1 : 0))
550 && (allocator()->count(rcx) == (frame()->is_used(rcx) ? 1 : 0))
551 && (allocator()->count(rdx) == (frame()->is_used(rdx) ? 1 : 0))
552 && (allocator()->count(rdi) == (frame()->is_used(rdi) ? 1 : 0))
553 && (allocator()->count(r8) == (frame()->is_used(r8) ? 1 : 0))
554 && (allocator()->count(r9) == (frame()->is_used(r9) ? 1 : 0))
555 && (allocator()->count(r11) == (frame()->is_used(r11) ? 1 : 0))
556 && (allocator()->count(r14) == (frame()->is_used(r14) ? 1 : 0))
557 && (allocator()->count(r15) == (frame()->is_used(r15) ? 1 : 0))
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000558 && (allocator()->count(r12) == (frame()->is_used(r12) ? 1 : 0));
559}
560#endif
561
562
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000563class DeferredReferenceGetKeyedValue: public DeferredCode {
564 public:
565 explicit DeferredReferenceGetKeyedValue(Register dst,
566 Register receiver,
567 Register key,
568 bool is_global)
569 : dst_(dst), receiver_(receiver), key_(key), is_global_(is_global) {
570 set_comment("[ DeferredReferenceGetKeyedValue");
571 }
572
573 virtual void Generate();
574
575 Label* patch_site() { return &patch_site_; }
576
577 private:
578 Label patch_site_;
579 Register dst_;
580 Register receiver_;
581 Register key_;
582 bool is_global_;
583};
584
585
586void DeferredReferenceGetKeyedValue::Generate() {
587 __ push(receiver_); // First IC argument.
588 __ push(key_); // Second IC argument.
589
590 // Calculate the delta from the IC call instruction to the map check
591 // movq instruction in the inlined version. This delta is stored in
592 // a test(rax, delta) instruction after the call so that we can find
593 // it in the IC initialization code and patch the movq instruction.
594 // This means that we cannot allow test instructions after calls to
595 // KeyedLoadIC stubs in other places.
596 Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
597 RelocInfo::Mode mode = is_global_
598 ? RelocInfo::CODE_TARGET_CONTEXT
599 : RelocInfo::CODE_TARGET;
600 __ Call(ic, mode);
601 // The delta from the start of the map-compare instruction to the
602 // test instruction. We use masm_-> directly here instead of the __
603 // macro because the macro sometimes uses macro expansion to turn
604 // into something that can't return a value. This is encountered
605 // when doing generated code coverage tests.
606 int delta_to_patch_site = masm_->SizeOfCodeGeneratedSince(patch_site());
607 // Here we use masm_-> instead of the __ macro because this is the
608 // instruction that gets patched and coverage code gets in the way.
609 // TODO(X64): Consider whether it's worth switching the test to a
610 // 7-byte NOP with non-zero immediate (0f 1f 80 xxxxxxxx) which won't
611 // be generated normally.
612 masm_->testl(rax, Immediate(-delta_to_patch_site));
613 __ IncrementCounter(&Counters::keyed_load_inline_miss, 1);
614
615 if (!dst_.is(rax)) __ movq(dst_, rax);
616 __ pop(key_);
617 __ pop(receiver_);
618}
619
620
621class DeferredReferenceSetKeyedValue: public DeferredCode {
622 public:
623 DeferredReferenceSetKeyedValue(Register value,
624 Register key,
625 Register receiver)
626 : value_(value), key_(key), receiver_(receiver) {
627 set_comment("[ DeferredReferenceSetKeyedValue");
628 }
629
630 virtual void Generate();
631
632 Label* patch_site() { return &patch_site_; }
633
634 private:
635 Register value_;
636 Register key_;
637 Register receiver_;
638 Label patch_site_;
639};
640
641
642void DeferredReferenceSetKeyedValue::Generate() {
643 __ IncrementCounter(&Counters::keyed_store_inline_miss, 1);
644 // Push receiver and key arguments on the stack.
645 __ push(receiver_);
646 __ push(key_);
647 // Move value argument to eax as expected by the IC stub.
648 if (!value_.is(rax)) __ movq(rax, value_);
649 // Call the IC stub.
650 Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize));
651 __ Call(ic, RelocInfo::CODE_TARGET);
652 // The delta from the start of the map-compare instructions (initial movq)
653 // to the test instruction. We use masm_-> directly here instead of the
654 // __ macro because the macro sometimes uses macro expansion to turn
655 // into something that can't return a value. This is encountered
656 // when doing generated code coverage tests.
657 int delta_to_patch_site = masm_->SizeOfCodeGeneratedSince(patch_site());
658 // Here we use masm_-> instead of the __ macro because this is the
659 // instruction that gets patched and coverage code gets in the way.
660 masm_->testl(rax, Immediate(-delta_to_patch_site));
661 // Restore value (returned from store IC), key and receiver
662 // registers.
663 if (!value_.is(rax)) __ movq(value_, rax);
664 __ pop(key_);
665 __ pop(receiver_);
666}
667
668
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000669void CodeGenerator::CallApplyLazy(Expression* applicand,
ager@chromium.org18ad94b2009-09-02 08:22:29 +0000670 Expression* receiver,
671 VariableProxy* arguments,
672 int position) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000673 // An optimized implementation of expressions of the form
674 // x.apply(y, arguments).
675 // If the arguments object of the scope has not been allocated,
676 // and x.apply is Function.prototype.apply, this optimization
677 // just copies y and the arguments of the current function on the
678 // stack, as receiver and arguments, and calls x.
679 // In the implementation comments, we call x the applicand
680 // and y the receiver.
ager@chromium.org18ad94b2009-09-02 08:22:29 +0000681 ASSERT(ArgumentsMode() == LAZY_ARGUMENTS_ALLOCATION);
682 ASSERT(arguments->IsArguments());
683
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000684 // Load applicand.apply onto the stack. This will usually
ager@chromium.org18ad94b2009-09-02 08:22:29 +0000685 // give us a megamorphic load site. Not super, but it works.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000686 Load(applicand);
687 Handle<String> name = Factory::LookupAsciiSymbol("apply");
688 frame()->Push(name);
689 Result answer = frame()->CallLoadIC(RelocInfo::CODE_TARGET);
690 __ nop();
691 frame()->Push(&answer);
ager@chromium.org18ad94b2009-09-02 08:22:29 +0000692
693 // Load the receiver and the existing arguments object onto the
694 // expression stack. Avoid allocating the arguments object here.
695 Load(receiver);
ager@chromium.org5c838252010-02-19 08:53:10 +0000696 LoadFromSlot(scope()->arguments()->var()->slot(), NOT_INSIDE_TYPEOF);
ager@chromium.org18ad94b2009-09-02 08:22:29 +0000697
698 // Emit the source position information after having loaded the
699 // receiver and the arguments.
700 CodeForSourcePosition(position);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000701 // Contents of frame at this point:
702 // Frame[0]: arguments object of the current function or the hole.
703 // Frame[1]: receiver
704 // Frame[2]: applicand.apply
705 // Frame[3]: applicand.
ager@chromium.org18ad94b2009-09-02 08:22:29 +0000706
707 // Check if the arguments object has been lazily allocated
708 // already. If so, just use that instead of copying the arguments
709 // from the stack. This also deals with cases where a local variable
710 // named 'arguments' has been introduced.
711 frame_->Dup();
712 Result probe = frame_->Pop();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000713 { VirtualFrame::SpilledScope spilled_scope;
714 Label slow, done;
715 bool try_lazy = true;
716 if (probe.is_constant()) {
717 try_lazy = probe.handle()->IsTheHole();
718 } else {
719 __ CompareRoot(probe.reg(), Heap::kTheHoleValueRootIndex);
720 probe.Unuse();
721 __ j(not_equal, &slow);
722 }
ager@chromium.org18ad94b2009-09-02 08:22:29 +0000723
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000724 if (try_lazy) {
725 Label build_args;
726 // Get rid of the arguments object probe.
727 frame_->Drop(); // Can be called on a spilled frame.
728 // Stack now has 3 elements on it.
729 // Contents of stack at this point:
730 // rsp[0]: receiver
731 // rsp[1]: applicand.apply
732 // rsp[2]: applicand.
ager@chromium.org18ad94b2009-09-02 08:22:29 +0000733
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000734 // Check that the receiver really is a JavaScript object.
735 __ movq(rax, Operand(rsp, 0));
736 Condition is_smi = masm_->CheckSmi(rax);
737 __ j(is_smi, &build_args);
ager@chromium.org18ad94b2009-09-02 08:22:29 +0000738 // We allow all JSObjects including JSFunctions. As long as
739 // JS_FUNCTION_TYPE is the last instance type and it is right
740 // after LAST_JS_OBJECT_TYPE, we do not have to check the upper
741 // bound.
742 ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
743 ASSERT(JS_FUNCTION_TYPE == LAST_JS_OBJECT_TYPE + 1);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000744 __ CmpObjectType(rax, FIRST_JS_OBJECT_TYPE, rcx);
745 __ j(below, &build_args);
ager@chromium.org18ad94b2009-09-02 08:22:29 +0000746
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000747 // Check that applicand.apply is Function.prototype.apply.
748 __ movq(rax, Operand(rsp, kPointerSize));
749 is_smi = masm_->CheckSmi(rax);
750 __ j(is_smi, &build_args);
751 __ CmpObjectType(rax, JS_FUNCTION_TYPE, rcx);
752 __ j(not_equal, &build_args);
753 __ movq(rax, FieldOperand(rax, JSFunction::kSharedFunctionInfoOffset));
ager@chromium.org18ad94b2009-09-02 08:22:29 +0000754 Handle<Code> apply_code(Builtins::builtin(Builtins::FunctionApply));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000755 __ Cmp(FieldOperand(rax, SharedFunctionInfo::kCodeOffset), apply_code);
756 __ j(not_equal, &build_args);
757
758 // Check that applicand is a function.
759 __ movq(rdi, Operand(rsp, 2 * kPointerSize));
760 is_smi = masm_->CheckSmi(rdi);
761 __ j(is_smi, &build_args);
762 __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rcx);
763 __ j(not_equal, &build_args);
764
765 // Copy the arguments to this function possibly from the
766 // adaptor frame below it.
767 Label invoke, adapted;
768 __ movq(rdx, Operand(rbp, StandardFrameConstants::kCallerFPOffset));
769 __ SmiCompare(Operand(rdx, StandardFrameConstants::kContextOffset),
770 Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR));
771 __ j(equal, &adapted);
772
773 // No arguments adaptor frame. Copy fixed number of arguments.
ager@chromium.org5c838252010-02-19 08:53:10 +0000774 __ movq(rax, Immediate(scope()->num_parameters()));
775 for (int i = 0; i < scope()->num_parameters(); i++) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000776 __ push(frame_->ParameterAt(i));
777 }
778 __ jmp(&invoke);
779
780 // Arguments adaptor frame present. Copy arguments from there, but
781 // avoid copying too many arguments to avoid stack overflows.
782 __ bind(&adapted);
783 static const uint32_t kArgumentsLimit = 1 * KB;
784 __ movq(rax, Operand(rdx, ArgumentsAdaptorFrameConstants::kLengthOffset));
785 __ SmiToInteger32(rax, rax);
786 __ movq(rcx, rax);
787 __ cmpq(rax, Immediate(kArgumentsLimit));
788 __ j(above, &build_args);
789
790 // Loop through the arguments pushing them onto the execution
791 // stack. We don't inform the virtual frame of the push, so we don't
792 // have to worry about getting rid of the elements from the virtual
793 // frame.
794 Label loop;
795 // rcx is a small non-negative integer, due to the test above.
796 __ testl(rcx, rcx);
797 __ j(zero, &invoke);
798 __ bind(&loop);
799 __ push(Operand(rdx, rcx, times_pointer_size, 1 * kPointerSize));
800 __ decl(rcx);
801 __ j(not_zero, &loop);
802
803 // Invoke the function.
804 __ bind(&invoke);
805 ParameterCount actual(rax);
806 __ InvokeFunction(rdi, actual, CALL_FUNCTION);
807 // Drop applicand.apply and applicand from the stack, and push
808 // the result of the function call, but leave the spilled frame
809 // unchanged, with 3 elements, so it is correct when we compile the
810 // slow-case code.
811 __ addq(rsp, Immediate(2 * kPointerSize));
812 __ push(rax);
813 // Stack now has 1 element:
814 // rsp[0]: result
815 __ jmp(&done);
816
817 // Slow-case: Allocate the arguments object since we know it isn't
818 // there, and fall-through to the slow-case where we call
819 // applicand.apply.
820 __ bind(&build_args);
821 // Stack now has 3 elements, because we have jumped from where:
822 // rsp[0]: receiver
823 // rsp[1]: applicand.apply
824 // rsp[2]: applicand.
825
826 // StoreArgumentsObject requires a correct frame, and may modify it.
827 Result arguments_object = StoreArgumentsObject(false);
828 frame_->SpillAll();
829 arguments_object.ToRegister();
830 frame_->EmitPush(arguments_object.reg());
831 arguments_object.Unuse();
832 // Stack and frame now have 4 elements.
833 __ bind(&slow);
ager@chromium.org18ad94b2009-09-02 08:22:29 +0000834 }
835
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000836 // Generic computation of x.apply(y, args) with no special optimization.
837 // Flip applicand.apply and applicand on the stack, so
838 // applicand looks like the receiver of the applicand.apply call.
839 // Then process it as a normal function call.
840 __ movq(rax, Operand(rsp, 3 * kPointerSize));
841 __ movq(rbx, Operand(rsp, 2 * kPointerSize));
842 __ movq(Operand(rsp, 2 * kPointerSize), rax);
843 __ movq(Operand(rsp, 3 * kPointerSize), rbx);
ager@chromium.org18ad94b2009-09-02 08:22:29 +0000844
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000845 CallFunctionStub call_function(2, NOT_IN_LOOP, NO_CALL_FUNCTION_FLAGS);
846 Result res = frame_->CallStub(&call_function, 3);
847 // The function and its two arguments have been dropped.
848 frame_->Drop(1); // Drop the receiver as well.
849 res.ToRegister();
850 frame_->EmitPush(res.reg());
851 // Stack now has 1 element:
852 // rsp[0]: result
853 if (try_lazy) __ bind(&done);
854 } // End of spilled scope.
855 // Restore the context register after a call.
ager@chromium.org18ad94b2009-09-02 08:22:29 +0000856 frame_->RestoreContextRegister();
857}
858
859
860class DeferredStackCheck: public DeferredCode {
861 public:
862 DeferredStackCheck() {
863 set_comment("[ DeferredStackCheck");
864 }
865
866 virtual void Generate();
867};
868
869
870void DeferredStackCheck::Generate() {
871 StackCheckStub stub;
872 __ CallStub(&stub);
873}
874
875
876void CodeGenerator::CheckStack() {
ager@chromium.org3811b432009-10-28 14:53:37 +0000877 DeferredStackCheck* deferred = new DeferredStackCheck;
878 __ CompareRoot(rsp, Heap::kStackLimitRootIndex);
879 deferred->Branch(below);
880 deferred->BindExit();
ager@chromium.org18ad94b2009-09-02 08:22:29 +0000881}
882
883
ager@chromium.org3e875802009-06-29 08:26:34 +0000884void CodeGenerator::VisitAndSpill(Statement* statement) {
885 // TODO(X64): No architecture specific code. Move to shared location.
886 ASSERT(in_spilled_code());
887 set_in_spilled_code(false);
888 Visit(statement);
889 if (frame_ != NULL) {
890 frame_->SpillAll();
891 }
892 set_in_spilled_code(true);
893}
894
895
896void CodeGenerator::VisitStatementsAndSpill(ZoneList<Statement*>* statements) {
897 ASSERT(in_spilled_code());
898 set_in_spilled_code(false);
899 VisitStatements(statements);
900 if (frame_ != NULL) {
901 frame_->SpillAll();
902 }
903 set_in_spilled_code(true);
904}
905
906
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000907void CodeGenerator::VisitStatements(ZoneList<Statement*>* statements) {
908 ASSERT(!in_spilled_code());
909 for (int i = 0; has_valid_frame() && i < statements->length(); i++) {
910 Visit(statements->at(i));
911 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +0000912}
913
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000914
915void CodeGenerator::VisitBlock(Block* node) {
916 ASSERT(!in_spilled_code());
917 Comment cmnt(masm_, "[ Block");
918 CodeForStatementPosition(node);
919 node->break_target()->set_direction(JumpTarget::FORWARD_ONLY);
920 VisitStatements(node->statements());
921 if (node->break_target()->is_linked()) {
922 node->break_target()->Bind();
923 }
924 node->break_target()->Unuse();
kasperl@chromium.org71affb52009-05-26 05:44:31 +0000925}
926
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000927
928void CodeGenerator::VisitDeclaration(Declaration* node) {
929 Comment cmnt(masm_, "[ Declaration");
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000930 Variable* var = node->proxy()->var();
931 ASSERT(var != NULL); // must have been resolved
932 Slot* slot = var->slot();
933
934 // If it was not possible to allocate the variable at compile time,
935 // we need to "declare" it at runtime to make sure it actually
936 // exists in the local context.
937 if (slot != NULL && slot->type() == Slot::LOOKUP) {
938 // Variables with a "LOOKUP" slot were introduced as non-locals
939 // during variable resolution and must have mode DYNAMIC.
940 ASSERT(var->is_dynamic());
941 // For now, just do a runtime call. Sync the virtual frame eagerly
942 // so we can simply push the arguments into place.
943 frame_->SyncRange(0, frame_->element_count() - 1);
944 frame_->EmitPush(rsi);
945 __ movq(kScratchRegister, var->name(), RelocInfo::EMBEDDED_OBJECT);
946 frame_->EmitPush(kScratchRegister);
947 // Declaration nodes are always introduced in one of two modes.
948 ASSERT(node->mode() == Variable::VAR || node->mode() == Variable::CONST);
949 PropertyAttributes attr = node->mode() == Variable::VAR ? NONE : READ_ONLY;
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +0000950 frame_->EmitPush(Smi::FromInt(attr));
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000951 // Push initial value, if any.
952 // Note: For variables we must not push an initial value (such as
953 // 'undefined') because we may have a (legal) redeclaration and we
954 // must not destroy the current value.
955 if (node->mode() == Variable::CONST) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +0000956 frame_->EmitPush(Heap::kTheHoleValueRootIndex);
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000957 } else if (node->fun() != NULL) {
958 Load(node->fun());
959 } else {
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +0000960 frame_->EmitPush(Smi::FromInt(0)); // no initial value!
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000961 }
962 Result ignored = frame_->CallRuntime(Runtime::kDeclareContextSlot, 4);
963 // Ignore the return value (declarations are statements).
964 return;
965 }
966
967 ASSERT(!var->is_global());
968
969 // If we have a function or a constant, we need to initialize the variable.
970 Expression* val = NULL;
971 if (node->mode() == Variable::CONST) {
972 val = new Literal(Factory::the_hole_value());
973 } else {
974 val = node->fun(); // NULL if we don't have a function
975 }
976
977 if (val != NULL) {
978 {
979 // Set the initial value.
980 Reference target(this, node->proxy());
981 Load(val);
982 target.SetValue(NOT_CONST_INIT);
983 // The reference is removed from the stack (preserving TOS) when
984 // it goes out of scope.
985 }
986 // Get rid of the assigned value (declarations are statements).
987 frame_->Drop();
988 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +0000989}
990
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000991
992void CodeGenerator::VisitExpressionStatement(ExpressionStatement* node) {
993 ASSERT(!in_spilled_code());
994 Comment cmnt(masm_, "[ ExpressionStatement");
995 CodeForStatementPosition(node);
996 Expression* expression = node->expression();
997 expression->MarkAsStatement();
998 Load(expression);
999 // Remove the lingering expression result from the top of stack.
1000 frame_->Drop();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001001}
1002
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001003
1004void CodeGenerator::VisitEmptyStatement(EmptyStatement* node) {
1005 ASSERT(!in_spilled_code());
1006 Comment cmnt(masm_, "// EmptyStatement");
1007 CodeForStatementPosition(node);
1008 // nothing to do
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001009}
1010
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001011
1012void CodeGenerator::VisitIfStatement(IfStatement* node) {
1013 ASSERT(!in_spilled_code());
1014 Comment cmnt(masm_, "[ IfStatement");
1015 // Generate different code depending on which parts of the if statement
1016 // are present or not.
1017 bool has_then_stm = node->HasThenStatement();
1018 bool has_else_stm = node->HasElseStatement();
1019
1020 CodeForStatementPosition(node);
1021 JumpTarget exit;
1022 if (has_then_stm && has_else_stm) {
1023 JumpTarget then;
1024 JumpTarget else_;
1025 ControlDestination dest(&then, &else_, true);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001026 LoadCondition(node->condition(), &dest, true);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001027
1028 if (dest.false_was_fall_through()) {
1029 // The else target was bound, so we compile the else part first.
1030 Visit(node->else_statement());
1031
1032 // We may have dangling jumps to the then part.
1033 if (then.is_linked()) {
1034 if (has_valid_frame()) exit.Jump();
1035 then.Bind();
1036 Visit(node->then_statement());
1037 }
1038 } else {
1039 // The then target was bound, so we compile the then part first.
1040 Visit(node->then_statement());
1041
1042 if (else_.is_linked()) {
1043 if (has_valid_frame()) exit.Jump();
1044 else_.Bind();
1045 Visit(node->else_statement());
1046 }
1047 }
1048
1049 } else if (has_then_stm) {
1050 ASSERT(!has_else_stm);
1051 JumpTarget then;
1052 ControlDestination dest(&then, &exit, true);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001053 LoadCondition(node->condition(), &dest, true);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001054
1055 if (dest.false_was_fall_through()) {
1056 // The exit label was bound. We may have dangling jumps to the
1057 // then part.
1058 if (then.is_linked()) {
1059 exit.Unuse();
1060 exit.Jump();
1061 then.Bind();
1062 Visit(node->then_statement());
1063 }
1064 } else {
1065 // The then label was bound.
1066 Visit(node->then_statement());
1067 }
1068
1069 } else if (has_else_stm) {
1070 ASSERT(!has_then_stm);
1071 JumpTarget else_;
1072 ControlDestination dest(&exit, &else_, false);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001073 LoadCondition(node->condition(), &dest, true);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001074
1075 if (dest.true_was_fall_through()) {
1076 // The exit label was bound. We may have dangling jumps to the
1077 // else part.
1078 if (else_.is_linked()) {
1079 exit.Unuse();
1080 exit.Jump();
1081 else_.Bind();
1082 Visit(node->else_statement());
1083 }
1084 } else {
1085 // The else label was bound.
1086 Visit(node->else_statement());
1087 }
1088
1089 } else {
1090 ASSERT(!has_then_stm && !has_else_stm);
1091 // We only care about the condition's side effects (not its value
1092 // or control flow effect). LoadCondition is called without
1093 // forcing control flow.
1094 ControlDestination dest(&exit, &exit, true);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001095 LoadCondition(node->condition(), &dest, false);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001096 if (!dest.is_used()) {
1097 // We got a value on the frame rather than (or in addition to)
1098 // control flow.
1099 frame_->Drop();
1100 }
1101 }
1102
1103 if (exit.is_linked()) {
1104 exit.Bind();
1105 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001106}
1107
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001108
ager@chromium.org3e875802009-06-29 08:26:34 +00001109void CodeGenerator::VisitContinueStatement(ContinueStatement* node) {
1110 ASSERT(!in_spilled_code());
1111 Comment cmnt(masm_, "[ ContinueStatement");
1112 CodeForStatementPosition(node);
1113 node->target()->continue_target()->Jump();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001114}
1115
ager@chromium.org3e875802009-06-29 08:26:34 +00001116
1117void CodeGenerator::VisitBreakStatement(BreakStatement* node) {
1118 ASSERT(!in_spilled_code());
1119 Comment cmnt(masm_, "[ BreakStatement");
1120 CodeForStatementPosition(node);
1121 node->target()->break_target()->Jump();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001122}
1123
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001124
1125void CodeGenerator::VisitReturnStatement(ReturnStatement* node) {
1126 ASSERT(!in_spilled_code());
1127 Comment cmnt(masm_, "[ ReturnStatement");
1128
1129 CodeForStatementPosition(node);
1130 Load(node->expression());
1131 Result return_value = frame_->Pop();
1132 if (function_return_is_shadowed_) {
1133 function_return_.Jump(&return_value);
1134 } else {
1135 frame_->PrepareForReturn();
1136 if (function_return_.is_bound()) {
1137 // If the function return label is already bound we reuse the
1138 // code by jumping to the return site.
1139 function_return_.Jump(&return_value);
1140 } else {
1141 function_return_.Bind(&return_value);
1142 GenerateReturnSequence(&return_value);
1143 }
1144 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001145}
1146
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001147
ager@chromium.org3e875802009-06-29 08:26:34 +00001148void CodeGenerator::VisitWithEnterStatement(WithEnterStatement* node) {
1149 ASSERT(!in_spilled_code());
1150 Comment cmnt(masm_, "[ WithEnterStatement");
1151 CodeForStatementPosition(node);
1152 Load(node->expression());
1153 Result context;
1154 if (node->is_catch_block()) {
1155 context = frame_->CallRuntime(Runtime::kPushCatchContext, 1);
1156 } else {
1157 context = frame_->CallRuntime(Runtime::kPushContext, 1);
1158 }
1159
1160 // Update context local.
1161 frame_->SaveContextRegister();
1162
1163 // Verify that the runtime call result and rsi agree.
1164 if (FLAG_debug_code) {
1165 __ cmpq(context.reg(), rsi);
1166 __ Assert(equal, "Runtime::NewContext should end up in rsi");
1167 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001168}
1169
ager@chromium.org3e875802009-06-29 08:26:34 +00001170
1171void CodeGenerator::VisitWithExitStatement(WithExitStatement* node) {
1172 ASSERT(!in_spilled_code());
1173 Comment cmnt(masm_, "[ WithExitStatement");
1174 CodeForStatementPosition(node);
1175 // Pop context.
1176 __ movq(rsi, ContextOperand(rsi, Context::PREVIOUS_INDEX));
1177 // Update context local.
1178 frame_->SaveContextRegister();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001179}
1180
ager@chromium.org3e875802009-06-29 08:26:34 +00001181
1182void CodeGenerator::VisitSwitchStatement(SwitchStatement* node) {
1183 // TODO(X64): This code is completely generic and should be moved somewhere
1184 // where it can be shared between architectures.
1185 ASSERT(!in_spilled_code());
1186 Comment cmnt(masm_, "[ SwitchStatement");
1187 CodeForStatementPosition(node);
1188 node->break_target()->set_direction(JumpTarget::FORWARD_ONLY);
1189
1190 // Compile the switch value.
1191 Load(node->tag());
1192
1193 ZoneList<CaseClause*>* cases = node->cases();
1194 int length = cases->length();
1195 CaseClause* default_clause = NULL;
1196
1197 JumpTarget next_test;
1198 // Compile the case label expressions and comparisons. Exit early
1199 // if a comparison is unconditionally true. The target next_test is
1200 // bound before the loop in order to indicate control flow to the
1201 // first comparison.
1202 next_test.Bind();
1203 for (int i = 0; i < length && !next_test.is_unused(); i++) {
1204 CaseClause* clause = cases->at(i);
1205 // The default is not a test, but remember it for later.
1206 if (clause->is_default()) {
1207 default_clause = clause;
1208 continue;
1209 }
1210
1211 Comment cmnt(masm_, "[ Case comparison");
1212 // We recycle the same target next_test for each test. Bind it if
1213 // the previous test has not done so and then unuse it for the
1214 // loop.
1215 if (next_test.is_linked()) {
1216 next_test.Bind();
1217 }
1218 next_test.Unuse();
1219
1220 // Duplicate the switch value.
1221 frame_->Dup();
1222
1223 // Compile the label expression.
1224 Load(clause->label());
1225
1226 // Compare and branch to the body if true or the next test if
1227 // false. Prefer the next test as a fall through.
1228 ControlDestination dest(clause->body_target(), &next_test, false);
ager@chromium.org5c838252010-02-19 08:53:10 +00001229 Comparison(node, equal, true, &dest);
ager@chromium.org3e875802009-06-29 08:26:34 +00001230
1231 // If the comparison fell through to the true target, jump to the
1232 // actual body.
1233 if (dest.true_was_fall_through()) {
1234 clause->body_target()->Unuse();
1235 clause->body_target()->Jump();
1236 }
1237 }
1238
1239 // If there was control flow to a next test from the last one
1240 // compiled, compile a jump to the default or break target.
1241 if (!next_test.is_unused()) {
1242 if (next_test.is_linked()) {
1243 next_test.Bind();
1244 }
1245 // Drop the switch value.
1246 frame_->Drop();
1247 if (default_clause != NULL) {
1248 default_clause->body_target()->Jump();
1249 } else {
1250 node->break_target()->Jump();
1251 }
1252 }
1253
1254 // The last instruction emitted was a jump, either to the default
1255 // clause or the break target, or else to a case body from the loop
1256 // that compiles the tests.
1257 ASSERT(!has_valid_frame());
1258 // Compile case bodies as needed.
1259 for (int i = 0; i < length; i++) {
1260 CaseClause* clause = cases->at(i);
1261
1262 // There are two ways to reach the body: from the corresponding
1263 // test or as the fall through of the previous body.
1264 if (clause->body_target()->is_linked() || has_valid_frame()) {
1265 if (clause->body_target()->is_linked()) {
1266 if (has_valid_frame()) {
1267 // If we have both a jump to the test and a fall through, put
1268 // a jump on the fall through path to avoid the dropping of
1269 // the switch value on the test path. The exception is the
1270 // default which has already had the switch value dropped.
1271 if (clause->is_default()) {
1272 clause->body_target()->Bind();
1273 } else {
1274 JumpTarget body;
1275 body.Jump();
1276 clause->body_target()->Bind();
1277 frame_->Drop();
1278 body.Bind();
1279 }
1280 } else {
1281 // No fall through to worry about.
1282 clause->body_target()->Bind();
1283 if (!clause->is_default()) {
1284 frame_->Drop();
1285 }
1286 }
1287 } else {
1288 // Otherwise, we have only fall through.
1289 ASSERT(has_valid_frame());
1290 }
1291
1292 // We are now prepared to compile the body.
1293 Comment cmnt(masm_, "[ Case body");
1294 VisitStatements(clause->statements());
1295 }
1296 clause->body_target()->Unuse();
1297 }
1298
1299 // We may not have a valid frame here so bind the break target only
1300 // if needed.
1301 if (node->break_target()->is_linked()) {
1302 node->break_target()->Bind();
1303 }
1304 node->break_target()->Unuse();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001305}
1306
ager@chromium.org3e875802009-06-29 08:26:34 +00001307
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001308void CodeGenerator::VisitDoWhileStatement(DoWhileStatement* node) {
ager@chromium.org3e875802009-06-29 08:26:34 +00001309 ASSERT(!in_spilled_code());
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001310 Comment cmnt(masm_, "[ DoWhileStatement");
ager@chromium.org3e875802009-06-29 08:26:34 +00001311 CodeForStatementPosition(node);
1312 node->break_target()->set_direction(JumpTarget::FORWARD_ONLY);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001313 JumpTarget body(JumpTarget::BIDIRECTIONAL);
1314 IncrementLoopNesting();
ager@chromium.org3e875802009-06-29 08:26:34 +00001315
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001316 ConditionAnalysis info = AnalyzeCondition(node->cond());
1317 // Label the top of the loop for the backward jump if necessary.
1318 switch (info) {
1319 case ALWAYS_TRUE:
1320 // Use the continue target.
1321 node->continue_target()->set_direction(JumpTarget::BIDIRECTIONAL);
1322 node->continue_target()->Bind();
1323 break;
1324 case ALWAYS_FALSE:
1325 // No need to label it.
1326 node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY);
1327 break;
1328 case DONT_KNOW:
1329 // Continue is the test, so use the backward body target.
1330 node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY);
1331 body.Bind();
1332 break;
ager@chromium.org3e875802009-06-29 08:26:34 +00001333 }
1334
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001335 CheckStack(); // TODO(1222600): ignore if body contains calls.
1336 Visit(node->body());
ager@chromium.org3e875802009-06-29 08:26:34 +00001337
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001338 // Compile the test.
1339 switch (info) {
1340 case ALWAYS_TRUE:
1341 // If control flow can fall off the end of the body, jump back
1342 // to the top and bind the break target at the exit.
1343 if (has_valid_frame()) {
1344 node->continue_target()->Jump();
ager@chromium.org3e875802009-06-29 08:26:34 +00001345 }
ager@chromium.org3e875802009-06-29 08:26:34 +00001346 if (node->break_target()->is_linked()) {
1347 node->break_target()->Bind();
1348 }
1349 break;
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001350 case ALWAYS_FALSE:
1351 // We may have had continues or breaks in the body.
1352 if (node->continue_target()->is_linked()) {
1353 node->continue_target()->Bind();
ager@chromium.org3e875802009-06-29 08:26:34 +00001354 }
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001355 if (node->break_target()->is_linked()) {
1356 node->break_target()->Bind();
ager@chromium.org3e875802009-06-29 08:26:34 +00001357 }
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001358 break;
1359 case DONT_KNOW:
1360 // We have to compile the test expression if it can be reached by
1361 // control flow falling out of the body or via continue.
1362 if (node->continue_target()->is_linked()) {
1363 node->continue_target()->Bind();
1364 }
1365 if (has_valid_frame()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001366 Comment cmnt(masm_, "[ DoWhileCondition");
1367 CodeForDoWhileConditionPosition(node);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001368 ControlDestination dest(&body, node->break_target(), false);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001369 LoadCondition(node->cond(), &dest, true);
ager@chromium.org3e875802009-06-29 08:26:34 +00001370 }
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001371 if (node->break_target()->is_linked()) {
1372 node->break_target()->Bind();
1373 }
1374 break;
1375 }
ager@chromium.org3e875802009-06-29 08:26:34 +00001376
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001377 DecrementLoopNesting();
1378 node->continue_target()->Unuse();
1379 node->break_target()->Unuse();
1380}
ager@chromium.org3e875802009-06-29 08:26:34 +00001381
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001382
1383void CodeGenerator::VisitWhileStatement(WhileStatement* node) {
1384 ASSERT(!in_spilled_code());
1385 Comment cmnt(masm_, "[ WhileStatement");
1386 CodeForStatementPosition(node);
1387
1388 // If the condition is always false and has no side effects, we do not
1389 // need to compile anything.
1390 ConditionAnalysis info = AnalyzeCondition(node->cond());
1391 if (info == ALWAYS_FALSE) return;
1392
1393 // Do not duplicate conditions that may have function literal
1394 // subexpressions. This can cause us to compile the function literal
1395 // twice.
1396 bool test_at_bottom = !node->may_have_function_literal();
1397 node->break_target()->set_direction(JumpTarget::FORWARD_ONLY);
1398 IncrementLoopNesting();
1399 JumpTarget body;
1400 if (test_at_bottom) {
1401 body.set_direction(JumpTarget::BIDIRECTIONAL);
1402 }
1403
1404 // Based on the condition analysis, compile the test as necessary.
1405 switch (info) {
1406 case ALWAYS_TRUE:
1407 // We will not compile the test expression. Label the top of the
1408 // loop with the continue target.
1409 node->continue_target()->set_direction(JumpTarget::BIDIRECTIONAL);
1410 node->continue_target()->Bind();
1411 break;
1412 case DONT_KNOW: {
1413 if (test_at_bottom) {
1414 // Continue is the test at the bottom, no need to label the test
1415 // at the top. The body is a backward target.
1416 node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY);
1417 } else {
1418 // Label the test at the top as the continue target. The body
1419 // is a forward-only target.
1420 node->continue_target()->set_direction(JumpTarget::BIDIRECTIONAL);
1421 node->continue_target()->Bind();
1422 }
1423 // Compile the test with the body as the true target and preferred
1424 // fall-through and with the break target as the false target.
1425 ControlDestination dest(&body, node->break_target(), true);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001426 LoadCondition(node->cond(), &dest, true);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001427
1428 if (dest.false_was_fall_through()) {
1429 // If we got the break target as fall-through, the test may have
1430 // been unconditionally false (if there are no jumps to the
1431 // body).
1432 if (!body.is_linked()) {
1433 DecrementLoopNesting();
1434 return;
1435 }
1436
1437 // Otherwise, jump around the body on the fall through and then
1438 // bind the body target.
1439 node->break_target()->Unuse();
1440 node->break_target()->Jump();
1441 body.Bind();
1442 }
1443 break;
1444 }
1445 case ALWAYS_FALSE:
1446 UNREACHABLE();
1447 break;
1448 }
1449
1450 CheckStack(); // TODO(1222600): ignore if body contains calls.
1451 Visit(node->body());
1452
1453 // Based on the condition analysis, compile the backward jump as
1454 // necessary.
1455 switch (info) {
1456 case ALWAYS_TRUE:
1457 // The loop body has been labeled with the continue target.
1458 if (has_valid_frame()) {
1459 node->continue_target()->Jump();
1460 }
1461 break;
1462 case DONT_KNOW:
1463 if (test_at_bottom) {
1464 // If we have chosen to recompile the test at the bottom,
1465 // then it is the continue target.
ager@chromium.org3e875802009-06-29 08:26:34 +00001466 if (node->continue_target()->is_linked()) {
1467 node->continue_target()->Bind();
1468 }
ager@chromium.org3e875802009-06-29 08:26:34 +00001469 if (has_valid_frame()) {
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001470 // The break target is the fall-through (body is a backward
1471 // jump from here and thus an invalid fall-through).
1472 ControlDestination dest(&body, node->break_target(), false);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001473 LoadCondition(node->cond(), &dest, true);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001474 }
1475 } else {
1476 // If we have chosen not to recompile the test at the
1477 // bottom, jump back to the one at the top.
1478 if (has_valid_frame()) {
1479 node->continue_target()->Jump();
ager@chromium.org3e875802009-06-29 08:26:34 +00001480 }
1481 }
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001482 break;
1483 case ALWAYS_FALSE:
1484 UNREACHABLE();
1485 break;
1486 }
ager@chromium.org3e875802009-06-29 08:26:34 +00001487
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001488 // The break target may be already bound (by the condition), or there
1489 // may not be a valid frame. Bind it only if needed.
1490 if (node->break_target()->is_linked()) {
1491 node->break_target()->Bind();
1492 }
1493 DecrementLoopNesting();
1494}
1495
1496
1497void CodeGenerator::VisitForStatement(ForStatement* node) {
1498 ASSERT(!in_spilled_code());
1499 Comment cmnt(masm_, "[ ForStatement");
1500 CodeForStatementPosition(node);
1501
1502 // Compile the init expression if present.
1503 if (node->init() != NULL) {
1504 Visit(node->init());
1505 }
1506
1507 // If the condition is always false and has no side effects, we do not
1508 // need to compile anything else.
1509 ConditionAnalysis info = AnalyzeCondition(node->cond());
1510 if (info == ALWAYS_FALSE) return;
1511
1512 // Do not duplicate conditions that may have function literal
1513 // subexpressions. This can cause us to compile the function literal
1514 // twice.
1515 bool test_at_bottom = !node->may_have_function_literal();
1516 node->break_target()->set_direction(JumpTarget::FORWARD_ONLY);
1517 IncrementLoopNesting();
1518
1519 // Target for backward edge if no test at the bottom, otherwise
1520 // unused.
1521 JumpTarget loop(JumpTarget::BIDIRECTIONAL);
1522
1523 // Target for backward edge if there is a test at the bottom,
1524 // otherwise used as target for test at the top.
1525 JumpTarget body;
1526 if (test_at_bottom) {
1527 body.set_direction(JumpTarget::BIDIRECTIONAL);
1528 }
1529
1530 // Based on the condition analysis, compile the test as necessary.
1531 switch (info) {
1532 case ALWAYS_TRUE:
1533 // We will not compile the test expression. Label the top of the
1534 // loop.
1535 if (node->next() == NULL) {
1536 // Use the continue target if there is no update expression.
1537 node->continue_target()->set_direction(JumpTarget::BIDIRECTIONAL);
1538 node->continue_target()->Bind();
1539 } else {
1540 // Otherwise use the backward loop target.
1541 node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY);
1542 loop.Bind();
1543 }
1544 break;
1545 case DONT_KNOW: {
1546 if (test_at_bottom) {
1547 // Continue is either the update expression or the test at the
1548 // bottom, no need to label the test at the top.
1549 node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY);
1550 } else if (node->next() == NULL) {
1551 // We are not recompiling the test at the bottom and there is no
1552 // update expression.
1553 node->continue_target()->set_direction(JumpTarget::BIDIRECTIONAL);
1554 node->continue_target()->Bind();
1555 } else {
1556 // We are not recompiling the test at the bottom and there is an
1557 // update expression.
1558 node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY);
1559 loop.Bind();
1560 }
1561
1562 // Compile the test with the body as the true target and preferred
1563 // fall-through and with the break target as the false target.
1564 ControlDestination dest(&body, node->break_target(), true);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001565 LoadCondition(node->cond(), &dest, true);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001566
1567 if (dest.false_was_fall_through()) {
1568 // If we got the break target as fall-through, the test may have
1569 // been unconditionally false (if there are no jumps to the
1570 // body).
1571 if (!body.is_linked()) {
1572 DecrementLoopNesting();
1573 return;
1574 }
1575
1576 // Otherwise, jump around the body on the fall through and then
1577 // bind the body target.
1578 node->break_target()->Unuse();
1579 node->break_target()->Jump();
1580 body.Bind();
1581 }
1582 break;
1583 }
1584 case ALWAYS_FALSE:
1585 UNREACHABLE();
1586 break;
1587 }
1588
1589 CheckStack(); // TODO(1222600): ignore if body contains calls.
1590 Visit(node->body());
1591
1592 // If there is an update expression, compile it if necessary.
1593 if (node->next() != NULL) {
1594 if (node->continue_target()->is_linked()) {
1595 node->continue_target()->Bind();
1596 }
1597
1598 // Control can reach the update by falling out of the body or by a
1599 // continue.
1600 if (has_valid_frame()) {
1601 // Record the source position of the statement as this code which
1602 // is after the code for the body actually belongs to the loop
1603 // statement and not the body.
1604 CodeForStatementPosition(node);
1605 Visit(node->next());
1606 }
1607 }
1608
1609 // Based on the condition analysis, compile the backward jump as
1610 // necessary.
1611 switch (info) {
1612 case ALWAYS_TRUE:
1613 if (has_valid_frame()) {
1614 if (node->next() == NULL) {
1615 node->continue_target()->Jump();
1616 } else {
1617 loop.Jump();
1618 }
1619 }
1620 break;
1621 case DONT_KNOW:
1622 if (test_at_bottom) {
1623 if (node->continue_target()->is_linked()) {
1624 // We can have dangling jumps to the continue target if there
1625 // was no update expression.
1626 node->continue_target()->Bind();
1627 }
1628 // Control can reach the test at the bottom by falling out of
1629 // the body, by a continue in the body, or from the update
1630 // expression.
1631 if (has_valid_frame()) {
1632 // The break target is the fall-through (body is a backward
1633 // jump from here).
1634 ControlDestination dest(&body, node->break_target(), false);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001635 LoadCondition(node->cond(), &dest, true);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001636 }
1637 } else {
1638 // Otherwise, jump back to the test at the top.
ager@chromium.org3e875802009-06-29 08:26:34 +00001639 if (has_valid_frame()) {
1640 if (node->next() == NULL) {
1641 node->continue_target()->Jump();
1642 } else {
1643 loop.Jump();
1644 }
1645 }
ager@chromium.org3e875802009-06-29 08:26:34 +00001646 }
1647 break;
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001648 case ALWAYS_FALSE:
1649 UNREACHABLE();
1650 break;
ager@chromium.org3e875802009-06-29 08:26:34 +00001651 }
1652
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001653 // The break target may be already bound (by the condition), or there
1654 // may not be a valid frame. Bind it only if needed.
1655 if (node->break_target()->is_linked()) {
1656 node->break_target()->Bind();
1657 }
ager@chromium.org3e875802009-06-29 08:26:34 +00001658 DecrementLoopNesting();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001659}
1660
ager@chromium.org3e875802009-06-29 08:26:34 +00001661
1662void CodeGenerator::VisitForInStatement(ForInStatement* node) {
1663 ASSERT(!in_spilled_code());
1664 VirtualFrame::SpilledScope spilled_scope;
1665 Comment cmnt(masm_, "[ ForInStatement");
1666 CodeForStatementPosition(node);
1667
1668 JumpTarget primitive;
1669 JumpTarget jsobject;
1670 JumpTarget fixed_array;
1671 JumpTarget entry(JumpTarget::BIDIRECTIONAL);
1672 JumpTarget end_del_check;
1673 JumpTarget exit;
1674
1675 // Get the object to enumerate over (converted to JSObject).
1676 LoadAndSpill(node->enumerable());
1677
1678 // Both SpiderMonkey and kjs ignore null and undefined in contrast
1679 // to the specification. 12.6.4 mandates a call to ToObject.
1680 frame_->EmitPop(rax);
1681
1682 // rax: value to be iterated over
ager@chromium.org18ad94b2009-09-02 08:22:29 +00001683 __ CompareRoot(rax, Heap::kUndefinedValueRootIndex);
ager@chromium.org3e875802009-06-29 08:26:34 +00001684 exit.Branch(equal);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00001685 __ CompareRoot(rax, Heap::kNullValueRootIndex);
ager@chromium.org3e875802009-06-29 08:26:34 +00001686 exit.Branch(equal);
1687
1688 // Stack layout in body:
1689 // [iteration counter (smi)] <- slot 0
1690 // [length of array] <- slot 1
1691 // [FixedArray] <- slot 2
1692 // [Map or 0] <- slot 3
1693 // [Object] <- slot 4
1694
1695 // Check if enumerable is already a JSObject
1696 // rax: value to be iterated over
ager@chromium.org4af710e2009-09-15 12:20:11 +00001697 Condition is_smi = masm_->CheckSmi(rax);
1698 primitive.Branch(is_smi);
ager@chromium.org3e875802009-06-29 08:26:34 +00001699 __ CmpObjectType(rax, FIRST_JS_OBJECT_TYPE, rcx);
1700 jsobject.Branch(above_equal);
1701
1702 primitive.Bind();
1703 frame_->EmitPush(rax);
1704 frame_->InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION, 1);
1705 // function call returns the value in rax, which is where we want it below
1706
1707 jsobject.Bind();
1708 // Get the set of properties (as a FixedArray or Map).
1709 // rax: value to be iterated over
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001710 frame_->EmitPush(rax); // Push the object being iterated over.
ager@chromium.org3e875802009-06-29 08:26:34 +00001711
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001712
1713 // Check cache validity in generated code. This is a fast case for
1714 // the JSObject::IsSimpleEnum cache validity checks. If we cannot
1715 // guarantee cache validity, call the runtime system to check cache
1716 // validity or get the property names in a fixed array.
1717 JumpTarget call_runtime;
1718 JumpTarget loop(JumpTarget::BIDIRECTIONAL);
1719 JumpTarget check_prototype;
1720 JumpTarget use_cache;
1721 __ movq(rcx, rax);
1722 loop.Bind();
1723 // Check that there are no elements.
1724 __ movq(rdx, FieldOperand(rcx, JSObject::kElementsOffset));
1725 __ CompareRoot(rdx, Heap::kEmptyFixedArrayRootIndex);
1726 call_runtime.Branch(not_equal);
1727 // Check that instance descriptors are not empty so that we can
1728 // check for an enum cache. Leave the map in ebx for the subsequent
1729 // prototype load.
1730 __ movq(rbx, FieldOperand(rcx, HeapObject::kMapOffset));
1731 __ movq(rdx, FieldOperand(rbx, Map::kInstanceDescriptorsOffset));
1732 __ CompareRoot(rdx, Heap::kEmptyDescriptorArrayRootIndex);
1733 call_runtime.Branch(equal);
1734 // Check that there in an enum cache in the non-empty instance
1735 // descriptors. This is the case if the next enumeration index
1736 // field does not contain a smi.
1737 __ movq(rdx, FieldOperand(rdx, DescriptorArray::kEnumerationIndexOffset));
1738 is_smi = masm_->CheckSmi(rdx);
1739 call_runtime.Branch(is_smi);
1740 // For all objects but the receiver, check that the cache is empty.
1741 __ cmpq(rcx, rax);
1742 check_prototype.Branch(equal);
1743 __ movq(rdx, FieldOperand(rdx, DescriptorArray::kEnumCacheBridgeCacheOffset));
1744 __ CompareRoot(rdx, Heap::kEmptyFixedArrayRootIndex);
1745 call_runtime.Branch(not_equal);
1746 check_prototype.Bind();
1747 // Load the prototype from the map and loop if non-null.
1748 __ movq(rcx, FieldOperand(rbx, Map::kPrototypeOffset));
1749 __ CompareRoot(rcx, Heap::kNullValueRootIndex);
1750 loop.Branch(not_equal);
1751 // The enum cache is valid. Load the map of the object being
1752 // iterated over and use the cache for the iteration.
1753 __ movq(rax, FieldOperand(rax, HeapObject::kMapOffset));
1754 use_cache.Jump();
1755
1756 call_runtime.Bind();
1757 // Call the runtime to get the property names for the object.
ager@chromium.org3e875802009-06-29 08:26:34 +00001758 frame_->EmitPush(rax); // push the Object (slot 4) for the runtime call
1759 frame_->CallRuntime(Runtime::kGetPropertyNamesFast, 1);
1760
1761 // If we got a Map, we can do a fast modification check.
1762 // Otherwise, we got a FixedArray, and we have to do a slow check.
1763 // rax: map or fixed array (result from call to
1764 // Runtime::kGetPropertyNamesFast)
1765 __ movq(rdx, rax);
1766 __ movq(rcx, FieldOperand(rdx, HeapObject::kMapOffset));
ager@chromium.org18ad94b2009-09-02 08:22:29 +00001767 __ CompareRoot(rcx, Heap::kMetaMapRootIndex);
ager@chromium.org3e875802009-06-29 08:26:34 +00001768 fixed_array.Branch(not_equal);
1769
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001770 use_cache.Bind();
ager@chromium.org3e875802009-06-29 08:26:34 +00001771 // Get enum cache
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001772 // rax: map (either the result from a call to
1773 // Runtime::kGetPropertyNamesFast or has been fetched directly from
1774 // the object)
ager@chromium.org3e875802009-06-29 08:26:34 +00001775 __ movq(rcx, rax);
1776 __ movq(rcx, FieldOperand(rcx, Map::kInstanceDescriptorsOffset));
1777 // Get the bridge array held in the enumeration index field.
1778 __ movq(rcx, FieldOperand(rcx, DescriptorArray::kEnumerationIndexOffset));
1779 // Get the cache from the bridge array.
1780 __ movq(rdx, FieldOperand(rcx, DescriptorArray::kEnumCacheBridgeCacheOffset));
1781
1782 frame_->EmitPush(rax); // <- slot 3
1783 frame_->EmitPush(rdx); // <- slot 2
ager@chromium.org4af710e2009-09-15 12:20:11 +00001784 __ movl(rax, FieldOperand(rdx, FixedArray::kLengthOffset));
1785 __ Integer32ToSmi(rax, rax);
ager@chromium.org3e875802009-06-29 08:26:34 +00001786 frame_->EmitPush(rax); // <- slot 1
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001787 frame_->EmitPush(Smi::FromInt(0)); // <- slot 0
ager@chromium.org3e875802009-06-29 08:26:34 +00001788 entry.Jump();
1789
1790 fixed_array.Bind();
1791 // rax: fixed array (result from call to Runtime::kGetPropertyNamesFast)
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001792 frame_->EmitPush(Smi::FromInt(0)); // <- slot 3
ager@chromium.org3e875802009-06-29 08:26:34 +00001793 frame_->EmitPush(rax); // <- slot 2
1794
1795 // Push the length of the array and the initial index onto the stack.
ager@chromium.org4af710e2009-09-15 12:20:11 +00001796 __ movl(rax, FieldOperand(rax, FixedArray::kLengthOffset));
1797 __ Integer32ToSmi(rax, rax);
ager@chromium.org3e875802009-06-29 08:26:34 +00001798 frame_->EmitPush(rax); // <- slot 1
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001799 frame_->EmitPush(Smi::FromInt(0)); // <- slot 0
ager@chromium.org3e875802009-06-29 08:26:34 +00001800
1801 // Condition.
1802 entry.Bind();
1803 // Grab the current frame's height for the break and continue
1804 // targets only after all the state is pushed on the frame.
1805 node->break_target()->set_direction(JumpTarget::FORWARD_ONLY);
1806 node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY);
1807
1808 __ movq(rax, frame_->ElementAt(0)); // load the current count
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001809 __ SmiCompare(frame_->ElementAt(1), rax); // compare to the array length
1810 node->break_target()->Branch(below_equal);
ager@chromium.org3e875802009-06-29 08:26:34 +00001811
1812 // Get the i'th entry of the array.
1813 __ movq(rdx, frame_->ElementAt(2));
ager@chromium.org4af710e2009-09-15 12:20:11 +00001814 SmiIndex index = masm_->SmiToIndex(rbx, rax, kPointerSizeLog2);
1815 __ movq(rbx,
1816 FieldOperand(rdx, index.reg, index.scale, FixedArray::kHeaderSize));
ager@chromium.org3e875802009-06-29 08:26:34 +00001817
1818 // Get the expected map from the stack or a zero map in the
1819 // permanent slow case rax: current iteration count rbx: i'th entry
1820 // of the enum cache
1821 __ movq(rdx, frame_->ElementAt(3));
1822 // Check if the expected map still matches that of the enumerable.
1823 // If not, we have to filter the key.
1824 // rax: current iteration count
1825 // rbx: i'th entry of the enum cache
1826 // rdx: expected map value
1827 __ movq(rcx, frame_->ElementAt(4));
1828 __ movq(rcx, FieldOperand(rcx, HeapObject::kMapOffset));
1829 __ cmpq(rcx, rdx);
1830 end_del_check.Branch(equal);
1831
1832 // Convert the entry to a string (or null if it isn't a property anymore).
1833 frame_->EmitPush(frame_->ElementAt(4)); // push enumerable
1834 frame_->EmitPush(rbx); // push entry
1835 frame_->InvokeBuiltin(Builtins::FILTER_KEY, CALL_FUNCTION, 2);
1836 __ movq(rbx, rax);
1837
1838 // If the property has been removed while iterating, we just skip it.
ager@chromium.org18ad94b2009-09-02 08:22:29 +00001839 __ CompareRoot(rbx, Heap::kNullValueRootIndex);
ager@chromium.org3e875802009-06-29 08:26:34 +00001840 node->continue_target()->Branch(equal);
1841
1842 end_del_check.Bind();
1843 // Store the entry in the 'each' expression and take another spin in the
1844 // loop. rdx: i'th entry of the enum cache (or string there of)
1845 frame_->EmitPush(rbx);
1846 { Reference each(this, node->each());
1847 // Loading a reference may leave the frame in an unspilled state.
1848 frame_->SpillAll();
1849 if (!each.is_illegal()) {
1850 if (each.size() > 0) {
1851 frame_->EmitPush(frame_->ElementAt(each.size()));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001852 each.SetValue(NOT_CONST_INIT);
1853 frame_->Drop(2); // Drop the original and the copy of the element.
1854 } else {
1855 // If the reference has size zero then we can use the value below
1856 // the reference as if it were above the reference, instead of pushing
1857 // a new copy of it above the reference.
1858 each.SetValue(NOT_CONST_INIT);
1859 frame_->Drop(); // Drop the original of the element.
ager@chromium.org3e875802009-06-29 08:26:34 +00001860 }
1861 }
1862 }
1863 // Unloading a reference may leave the frame in an unspilled state.
1864 frame_->SpillAll();
1865
ager@chromium.org3e875802009-06-29 08:26:34 +00001866 // Body.
1867 CheckStack(); // TODO(1222600): ignore if body contains calls.
1868 VisitAndSpill(node->body());
1869
1870 // Next. Reestablish a spilled frame in case we are coming here via
1871 // a continue in the body.
1872 node->continue_target()->Bind();
1873 frame_->SpillAll();
1874 frame_->EmitPop(rax);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001875 __ SmiAddConstant(rax, rax, Smi::FromInt(1));
ager@chromium.org3e875802009-06-29 08:26:34 +00001876 frame_->EmitPush(rax);
1877 entry.Jump();
1878
1879 // Cleanup. No need to spill because VirtualFrame::Drop is safe for
1880 // any frame.
1881 node->break_target()->Bind();
1882 frame_->Drop(5);
1883
1884 // Exit.
1885 exit.Bind();
1886
1887 node->continue_target()->Unuse();
1888 node->break_target()->Unuse();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001889}
1890
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001891void CodeGenerator::VisitTryCatchStatement(TryCatchStatement* node) {
ager@chromium.org3e875802009-06-29 08:26:34 +00001892 ASSERT(!in_spilled_code());
1893 VirtualFrame::SpilledScope spilled_scope;
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001894 Comment cmnt(masm_, "[ TryCatchStatement");
ager@chromium.org3e875802009-06-29 08:26:34 +00001895 CodeForStatementPosition(node);
1896
1897 JumpTarget try_block;
1898 JumpTarget exit;
1899
1900 try_block.Call();
1901 // --- Catch block ---
1902 frame_->EmitPush(rax);
1903
1904 // Store the caught exception in the catch variable.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001905 Variable* catch_var = node->catch_var()->var();
1906 ASSERT(catch_var != NULL && catch_var->slot() != NULL);
1907 StoreToSlot(catch_var->slot(), NOT_CONST_INIT);
ager@chromium.org3e875802009-06-29 08:26:34 +00001908
1909 // Remove the exception from the stack.
1910 frame_->Drop();
1911
1912 VisitStatementsAndSpill(node->catch_block()->statements());
1913 if (has_valid_frame()) {
1914 exit.Jump();
1915 }
1916
1917
1918 // --- Try block ---
1919 try_block.Bind();
1920
1921 frame_->PushTryHandler(TRY_CATCH_HANDLER);
1922 int handler_height = frame_->height();
1923
1924 // Shadow the jump targets for all escapes from the try block, including
1925 // returns. During shadowing, the original target is hidden as the
1926 // ShadowTarget and operations on the original actually affect the
1927 // shadowing target.
1928 //
1929 // We should probably try to unify the escaping targets and the return
1930 // target.
1931 int nof_escapes = node->escaping_targets()->length();
1932 List<ShadowTarget*> shadows(1 + nof_escapes);
1933
1934 // Add the shadow target for the function return.
1935 static const int kReturnShadowIndex = 0;
1936 shadows.Add(new ShadowTarget(&function_return_));
1937 bool function_return_was_shadowed = function_return_is_shadowed_;
1938 function_return_is_shadowed_ = true;
1939 ASSERT(shadows[kReturnShadowIndex]->other_target() == &function_return_);
1940
1941 // Add the remaining shadow targets.
1942 for (int i = 0; i < nof_escapes; i++) {
1943 shadows.Add(new ShadowTarget(node->escaping_targets()->at(i)));
1944 }
1945
1946 // Generate code for the statements in the try block.
1947 VisitStatementsAndSpill(node->try_block()->statements());
1948
1949 // Stop the introduced shadowing and count the number of required unlinks.
1950 // After shadowing stops, the original targets are unshadowed and the
1951 // ShadowTargets represent the formerly shadowing targets.
1952 bool has_unlinks = false;
1953 for (int i = 0; i < shadows.length(); i++) {
1954 shadows[i]->StopShadowing();
1955 has_unlinks = has_unlinks || shadows[i]->is_linked();
1956 }
1957 function_return_is_shadowed_ = function_return_was_shadowed;
1958
1959 // Get an external reference to the handler address.
1960 ExternalReference handler_address(Top::k_handler_address);
1961
1962 // Make sure that there's nothing left on the stack above the
1963 // handler structure.
1964 if (FLAG_debug_code) {
1965 __ movq(kScratchRegister, handler_address);
1966 __ cmpq(rsp, Operand(kScratchRegister, 0));
1967 __ Assert(equal, "stack pointer should point to top handler");
1968 }
1969
1970 // If we can fall off the end of the try block, unlink from try chain.
1971 if (has_valid_frame()) {
1972 // The next handler address is on top of the frame. Unlink from
1973 // the handler list and drop the rest of this handler from the
1974 // frame.
1975 ASSERT(StackHandlerConstants::kNextOffset == 0);
1976 __ movq(kScratchRegister, handler_address);
1977 frame_->EmitPop(Operand(kScratchRegister, 0));
1978 frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1);
1979 if (has_unlinks) {
1980 exit.Jump();
1981 }
1982 }
1983
1984 // Generate unlink code for the (formerly) shadowing targets that
1985 // have been jumped to. Deallocate each shadow target.
1986 Result return_value;
1987 for (int i = 0; i < shadows.length(); i++) {
1988 if (shadows[i]->is_linked()) {
1989 // Unlink from try chain; be careful not to destroy the TOS if
1990 // there is one.
1991 if (i == kReturnShadowIndex) {
1992 shadows[i]->Bind(&return_value);
1993 return_value.ToRegister(rax);
1994 } else {
1995 shadows[i]->Bind();
1996 }
1997 // Because we can be jumping here (to spilled code) from
1998 // unspilled code, we need to reestablish a spilled frame at
1999 // this block.
2000 frame_->SpillAll();
2001
2002 // Reload sp from the top handler, because some statements that we
2003 // break from (eg, for...in) may have left stuff on the stack.
2004 __ movq(kScratchRegister, handler_address);
2005 __ movq(rsp, Operand(kScratchRegister, 0));
2006 frame_->Forget(frame_->height() - handler_height);
2007
2008 ASSERT(StackHandlerConstants::kNextOffset == 0);
2009 __ movq(kScratchRegister, handler_address);
2010 frame_->EmitPop(Operand(kScratchRegister, 0));
2011 frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1);
2012
2013 if (i == kReturnShadowIndex) {
2014 if (!function_return_is_shadowed_) frame_->PrepareForReturn();
2015 shadows[i]->other_target()->Jump(&return_value);
2016 } else {
2017 shadows[i]->other_target()->Jump();
2018 }
2019 }
2020 }
2021
2022 exit.Bind();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002023}
2024
ager@chromium.org3e875802009-06-29 08:26:34 +00002025
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002026void CodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* node) {
ager@chromium.org3e875802009-06-29 08:26:34 +00002027 ASSERT(!in_spilled_code());
2028 VirtualFrame::SpilledScope spilled_scope;
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002029 Comment cmnt(masm_, "[ TryFinallyStatement");
ager@chromium.org3e875802009-06-29 08:26:34 +00002030 CodeForStatementPosition(node);
2031
2032 // State: Used to keep track of reason for entering the finally
2033 // block. Should probably be extended to hold information for
2034 // break/continue from within the try block.
2035 enum { FALLING, THROWING, JUMPING };
2036
2037 JumpTarget try_block;
2038 JumpTarget finally_block;
2039
2040 try_block.Call();
2041
2042 frame_->EmitPush(rax);
2043 // In case of thrown exceptions, this is where we continue.
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002044 __ Move(rcx, Smi::FromInt(THROWING));
ager@chromium.org3e875802009-06-29 08:26:34 +00002045 finally_block.Jump();
2046
2047 // --- Try block ---
2048 try_block.Bind();
2049
2050 frame_->PushTryHandler(TRY_FINALLY_HANDLER);
2051 int handler_height = frame_->height();
2052
2053 // Shadow the jump targets for all escapes from the try block, including
2054 // returns. During shadowing, the original target is hidden as the
2055 // ShadowTarget and operations on the original actually affect the
2056 // shadowing target.
2057 //
2058 // We should probably try to unify the escaping targets and the return
2059 // target.
2060 int nof_escapes = node->escaping_targets()->length();
2061 List<ShadowTarget*> shadows(1 + nof_escapes);
2062
2063 // Add the shadow target for the function return.
2064 static const int kReturnShadowIndex = 0;
2065 shadows.Add(new ShadowTarget(&function_return_));
2066 bool function_return_was_shadowed = function_return_is_shadowed_;
2067 function_return_is_shadowed_ = true;
2068 ASSERT(shadows[kReturnShadowIndex]->other_target() == &function_return_);
2069
2070 // Add the remaining shadow targets.
2071 for (int i = 0; i < nof_escapes; i++) {
2072 shadows.Add(new ShadowTarget(node->escaping_targets()->at(i)));
2073 }
2074
2075 // Generate code for the statements in the try block.
2076 VisitStatementsAndSpill(node->try_block()->statements());
2077
2078 // Stop the introduced shadowing and count the number of required unlinks.
2079 // After shadowing stops, the original targets are unshadowed and the
2080 // ShadowTargets represent the formerly shadowing targets.
2081 int nof_unlinks = 0;
2082 for (int i = 0; i < shadows.length(); i++) {
2083 shadows[i]->StopShadowing();
2084 if (shadows[i]->is_linked()) nof_unlinks++;
2085 }
2086 function_return_is_shadowed_ = function_return_was_shadowed;
2087
2088 // Get an external reference to the handler address.
2089 ExternalReference handler_address(Top::k_handler_address);
2090
2091 // If we can fall off the end of the try block, unlink from the try
2092 // chain and set the state on the frame to FALLING.
2093 if (has_valid_frame()) {
2094 // The next handler address is on top of the frame.
2095 ASSERT(StackHandlerConstants::kNextOffset == 0);
2096 __ movq(kScratchRegister, handler_address);
2097 frame_->EmitPop(Operand(kScratchRegister, 0));
2098 frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1);
2099
2100 // Fake a top of stack value (unneeded when FALLING) and set the
2101 // state in ecx, then jump around the unlink blocks if any.
ager@chromium.org18ad94b2009-09-02 08:22:29 +00002102 frame_->EmitPush(Heap::kUndefinedValueRootIndex);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002103 __ Move(rcx, Smi::FromInt(FALLING));
ager@chromium.org3e875802009-06-29 08:26:34 +00002104 if (nof_unlinks > 0) {
2105 finally_block.Jump();
2106 }
2107 }
2108
2109 // Generate code to unlink and set the state for the (formerly)
2110 // shadowing targets that have been jumped to.
2111 for (int i = 0; i < shadows.length(); i++) {
2112 if (shadows[i]->is_linked()) {
2113 // If we have come from the shadowed return, the return value is
2114 // on the virtual frame. We must preserve it until it is
2115 // pushed.
2116 if (i == kReturnShadowIndex) {
2117 Result return_value;
2118 shadows[i]->Bind(&return_value);
2119 return_value.ToRegister(rax);
2120 } else {
2121 shadows[i]->Bind();
2122 }
2123 // Because we can be jumping here (to spilled code) from
2124 // unspilled code, we need to reestablish a spilled frame at
2125 // this block.
2126 frame_->SpillAll();
2127
2128 // Reload sp from the top handler, because some statements that
2129 // we break from (eg, for...in) may have left stuff on the
2130 // stack.
2131 __ movq(kScratchRegister, handler_address);
2132 __ movq(rsp, Operand(kScratchRegister, 0));
2133 frame_->Forget(frame_->height() - handler_height);
2134
2135 // Unlink this handler and drop it from the frame.
2136 ASSERT(StackHandlerConstants::kNextOffset == 0);
2137 __ movq(kScratchRegister, handler_address);
2138 frame_->EmitPop(Operand(kScratchRegister, 0));
2139 frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1);
2140
2141 if (i == kReturnShadowIndex) {
2142 // If this target shadowed the function return, materialize
2143 // the return value on the stack.
2144 frame_->EmitPush(rax);
2145 } else {
2146 // Fake TOS for targets that shadowed breaks and continues.
ager@chromium.org18ad94b2009-09-02 08:22:29 +00002147 frame_->EmitPush(Heap::kUndefinedValueRootIndex);
ager@chromium.org3e875802009-06-29 08:26:34 +00002148 }
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002149 __ Move(rcx, Smi::FromInt(JUMPING + i));
ager@chromium.org3e875802009-06-29 08:26:34 +00002150 if (--nof_unlinks > 0) {
2151 // If this is not the last unlink block, jump around the next.
2152 finally_block.Jump();
2153 }
2154 }
2155 }
2156
2157 // --- Finally block ---
2158 finally_block.Bind();
2159
2160 // Push the state on the stack.
2161 frame_->EmitPush(rcx);
2162
2163 // We keep two elements on the stack - the (possibly faked) result
2164 // and the state - while evaluating the finally block.
2165 //
2166 // Generate code for the statements in the finally block.
2167 VisitStatementsAndSpill(node->finally_block()->statements());
2168
2169 if (has_valid_frame()) {
2170 // Restore state and return value or faked TOS.
2171 frame_->EmitPop(rcx);
2172 frame_->EmitPop(rax);
2173 }
2174
2175 // Generate code to jump to the right destination for all used
2176 // formerly shadowing targets. Deallocate each shadow target.
2177 for (int i = 0; i < shadows.length(); i++) {
2178 if (has_valid_frame() && shadows[i]->is_bound()) {
2179 BreakTarget* original = shadows[i]->other_target();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002180 __ SmiCompare(rcx, Smi::FromInt(JUMPING + i));
ager@chromium.org3e875802009-06-29 08:26:34 +00002181 if (i == kReturnShadowIndex) {
2182 // The return value is (already) in rax.
2183 Result return_value = allocator_->Allocate(rax);
2184 ASSERT(return_value.is_valid());
2185 if (function_return_is_shadowed_) {
2186 original->Branch(equal, &return_value);
2187 } else {
2188 // Branch around the preparation for return which may emit
2189 // code.
2190 JumpTarget skip;
2191 skip.Branch(not_equal);
2192 frame_->PrepareForReturn();
2193 original->Jump(&return_value);
2194 skip.Bind();
2195 }
2196 } else {
2197 original->Branch(equal);
2198 }
2199 }
2200 }
2201
2202 if (has_valid_frame()) {
2203 // Check if we need to rethrow the exception.
2204 JumpTarget exit;
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002205 __ SmiCompare(rcx, Smi::FromInt(THROWING));
ager@chromium.org3e875802009-06-29 08:26:34 +00002206 exit.Branch(not_equal);
2207
2208 // Rethrow exception.
2209 frame_->EmitPush(rax); // undo pop from above
2210 frame_->CallRuntime(Runtime::kReThrow, 1);
2211
2212 // Done.
2213 exit.Bind();
2214 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002215}
2216
ager@chromium.org3e875802009-06-29 08:26:34 +00002217
2218void CodeGenerator::VisitDebuggerStatement(DebuggerStatement* node) {
2219 ASSERT(!in_spilled_code());
2220 Comment cmnt(masm_, "[ DebuggerStatement");
2221 CodeForStatementPosition(node);
2222#ifdef ENABLE_DEBUGGER_SUPPORT
2223 // Spill everything, even constants, to the frame.
2224 frame_->SpillAll();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002225
ager@chromium.org5c838252010-02-19 08:53:10 +00002226 frame_->DebugBreak();
ager@chromium.org3e875802009-06-29 08:26:34 +00002227 // Ignore the return value.
2228#endif
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002229}
2230
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002231
2232void CodeGenerator::InstantiateBoilerplate(Handle<JSFunction> boilerplate) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002233 ASSERT(boilerplate->IsBoilerplate());
2234
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002235 // The inevitable call will sync frame elements to memory anyway, so
2236 // we do it eagerly to allow us to push the arguments directly into
2237 // place.
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002238 frame_->SyncRange(0, frame_->element_count() - 1);
2239
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002240 // Use the fast case closure allocation code that allocates in new
2241 // space for nested functions that don't need literals cloning.
2242 if (scope()->is_function_scope() && boilerplate->NumberOfLiterals() == 0) {
2243 FastNewClosureStub stub;
2244 frame_->Push(boilerplate);
2245 Result answer = frame_->CallStub(&stub, 1);
2246 frame_->Push(&answer);
2247 } else {
2248 // Call the runtime to instantiate the function boilerplate
2249 // object.
2250 frame_->EmitPush(rsi);
2251 frame_->EmitPush(boilerplate);
2252 Result result = frame_->CallRuntime(Runtime::kNewClosure, 2);
2253 frame_->Push(&result);
2254 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002255}
2256
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002257
2258void CodeGenerator::VisitFunctionLiteral(FunctionLiteral* node) {
2259 Comment cmnt(masm_, "[ FunctionLiteral");
2260
2261 // Build the function boilerplate and instantiate it.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002262 Handle<JSFunction> boilerplate =
ager@chromium.org5c838252010-02-19 08:53:10 +00002263 Compiler::BuildBoilerplate(node, script(), this);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002264 // Check for stack-overflow exception.
2265 if (HasStackOverflow()) return;
2266 InstantiateBoilerplate(boilerplate);
2267}
2268
2269
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002270void CodeGenerator::VisitFunctionBoilerplateLiteral(
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002271 FunctionBoilerplateLiteral* node) {
2272 Comment cmnt(masm_, "[ FunctionBoilerplateLiteral");
2273 InstantiateBoilerplate(node->boilerplate());
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002274}
2275
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002276
2277void CodeGenerator::VisitConditional(Conditional* node) {
2278 Comment cmnt(masm_, "[ Conditional");
2279 JumpTarget then;
2280 JumpTarget else_;
2281 JumpTarget exit;
2282 ControlDestination dest(&then, &else_, true);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002283 LoadCondition(node->condition(), &dest, true);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002284
2285 if (dest.false_was_fall_through()) {
2286 // The else target was bound, so we compile the else part first.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002287 Load(node->else_expression());
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002288
2289 if (then.is_linked()) {
2290 exit.Jump();
2291 then.Bind();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002292 Load(node->then_expression());
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002293 }
2294 } else {
2295 // The then target was bound, so we compile the then part first.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002296 Load(node->then_expression());
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002297
2298 if (else_.is_linked()) {
2299 exit.Jump();
2300 else_.Bind();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002301 Load(node->else_expression());
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002302 }
2303 }
2304
2305 exit.Bind();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002306}
2307
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002308
2309void CodeGenerator::VisitSlot(Slot* node) {
2310 Comment cmnt(masm_, "[ Slot");
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002311 LoadFromSlotCheckForArguments(node, NOT_INSIDE_TYPEOF);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002312}
2313
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002314
2315void CodeGenerator::VisitVariableProxy(VariableProxy* node) {
2316 Comment cmnt(masm_, "[ VariableProxy");
2317 Variable* var = node->var();
2318 Expression* expr = var->rewrite();
2319 if (expr != NULL) {
2320 Visit(expr);
2321 } else {
2322 ASSERT(var->is_global());
2323 Reference ref(this, node);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002324 ref.GetValue();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002325 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002326}
2327
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002328
2329void CodeGenerator::VisitLiteral(Literal* node) {
2330 Comment cmnt(masm_, "[ Literal");
2331 frame_->Push(node->handle());
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002332}
2333
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002334
2335// Materialize the regexp literal 'node' in the literals array
2336// 'literals' of the function. Leave the regexp boilerplate in
2337// 'boilerplate'.
2338class DeferredRegExpLiteral: public DeferredCode {
2339 public:
2340 DeferredRegExpLiteral(Register boilerplate,
2341 Register literals,
2342 RegExpLiteral* node)
2343 : boilerplate_(boilerplate), literals_(literals), node_(node) {
2344 set_comment("[ DeferredRegExpLiteral");
2345 }
2346
2347 void Generate();
2348
2349 private:
2350 Register boilerplate_;
2351 Register literals_;
2352 RegExpLiteral* node_;
2353};
2354
2355
2356void DeferredRegExpLiteral::Generate() {
2357 // Since the entry is undefined we call the runtime system to
2358 // compute the literal.
2359 // Literal array (0).
2360 __ push(literals_);
2361 // Literal index (1).
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002362 __ Push(Smi::FromInt(node_->literal_index()));
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002363 // RegExp pattern (2).
2364 __ Push(node_->pattern());
2365 // RegExp flags (3).
2366 __ Push(node_->flags());
2367 __ CallRuntime(Runtime::kMaterializeRegExpLiteral, 4);
2368 if (!boilerplate_.is(rax)) __ movq(boilerplate_, rax);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002369}
2370
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002371
2372void CodeGenerator::VisitRegExpLiteral(RegExpLiteral* node) {
2373 Comment cmnt(masm_, "[ RegExp Literal");
2374
2375 // Retrieve the literals array and check the allocated entry. Begin
2376 // with a writable copy of the function of this activation in a
2377 // register.
2378 frame_->PushFunction();
2379 Result literals = frame_->Pop();
2380 literals.ToRegister();
2381 frame_->Spill(literals.reg());
2382
2383 // Load the literals array of the function.
2384 __ movq(literals.reg(),
2385 FieldOperand(literals.reg(), JSFunction::kLiteralsOffset));
2386
2387 // Load the literal at the ast saved index.
2388 Result boilerplate = allocator_->Allocate();
2389 ASSERT(boilerplate.is_valid());
2390 int literal_offset =
2391 FixedArray::kHeaderSize + node->literal_index() * kPointerSize;
2392 __ movq(boilerplate.reg(), FieldOperand(literals.reg(), literal_offset));
2393
2394 // Check whether we need to materialize the RegExp object. If so,
2395 // jump to the deferred code passing the literals array.
2396 DeferredRegExpLiteral* deferred =
2397 new DeferredRegExpLiteral(boilerplate.reg(), literals.reg(), node);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00002398 __ CompareRoot(boilerplate.reg(), Heap::kUndefinedValueRootIndex);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002399 deferred->Branch(equal);
2400 deferred->BindExit();
2401 literals.Unuse();
2402
2403 // Push the boilerplate object.
2404 frame_->Push(&boilerplate);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002405}
2406
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002407
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002408void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) {
2409 Comment cmnt(masm_, "[ ObjectLiteral");
2410
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002411 // Load a writable copy of the function of this activation in a
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002412 // register.
2413 frame_->PushFunction();
2414 Result literals = frame_->Pop();
2415 literals.ToRegister();
2416 frame_->Spill(literals.reg());
2417
2418 // Load the literals array of the function.
2419 __ movq(literals.reg(),
2420 FieldOperand(literals.reg(), JSFunction::kLiteralsOffset));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002421 // Literal array.
2422 frame_->Push(&literals);
2423 // Literal index.
2424 frame_->Push(Smi::FromInt(node->literal_index()));
2425 // Constant properties.
2426 frame_->Push(node->constant_properties());
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00002427 // Should the object literal have fast elements?
2428 frame_->Push(Smi::FromInt(node->fast_elements() ? 1 : 0));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002429 Result clone;
2430 if (node->depth() > 1) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00002431 clone = frame_->CallRuntime(Runtime::kCreateObjectLiteral, 4);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002432 } else {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00002433 clone = frame_->CallRuntime(Runtime::kCreateObjectLiteralShallow, 4);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002434 }
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002435 frame_->Push(&clone);
2436
2437 for (int i = 0; i < node->properties()->length(); i++) {
2438 ObjectLiteral::Property* property = node->properties()->at(i);
2439 switch (property->kind()) {
2440 case ObjectLiteral::Property::CONSTANT:
2441 break;
2442 case ObjectLiteral::Property::MATERIALIZED_LITERAL:
2443 if (CompileTimeValue::IsCompileTimeValue(property->value())) break;
2444 // else fall through.
2445 case ObjectLiteral::Property::COMPUTED: {
2446 Handle<Object> key(property->key()->handle());
2447 if (key->IsSymbol()) {
2448 // Duplicate the object as the IC receiver.
2449 frame_->Dup();
2450 Load(property->value());
2451 frame_->Push(key);
2452 Result ignored = frame_->CallStoreIC();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002453 break;
2454 }
2455 // Fall through
2456 }
2457 case ObjectLiteral::Property::PROTOTYPE: {
2458 // Duplicate the object as an argument to the runtime call.
2459 frame_->Dup();
2460 Load(property->key());
2461 Load(property->value());
2462 Result ignored = frame_->CallRuntime(Runtime::kSetProperty, 3);
2463 // Ignore the result.
2464 break;
2465 }
2466 case ObjectLiteral::Property::SETTER: {
2467 // Duplicate the object as an argument to the runtime call.
2468 frame_->Dup();
2469 Load(property->key());
2470 frame_->Push(Smi::FromInt(1));
2471 Load(property->value());
2472 Result ignored = frame_->CallRuntime(Runtime::kDefineAccessor, 4);
2473 // Ignore the result.
2474 break;
2475 }
2476 case ObjectLiteral::Property::GETTER: {
2477 // Duplicate the object as an argument to the runtime call.
2478 frame_->Dup();
2479 Load(property->key());
2480 frame_->Push(Smi::FromInt(0));
2481 Load(property->value());
2482 Result ignored = frame_->CallRuntime(Runtime::kDefineAccessor, 4);
2483 // Ignore the result.
2484 break;
2485 }
2486 default: UNREACHABLE();
2487 }
2488 }
2489}
2490
2491
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002492void CodeGenerator::VisitArrayLiteral(ArrayLiteral* node) {
2493 Comment cmnt(masm_, "[ ArrayLiteral");
2494
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002495 // Load a writable copy of the function of this activation in a
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002496 // register.
2497 frame_->PushFunction();
2498 Result literals = frame_->Pop();
2499 literals.ToRegister();
2500 frame_->Spill(literals.reg());
2501
2502 // Load the literals array of the function.
2503 __ movq(literals.reg(),
2504 FieldOperand(literals.reg(), JSFunction::kLiteralsOffset));
ager@chromium.org5c838252010-02-19 08:53:10 +00002505
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002506 frame_->Push(&literals);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002507 frame_->Push(Smi::FromInt(node->literal_index()));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002508 frame_->Push(node->constant_elements());
ager@chromium.org5c838252010-02-19 08:53:10 +00002509 int length = node->values()->length();
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002510 Result clone;
2511 if (node->depth() > 1) {
2512 clone = frame_->CallRuntime(Runtime::kCreateArrayLiteral, 3);
ager@chromium.org5c838252010-02-19 08:53:10 +00002513 } else if (length > FastCloneShallowArrayStub::kMaximumLength) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002514 clone = frame_->CallRuntime(Runtime::kCreateArrayLiteralShallow, 3);
ager@chromium.org5c838252010-02-19 08:53:10 +00002515 } else {
2516 FastCloneShallowArrayStub stub(length);
2517 clone = frame_->CallStub(&stub, 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002518 }
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002519 frame_->Push(&clone);
2520
2521 // Generate code to set the elements in the array that are not
2522 // literals.
2523 for (int i = 0; i < node->values()->length(); i++) {
2524 Expression* value = node->values()->at(i);
2525
2526 // If value is a literal the property value is already set in the
2527 // boilerplate object.
2528 if (value->AsLiteral() != NULL) continue;
2529 // If value is a materialized literal the property value is already set
2530 // in the boilerplate object if it is simple.
2531 if (CompileTimeValue::IsCompileTimeValue(value)) continue;
2532
2533 // The property must be set by generated code.
2534 Load(value);
2535
2536 // Get the property value off the stack.
2537 Result prop_value = frame_->Pop();
2538 prop_value.ToRegister();
2539
2540 // Fetch the array literal while leaving a copy on the stack and
2541 // use it to get the elements array.
2542 frame_->Dup();
2543 Result elements = frame_->Pop();
2544 elements.ToRegister();
2545 frame_->Spill(elements.reg());
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00002546 // Get the elements FixedArray.
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002547 __ movq(elements.reg(),
2548 FieldOperand(elements.reg(), JSObject::kElementsOffset));
2549
2550 // Write to the indexed properties array.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00002551 int offset = i * kPointerSize + FixedArray::kHeaderSize;
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002552 __ movq(FieldOperand(elements.reg(), offset), prop_value.reg());
2553
2554 // Update the write barrier for the array address.
2555 frame_->Spill(prop_value.reg()); // Overwritten by the write barrier.
2556 Result scratch = allocator_->Allocate();
2557 ASSERT(scratch.is_valid());
2558 __ RecordWrite(elements.reg(), offset, prop_value.reg(), scratch.reg());
2559 }
2560}
2561
2562
ager@chromium.org3e875802009-06-29 08:26:34 +00002563void CodeGenerator::VisitCatchExtensionObject(CatchExtensionObject* node) {
2564 ASSERT(!in_spilled_code());
2565 // Call runtime routine to allocate the catch extension object and
2566 // assign the exception value to the catch variable.
2567 Comment cmnt(masm_, "[ CatchExtensionObject");
2568 Load(node->key());
2569 Load(node->value());
2570 Result result =
2571 frame_->CallRuntime(Runtime::kCreateCatchExtensionObject, 2);
2572 frame_->Push(&result);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002573}
2574
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002575
2576void CodeGenerator::VisitAssignment(Assignment* node) {
2577 Comment cmnt(masm_, "[ Assignment");
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002578
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002579 { Reference target(this, node->target(), node->is_compound());
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002580 if (target.is_illegal()) {
2581 // Fool the virtual frame into thinking that we left the assignment's
2582 // value on the frame.
2583 frame_->Push(Smi::FromInt(0));
2584 return;
2585 }
2586 Variable* var = node->target()->AsVariableProxy()->AsVariable();
2587
2588 if (node->starts_initialization_block()) {
2589 ASSERT(target.type() == Reference::NAMED ||
2590 target.type() == Reference::KEYED);
2591 // Change to slow case in the beginning of an initialization
2592 // block to avoid the quadratic behavior of repeatedly adding
2593 // fast properties.
2594
2595 // The receiver is the argument to the runtime call. It is the
2596 // first value pushed when the reference was loaded to the
2597 // frame.
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002598 frame_->PushElementAt(target.size() - 1);
2599 Result ignored = frame_->CallRuntime(Runtime::kToSlowProperties, 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002600 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002601 if (node->ends_initialization_block()) {
2602 // Add an extra copy of the receiver to the frame, so that it can be
2603 // converted back to fast case after the assignment.
2604 ASSERT(target.type() == Reference::NAMED ||
2605 target.type() == Reference::KEYED);
2606 if (target.type() == Reference::NAMED) {
2607 frame_->Dup();
2608 // Dup target receiver on stack.
2609 } else {
2610 ASSERT(target.type() == Reference::KEYED);
2611 Result temp = frame_->Pop();
2612 frame_->Dup();
2613 frame_->Push(&temp);
2614 }
2615 }
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002616 if (node->op() == Token::ASSIGN ||
2617 node->op() == Token::INIT_VAR ||
2618 node->op() == Token::INIT_CONST) {
2619 Load(node->value());
2620
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002621 } else { // Assignment is a compound assignment.
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002622 Literal* literal = node->value()->AsLiteral();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002623 bool overwrite_value =
2624 (node->value()->AsBinaryOperation() != NULL &&
2625 node->value()->AsBinaryOperation()->ResultOverwriteAllowed());
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002626 Variable* right_var = node->value()->AsVariableProxy()->AsVariable();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002627 // There are two cases where the target is not read in the right hand
2628 // side, that are easy to test for: the right hand side is a literal,
2629 // or the right hand side is a different variable. TakeValue invalidates
2630 // the target, with an implicit promise that it will be written to again
2631 // before it is read.
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002632 if (literal != NULL || (right_var != NULL && right_var != var)) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002633 target.TakeValue();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002634 } else {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002635 target.GetValue();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002636 }
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002637 Load(node->value());
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002638 GenericBinaryOperation(node->binary_op(),
2639 node->type(),
2640 overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002641 }
2642
2643 if (var != NULL &&
2644 var->mode() == Variable::CONST &&
2645 node->op() != Token::INIT_VAR && node->op() != Token::INIT_CONST) {
2646 // Assignment ignored - leave the value on the stack.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002647 UnloadReference(&target);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002648 } else {
2649 CodeForSourcePosition(node->position());
2650 if (node->op() == Token::INIT_CONST) {
2651 // Dynamic constant initializations must use the function context
2652 // and initialize the actual constant declared. Dynamic variable
2653 // initializations are simply assignments and use SetValue.
2654 target.SetValue(CONST_INIT);
2655 } else {
2656 target.SetValue(NOT_CONST_INIT);
2657 }
2658 if (node->ends_initialization_block()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002659 ASSERT(target.type() == Reference::UNLOADED);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002660 // End of initialization block. Revert to fast case. The
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002661 // argument to the runtime call is the extra copy of the receiver,
2662 // which is below the value of the assignment.
2663 // Swap the receiver and the value of the assignment expression.
2664 Result lhs = frame_->Pop();
2665 Result receiver = frame_->Pop();
2666 frame_->Push(&lhs);
2667 frame_->Push(&receiver);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002668 Result ignored = frame_->CallRuntime(Runtime::kToFastProperties, 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002669 }
2670 }
2671 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002672}
2673
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002674
2675void CodeGenerator::VisitThrow(Throw* node) {
2676 Comment cmnt(masm_, "[ Throw");
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002677 Load(node->exception());
2678 Result result = frame_->CallRuntime(Runtime::kThrow, 1);
2679 frame_->Push(&result);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002680}
2681
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002682
2683void CodeGenerator::VisitProperty(Property* node) {
2684 Comment cmnt(masm_, "[ Property");
2685 Reference property(this, node);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002686 property.GetValue();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002687}
2688
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002689
2690void CodeGenerator::VisitCall(Call* node) {
2691 Comment cmnt(masm_, "[ Call");
2692
2693 ZoneList<Expression*>* args = node->arguments();
2694
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002695 // Check if the function is a variable or a property.
2696 Expression* function = node->expression();
2697 Variable* var = function->AsVariableProxy()->AsVariable();
2698 Property* property = function->AsProperty();
2699
2700 // ------------------------------------------------------------------------
2701 // Fast-case: Use inline caching.
2702 // ---
2703 // According to ECMA-262, section 11.2.3, page 44, the function to call
2704 // must be resolved after the arguments have been evaluated. The IC code
2705 // automatically handles this by loading the arguments before the function
2706 // is resolved in cache misses (this also holds for megamorphic calls).
2707 // ------------------------------------------------------------------------
2708
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00002709 if (var != NULL && var->is_possibly_eval()) {
2710 // ----------------------------------
2711 // JavaScript example: 'eval(arg)' // eval is not known to be shadowed
2712 // ----------------------------------
2713
2714 // In a call to eval, we first call %ResolvePossiblyDirectEval to
2715 // resolve the function we need to call and the receiver of the
2716 // call. Then we call the resolved function using the given
2717 // arguments.
2718
2719 // Prepare the stack for the call to the resolved function.
2720 Load(function);
2721
2722 // Allocate a frame slot for the receiver.
2723 frame_->Push(Factory::undefined_value());
2724 int arg_count = args->length();
2725 for (int i = 0; i < arg_count; i++) {
2726 Load(args->at(i));
2727 }
2728
2729 // Prepare the stack for the call to ResolvePossiblyDirectEval.
2730 frame_->PushElementAt(arg_count + 1);
2731 if (arg_count > 0) {
2732 frame_->PushElementAt(arg_count);
2733 } else {
2734 frame_->Push(Factory::undefined_value());
2735 }
2736
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002737 // Push the receiver.
2738 frame_->PushParameterAt(-1);
2739
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00002740 // Resolve the call.
2741 Result result =
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002742 frame_->CallRuntime(Runtime::kResolvePossiblyDirectEval, 3);
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00002743
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002744 // The runtime call returns a pair of values in rax (function) and
2745 // rdx (receiver). Touch up the stack with the right values.
2746 Result receiver = allocator_->Allocate(rdx);
2747 frame_->SetElementAt(arg_count + 1, &result);
2748 frame_->SetElementAt(arg_count, &receiver);
2749 receiver.Unuse();
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00002750
2751 // Call the function.
2752 CodeForSourcePosition(node->position());
2753 InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002754 CallFunctionStub call_function(arg_count, in_loop, RECEIVER_MIGHT_BE_VALUE);
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00002755 result = frame_->CallStub(&call_function, arg_count + 1);
2756
2757 // Restore the context and overwrite the function on the stack with
2758 // the result.
2759 frame_->RestoreContextRegister();
2760 frame_->SetElementAt(0, &result);
2761
2762 } else if (var != NULL && !var->is_this() && var->is_global()) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002763 // ----------------------------------
2764 // JavaScript example: 'foo(1, 2, 3)' // foo is global
2765 // ----------------------------------
2766
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002767 // Pass the global object as the receiver and let the IC stub
2768 // patch the stack to use the global proxy as 'this' in the
2769 // invoked function.
2770 LoadGlobal();
2771
2772 // Load the arguments.
2773 int arg_count = args->length();
2774 for (int i = 0; i < arg_count; i++) {
2775 Load(args->at(i));
2776 }
2777
ager@chromium.org5c838252010-02-19 08:53:10 +00002778 // Push the name of the function on the frame.
2779 frame_->Push(var->name());
2780
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002781 // Call the IC initialization code.
2782 CodeForSourcePosition(node->position());
2783 Result result = frame_->CallCallIC(RelocInfo::CODE_TARGET_CONTEXT,
2784 arg_count,
2785 loop_nesting());
2786 frame_->RestoreContextRegister();
2787 // Replace the function on the stack with the result.
ager@chromium.org5c838252010-02-19 08:53:10 +00002788 frame_->Push(&result);
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00002789
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002790 } else if (var != NULL && var->slot() != NULL &&
2791 var->slot()->type() == Slot::LOOKUP) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002792 // ----------------------------------
2793 // JavaScript example: 'with (obj) foo(1, 2, 3)' // foo is in obj
2794 // ----------------------------------
2795
2796 // Load the function from the context. Sync the frame so we can
2797 // push the arguments directly into place.
2798 frame_->SyncRange(0, frame_->element_count() - 1);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00002799 frame_->EmitPush(rsi);
2800 frame_->EmitPush(var->name());
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002801 frame_->CallRuntime(Runtime::kLoadContextSlot, 2);
ager@chromium.org3e875802009-06-29 08:26:34 +00002802 // The runtime call returns a pair of values in rax and rdx. The
2803 // looked-up function is in rax and the receiver is in rdx. These
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002804 // register references are not ref counted here. We spill them
2805 // eagerly since they are arguments to an inevitable call (and are
2806 // not sharable by the arguments).
ager@chromium.org3e875802009-06-29 08:26:34 +00002807 ASSERT(!allocator()->is_used(rax));
2808 frame_->EmitPush(rax);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002809
2810 // Load the receiver.
ager@chromium.org3e875802009-06-29 08:26:34 +00002811 ASSERT(!allocator()->is_used(rdx));
2812 frame_->EmitPush(rdx);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002813
2814 // Call the function.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002815 CallWithArguments(args, NO_CALL_FUNCTION_FLAGS, node->position());
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00002816
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002817 } else if (property != NULL) {
2818 // Check if the key is a literal string.
2819 Literal* literal = property->key()->AsLiteral();
2820
2821 if (literal != NULL && literal->handle()->IsSymbol()) {
2822 // ------------------------------------------------------------------
2823 // JavaScript example: 'object.foo(1, 2, 3)' or 'map["key"](1, 2, 3)'
2824 // ------------------------------------------------------------------
2825
ager@chromium.org18ad94b2009-09-02 08:22:29 +00002826 Handle<String> name = Handle<String>::cast(literal->handle());
ager@chromium.org3e875802009-06-29 08:26:34 +00002827
ager@chromium.org18ad94b2009-09-02 08:22:29 +00002828 if (ArgumentsMode() == LAZY_ARGUMENTS_ALLOCATION &&
2829 name->IsEqualTo(CStrVector("apply")) &&
2830 args->length() == 2 &&
2831 args->at(1)->AsVariableProxy() != NULL &&
2832 args->at(1)->AsVariableProxy()->IsArguments()) {
2833 // Use the optimized Function.prototype.apply that avoids
2834 // allocating lazily allocated arguments objects.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002835 CallApplyLazy(property->obj(),
ager@chromium.org18ad94b2009-09-02 08:22:29 +00002836 args->at(0),
2837 args->at(1)->AsVariableProxy(),
2838 node->position());
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002839
ager@chromium.org18ad94b2009-09-02 08:22:29 +00002840 } else {
ager@chromium.org5c838252010-02-19 08:53:10 +00002841 // Push the receiver onto the frame.
ager@chromium.org18ad94b2009-09-02 08:22:29 +00002842 Load(property->obj());
2843
2844 // Load the arguments.
2845 int arg_count = args->length();
2846 for (int i = 0; i < arg_count; i++) {
2847 Load(args->at(i));
2848 }
2849
ager@chromium.org5c838252010-02-19 08:53:10 +00002850 // Push the name of the function onto the frame.
2851 frame_->Push(name);
2852
ager@chromium.org18ad94b2009-09-02 08:22:29 +00002853 // Call the IC initialization code.
2854 CodeForSourcePosition(node->position());
2855 Result result = frame_->CallCallIC(RelocInfo::CODE_TARGET,
2856 arg_count,
2857 loop_nesting());
2858 frame_->RestoreContextRegister();
ager@chromium.org5c838252010-02-19 08:53:10 +00002859 frame_->Push(&result);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002860 }
2861
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002862 } else {
2863 // -------------------------------------------
2864 // JavaScript example: 'array[index](1, 2, 3)'
2865 // -------------------------------------------
2866
2867 // Load the function to call from the property through a reference.
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002868 if (property->is_synthetic()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002869 Reference ref(this, property, false);
2870 ref.GetValue();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002871 // Use global object as receiver.
2872 LoadGlobalReceiver();
2873 } else {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002874 Reference ref(this, property, false);
2875 ASSERT(ref.size() == 2);
2876 Result key = frame_->Pop();
2877 frame_->Dup(); // Duplicate the receiver.
2878 frame_->Push(&key);
2879 ref.GetValue();
2880 // Top of frame contains function to call, with duplicate copy of
2881 // receiver below it. Swap them.
2882 Result function = frame_->Pop();
2883 Result receiver = frame_->Pop();
2884 frame_->Push(&function);
2885 frame_->Push(&receiver);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002886 }
2887
2888 // Call the function.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002889 CallWithArguments(args, RECEIVER_MIGHT_BE_VALUE, node->position());
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002890 }
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00002891
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002892 } else {
2893 // ----------------------------------
2894 // JavaScript example: 'foo(1, 2, 3)' // foo is not global
2895 // ----------------------------------
2896
2897 // Load the function.
2898 Load(function);
2899
2900 // Pass the global proxy as the receiver.
2901 LoadGlobalReceiver();
2902
2903 // Call the function.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002904 CallWithArguments(args, NO_CALL_FUNCTION_FLAGS, node->position());
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002905 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002906}
2907
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002908
ager@chromium.org3e875802009-06-29 08:26:34 +00002909void CodeGenerator::VisitCallNew(CallNew* node) {
2910 Comment cmnt(masm_, "[ CallNew");
ager@chromium.org3e875802009-06-29 08:26:34 +00002911
2912 // According to ECMA-262, section 11.2.2, page 44, the function
2913 // expression in new calls must be evaluated before the
2914 // arguments. This is different from ordinary calls, where the
2915 // actual function to call is resolved after the arguments have been
2916 // evaluated.
2917
2918 // Compute function to call and use the global object as the
2919 // receiver. There is no need to use the global proxy here because
2920 // it will always be replaced with a newly allocated object.
2921 Load(node->expression());
2922 LoadGlobal();
2923
2924 // Push the arguments ("left-to-right") on the stack.
2925 ZoneList<Expression*>* args = node->arguments();
2926 int arg_count = args->length();
2927 for (int i = 0; i < arg_count; i++) {
2928 Load(args->at(i));
2929 }
2930
2931 // Call the construct call builtin that handles allocation and
2932 // constructor invocation.
2933 CodeForSourcePosition(node->position());
2934 Result result = frame_->CallConstructor(arg_count);
2935 // Replace the function on the stack with the result.
2936 frame_->SetElementAt(0, &result);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002937}
2938
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002939
ager@chromium.org3e875802009-06-29 08:26:34 +00002940void CodeGenerator::VisitCallRuntime(CallRuntime* node) {
2941 if (CheckForInlineRuntimeCall(node)) {
2942 return;
2943 }
2944
2945 ZoneList<Expression*>* args = node->arguments();
2946 Comment cmnt(masm_, "[ CallRuntime");
2947 Runtime::Function* function = node->function();
2948
2949 if (function == NULL) {
ager@chromium.org3e875802009-06-29 08:26:34 +00002950 // Push the builtins object found in the current global object.
2951 Result temp = allocator()->Allocate();
2952 ASSERT(temp.is_valid());
2953 __ movq(temp.reg(), GlobalObject());
2954 __ movq(temp.reg(),
2955 FieldOperand(temp.reg(), GlobalObject::kBuiltinsOffset));
2956 frame_->Push(&temp);
2957 }
2958
2959 // Push the arguments ("left-to-right").
2960 int arg_count = args->length();
2961 for (int i = 0; i < arg_count; i++) {
2962 Load(args->at(i));
2963 }
2964
2965 if (function == NULL) {
2966 // Call the JS runtime function.
ager@chromium.org5c838252010-02-19 08:53:10 +00002967 frame_->Push(node->name());
ager@chromium.org3e875802009-06-29 08:26:34 +00002968 Result answer = frame_->CallCallIC(RelocInfo::CODE_TARGET,
2969 arg_count,
2970 loop_nesting_);
2971 frame_->RestoreContextRegister();
ager@chromium.org5c838252010-02-19 08:53:10 +00002972 frame_->Push(&answer);
ager@chromium.org3e875802009-06-29 08:26:34 +00002973 } else {
2974 // Call the C runtime function.
2975 Result answer = frame_->CallRuntime(function, arg_count);
2976 frame_->Push(&answer);
2977 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002978}
2979
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002980
ager@chromium.org3e875802009-06-29 08:26:34 +00002981void CodeGenerator::VisitUnaryOperation(UnaryOperation* node) {
ager@chromium.org3e875802009-06-29 08:26:34 +00002982 Comment cmnt(masm_, "[ UnaryOperation");
2983
2984 Token::Value op = node->op();
2985
2986 if (op == Token::NOT) {
2987 // Swap the true and false targets but keep the same actual label
2988 // as the fall through.
2989 destination()->Invert();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002990 LoadCondition(node->expression(), destination(), true);
ager@chromium.org3e875802009-06-29 08:26:34 +00002991 // Swap the labels back.
2992 destination()->Invert();
2993
2994 } else if (op == Token::DELETE) {
2995 Property* property = node->expression()->AsProperty();
2996 if (property != NULL) {
2997 Load(property->obj());
2998 Load(property->key());
2999 Result answer = frame_->InvokeBuiltin(Builtins::DELETE, CALL_FUNCTION, 2);
3000 frame_->Push(&answer);
3001 return;
3002 }
3003
3004 Variable* variable = node->expression()->AsVariableProxy()->AsVariable();
3005 if (variable != NULL) {
3006 Slot* slot = variable->slot();
3007 if (variable->is_global()) {
3008 LoadGlobal();
3009 frame_->Push(variable->name());
3010 Result answer = frame_->InvokeBuiltin(Builtins::DELETE,
3011 CALL_FUNCTION, 2);
3012 frame_->Push(&answer);
3013 return;
3014
3015 } else if (slot != NULL && slot->type() == Slot::LOOKUP) {
3016 // Call the runtime to look up the context holding the named
3017 // variable. Sync the virtual frame eagerly so we can push the
3018 // arguments directly into place.
3019 frame_->SyncRange(0, frame_->element_count() - 1);
3020 frame_->EmitPush(rsi);
3021 frame_->EmitPush(variable->name());
3022 Result context = frame_->CallRuntime(Runtime::kLookupContext, 2);
3023 ASSERT(context.is_register());
3024 frame_->EmitPush(context.reg());
3025 context.Unuse();
3026 frame_->EmitPush(variable->name());
3027 Result answer = frame_->InvokeBuiltin(Builtins::DELETE,
3028 CALL_FUNCTION, 2);
3029 frame_->Push(&answer);
3030 return;
3031 }
3032
3033 // Default: Result of deleting non-global, not dynamically
3034 // introduced variables is false.
3035 frame_->Push(Factory::false_value());
3036
3037 } else {
3038 // Default: Result of deleting expressions is true.
3039 Load(node->expression()); // may have side-effects
3040 frame_->SetElementAt(0, Factory::true_value());
3041 }
3042
3043 } else if (op == Token::TYPEOF) {
3044 // Special case for loading the typeof expression; see comment on
3045 // LoadTypeofExpression().
3046 LoadTypeofExpression(node->expression());
3047 Result answer = frame_->CallRuntime(Runtime::kTypeof, 1);
3048 frame_->Push(&answer);
3049
3050 } else if (op == Token::VOID) {
3051 Expression* expression = node->expression();
3052 if (expression && expression->AsLiteral() && (
3053 expression->AsLiteral()->IsTrue() ||
3054 expression->AsLiteral()->IsFalse() ||
3055 expression->AsLiteral()->handle()->IsNumber() ||
3056 expression->AsLiteral()->handle()->IsString() ||
3057 expression->AsLiteral()->handle()->IsJSRegExp() ||
3058 expression->AsLiteral()->IsNull())) {
3059 // Omit evaluating the value of the primitive literal.
3060 // It will be discarded anyway, and can have no side effect.
3061 frame_->Push(Factory::undefined_value());
3062 } else {
3063 Load(node->expression());
3064 frame_->SetElementAt(0, Factory::undefined_value());
3065 }
3066
3067 } else {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003068 bool overwrite =
3069 (node->expression()->AsBinaryOperation() != NULL &&
3070 node->expression()->AsBinaryOperation()->ResultOverwriteAllowed());
ager@chromium.org3e875802009-06-29 08:26:34 +00003071 Load(node->expression());
3072 switch (op) {
3073 case Token::NOT:
3074 case Token::DELETE:
3075 case Token::TYPEOF:
3076 UNREACHABLE(); // handled above
3077 break;
3078
3079 case Token::SUB: {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003080 GenericUnaryOpStub stub(Token::SUB, overwrite);
ager@chromium.org3e875802009-06-29 08:26:34 +00003081 Result operand = frame_->Pop();
3082 Result answer = frame_->CallStub(&stub, &operand);
3083 frame_->Push(&answer);
3084 break;
3085 }
3086
3087 case Token::BIT_NOT: {
3088 // Smi check.
3089 JumpTarget smi_label;
3090 JumpTarget continue_label;
3091 Result operand = frame_->Pop();
3092 operand.ToRegister();
ager@chromium.org4af710e2009-09-15 12:20:11 +00003093
3094 Condition is_smi = masm_->CheckSmi(operand.reg());
3095 smi_label.Branch(is_smi, &operand);
ager@chromium.org3e875802009-06-29 08:26:34 +00003096
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003097 GenericUnaryOpStub stub(Token::BIT_NOT, overwrite);
3098 Result answer = frame_->CallStub(&stub, &operand);
ager@chromium.org3e875802009-06-29 08:26:34 +00003099 continue_label.Jump(&answer);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003100
ager@chromium.org3e875802009-06-29 08:26:34 +00003101 smi_label.Bind(&answer);
3102 answer.ToRegister();
3103 frame_->Spill(answer.reg());
ager@chromium.org4af710e2009-09-15 12:20:11 +00003104 __ SmiNot(answer.reg(), answer.reg());
ager@chromium.org3e875802009-06-29 08:26:34 +00003105 continue_label.Bind(&answer);
3106 frame_->Push(&answer);
3107 break;
3108 }
3109
3110 case Token::ADD: {
3111 // Smi check.
3112 JumpTarget continue_label;
3113 Result operand = frame_->Pop();
3114 operand.ToRegister();
ager@chromium.org4af710e2009-09-15 12:20:11 +00003115 Condition is_smi = masm_->CheckSmi(operand.reg());
3116 continue_label.Branch(is_smi, &operand);
ager@chromium.org3e875802009-06-29 08:26:34 +00003117 frame_->Push(&operand);
3118 Result answer = frame_->InvokeBuiltin(Builtins::TO_NUMBER,
3119 CALL_FUNCTION, 1);
3120
3121 continue_label.Bind(&answer);
3122 frame_->Push(&answer);
3123 break;
3124 }
3125
3126 default:
3127 UNREACHABLE();
3128 }
3129 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00003130}
3131
ager@chromium.org3e875802009-06-29 08:26:34 +00003132
3133// The value in dst was optimistically incremented or decremented. The
3134// result overflowed or was not smi tagged. Undo the operation, call
3135// into the runtime to convert the argument to a number, and call the
3136// specialized add or subtract stub. The result is left in dst.
3137class DeferredPrefixCountOperation: public DeferredCode {
3138 public:
3139 DeferredPrefixCountOperation(Register dst, bool is_increment)
3140 : dst_(dst), is_increment_(is_increment) {
3141 set_comment("[ DeferredCountOperation");
3142 }
3143
3144 virtual void Generate();
3145
3146 private:
3147 Register dst_;
3148 bool is_increment_;
3149};
3150
3151
3152void DeferredPrefixCountOperation::Generate() {
ager@chromium.org3e875802009-06-29 08:26:34 +00003153 __ push(dst_);
3154 __ InvokeBuiltin(Builtins::TO_NUMBER, CALL_FUNCTION);
3155 __ push(rax);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00003156 __ Push(Smi::FromInt(1));
ager@chromium.org3e875802009-06-29 08:26:34 +00003157 if (is_increment_) {
3158 __ CallRuntime(Runtime::kNumberAdd, 2);
3159 } else {
3160 __ CallRuntime(Runtime::kNumberSub, 2);
3161 }
3162 if (!dst_.is(rax)) __ movq(dst_, rax);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00003163}
3164
ager@chromium.org3e875802009-06-29 08:26:34 +00003165
3166// The value in dst was optimistically incremented or decremented. The
3167// result overflowed or was not smi tagged. Undo the operation and call
3168// into the runtime to convert the argument to a number. Update the
3169// original value in old. Call the specialized add or subtract stub.
3170// The result is left in dst.
3171class DeferredPostfixCountOperation: public DeferredCode {
3172 public:
3173 DeferredPostfixCountOperation(Register dst, Register old, bool is_increment)
3174 : dst_(dst), old_(old), is_increment_(is_increment) {
3175 set_comment("[ DeferredCountOperation");
3176 }
3177
3178 virtual void Generate();
3179
3180 private:
3181 Register dst_;
3182 Register old_;
3183 bool is_increment_;
3184};
3185
3186
3187void DeferredPostfixCountOperation::Generate() {
ager@chromium.org3e875802009-06-29 08:26:34 +00003188 __ push(dst_);
3189 __ InvokeBuiltin(Builtins::TO_NUMBER, CALL_FUNCTION);
3190
3191 // Save the result of ToNumber to use as the old value.
3192 __ push(rax);
3193
3194 // Call the runtime for the addition or subtraction.
3195 __ push(rax);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00003196 __ Push(Smi::FromInt(1));
ager@chromium.org3e875802009-06-29 08:26:34 +00003197 if (is_increment_) {
3198 __ CallRuntime(Runtime::kNumberAdd, 2);
3199 } else {
3200 __ CallRuntime(Runtime::kNumberSub, 2);
3201 }
3202 if (!dst_.is(rax)) __ movq(dst_, rax);
3203 __ pop(old_);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00003204}
3205
ager@chromium.org3e875802009-06-29 08:26:34 +00003206
3207void CodeGenerator::VisitCountOperation(CountOperation* node) {
3208 Comment cmnt(masm_, "[ CountOperation");
3209
3210 bool is_postfix = node->is_postfix();
3211 bool is_increment = node->op() == Token::INC;
3212
3213 Variable* var = node->expression()->AsVariableProxy()->AsVariable();
3214 bool is_const = (var != NULL && var->mode() == Variable::CONST);
3215
3216 // Postfix operations need a stack slot under the reference to hold
3217 // the old value while the new value is being stored. This is so that
3218 // in the case that storing the new value requires a call, the old
3219 // value will be in the frame to be spilled.
3220 if (is_postfix) frame_->Push(Smi::FromInt(0));
3221
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003222 // A constant reference is not saved to, so the reference is not a
3223 // compound assignment reference.
3224 { Reference target(this, node->expression(), !is_const);
ager@chromium.org3e875802009-06-29 08:26:34 +00003225 if (target.is_illegal()) {
3226 // Spoof the virtual frame to have the expected height (one higher
3227 // than on entry).
3228 if (!is_postfix) frame_->Push(Smi::FromInt(0));
3229 return;
3230 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003231 target.TakeValue();
ager@chromium.org3e875802009-06-29 08:26:34 +00003232
3233 Result new_value = frame_->Pop();
3234 new_value.ToRegister();
3235
3236 Result old_value; // Only allocated in the postfix case.
3237 if (is_postfix) {
3238 // Allocate a temporary to preserve the old value.
3239 old_value = allocator_->Allocate();
3240 ASSERT(old_value.is_valid());
3241 __ movq(old_value.reg(), new_value.reg());
3242 }
3243 // Ensure the new value is writable.
3244 frame_->Spill(new_value.reg());
3245
ager@chromium.org3e875802009-06-29 08:26:34 +00003246 DeferredCode* deferred = NULL;
3247 if (is_postfix) {
3248 deferred = new DeferredPostfixCountOperation(new_value.reg(),
3249 old_value.reg(),
3250 is_increment);
3251 } else {
3252 deferred = new DeferredPrefixCountOperation(new_value.reg(),
3253 is_increment);
3254 }
3255
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00003256 __ JumpIfNotSmi(new_value.reg(), deferred->entry_label());
ager@chromium.org3e875802009-06-29 08:26:34 +00003257 if (is_increment) {
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00003258 __ SmiAddConstant(kScratchRegister,
3259 new_value.reg(),
3260 Smi::FromInt(1),
3261 deferred->entry_label());
ager@chromium.org3e875802009-06-29 08:26:34 +00003262 } else {
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00003263 __ SmiSubConstant(kScratchRegister,
3264 new_value.reg(),
3265 Smi::FromInt(1),
3266 deferred->entry_label());
ager@chromium.org3e875802009-06-29 08:26:34 +00003267 }
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00003268 __ movq(new_value.reg(), kScratchRegister);
ager@chromium.org3e875802009-06-29 08:26:34 +00003269 deferred->BindExit();
3270
3271 // Postfix: store the old value in the allocated slot under the
3272 // reference.
3273 if (is_postfix) frame_->SetElementAt(target.size(), &old_value);
3274
3275 frame_->Push(&new_value);
3276 // Non-constant: update the reference.
3277 if (!is_const) target.SetValue(NOT_CONST_INIT);
3278 }
3279
3280 // Postfix: drop the new value and use the old.
3281 if (is_postfix) frame_->Drop();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00003282}
3283
ager@chromium.org3e875802009-06-29 08:26:34 +00003284
3285void CodeGenerator::VisitBinaryOperation(BinaryOperation* node) {
3286 // TODO(X64): This code was copied verbatim from codegen-ia32.
3287 // Either find a reason to change it or move it to a shared location.
3288
ager@chromium.org3e875802009-06-29 08:26:34 +00003289 Comment cmnt(masm_, "[ BinaryOperation");
3290 Token::Value op = node->op();
3291
3292 // According to ECMA-262 section 11.11, page 58, the binary logical
3293 // operators must yield the result of one of the two expressions
3294 // before any ToBoolean() conversions. This means that the value
3295 // produced by a && or || operator is not necessarily a boolean.
3296
3297 // NOTE: If the left hand side produces a materialized value (not
3298 // control flow), we force the right hand side to do the same. This
3299 // is necessary because we assume that if we get control flow on the
3300 // last path out of an expression we got it on all paths.
3301 if (op == Token::AND) {
3302 JumpTarget is_true;
3303 ControlDestination dest(&is_true, destination()->false_target(), true);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003304 LoadCondition(node->left(), &dest, false);
ager@chromium.org3e875802009-06-29 08:26:34 +00003305
3306 if (dest.false_was_fall_through()) {
3307 // The current false target was used as the fall-through. If
3308 // there are no dangling jumps to is_true then the left
3309 // subexpression was unconditionally false. Otherwise we have
3310 // paths where we do have to evaluate the right subexpression.
3311 if (is_true.is_linked()) {
3312 // We need to compile the right subexpression. If the jump to
3313 // the current false target was a forward jump then we have a
3314 // valid frame, we have just bound the false target, and we
3315 // have to jump around the code for the right subexpression.
3316 if (has_valid_frame()) {
3317 destination()->false_target()->Unuse();
3318 destination()->false_target()->Jump();
3319 }
3320 is_true.Bind();
3321 // The left subexpression compiled to control flow, so the
3322 // right one is free to do so as well.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003323 LoadCondition(node->right(), destination(), false);
ager@chromium.org3e875802009-06-29 08:26:34 +00003324 } else {
3325 // We have actually just jumped to or bound the current false
3326 // target but the current control destination is not marked as
3327 // used.
3328 destination()->Use(false);
3329 }
3330
3331 } else if (dest.is_used()) {
3332 // The left subexpression compiled to control flow (and is_true
3333 // was just bound), so the right is free to do so as well.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003334 LoadCondition(node->right(), destination(), false);
ager@chromium.org3e875802009-06-29 08:26:34 +00003335
3336 } else {
3337 // We have a materialized value on the frame, so we exit with
3338 // one on all paths. There are possibly also jumps to is_true
3339 // from nested subexpressions.
3340 JumpTarget pop_and_continue;
3341 JumpTarget exit;
3342
3343 // Avoid popping the result if it converts to 'false' using the
3344 // standard ToBoolean() conversion as described in ECMA-262,
3345 // section 9.2, page 30.
3346 //
3347 // Duplicate the TOS value. The duplicate will be popped by
3348 // ToBoolean.
3349 frame_->Dup();
3350 ControlDestination dest(&pop_and_continue, &exit, true);
3351 ToBoolean(&dest);
3352
3353 // Pop the result of evaluating the first part.
3354 frame_->Drop();
3355
3356 // Compile right side expression.
3357 is_true.Bind();
3358 Load(node->right());
3359
3360 // Exit (always with a materialized value).
3361 exit.Bind();
3362 }
3363
3364 } else if (op == Token::OR) {
3365 JumpTarget is_false;
3366 ControlDestination dest(destination()->true_target(), &is_false, false);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003367 LoadCondition(node->left(), &dest, false);
ager@chromium.org3e875802009-06-29 08:26:34 +00003368
3369 if (dest.true_was_fall_through()) {
3370 // The current true target was used as the fall-through. If
3371 // there are no dangling jumps to is_false then the left
3372 // subexpression was unconditionally true. Otherwise we have
3373 // paths where we do have to evaluate the right subexpression.
3374 if (is_false.is_linked()) {
3375 // We need to compile the right subexpression. If the jump to
3376 // the current true target was a forward jump then we have a
3377 // valid frame, we have just bound the true target, and we
3378 // have to jump around the code for the right subexpression.
3379 if (has_valid_frame()) {
3380 destination()->true_target()->Unuse();
3381 destination()->true_target()->Jump();
3382 }
3383 is_false.Bind();
3384 // The left subexpression compiled to control flow, so the
3385 // right one is free to do so as well.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003386 LoadCondition(node->right(), destination(), false);
ager@chromium.org3e875802009-06-29 08:26:34 +00003387 } else {
3388 // We have just jumped to or bound the current true target but
3389 // the current control destination is not marked as used.
3390 destination()->Use(true);
3391 }
3392
3393 } else if (dest.is_used()) {
3394 // The left subexpression compiled to control flow (and is_false
3395 // was just bound), so the right is free to do so as well.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003396 LoadCondition(node->right(), destination(), false);
ager@chromium.org3e875802009-06-29 08:26:34 +00003397
3398 } else {
3399 // We have a materialized value on the frame, so we exit with
3400 // one on all paths. There are possibly also jumps to is_false
3401 // from nested subexpressions.
3402 JumpTarget pop_and_continue;
3403 JumpTarget exit;
3404
3405 // Avoid popping the result if it converts to 'true' using the
3406 // standard ToBoolean() conversion as described in ECMA-262,
3407 // section 9.2, page 30.
3408 //
3409 // Duplicate the TOS value. The duplicate will be popped by
3410 // ToBoolean.
3411 frame_->Dup();
3412 ControlDestination dest(&exit, &pop_and_continue, false);
3413 ToBoolean(&dest);
3414
3415 // Pop the result of evaluating the first part.
3416 frame_->Drop();
3417
3418 // Compile right side expression.
3419 is_false.Bind();
3420 Load(node->right());
3421
3422 // Exit (always with a materialized value).
3423 exit.Bind();
3424 }
3425
3426 } else {
3427 // NOTE: The code below assumes that the slow cases (calls to runtime)
3428 // never return a constant/immutable object.
3429 OverwriteMode overwrite_mode = NO_OVERWRITE;
3430 if (node->left()->AsBinaryOperation() != NULL &&
3431 node->left()->AsBinaryOperation()->ResultOverwriteAllowed()) {
3432 overwrite_mode = OVERWRITE_LEFT;
3433 } else if (node->right()->AsBinaryOperation() != NULL &&
3434 node->right()->AsBinaryOperation()->ResultOverwriteAllowed()) {
3435 overwrite_mode = OVERWRITE_RIGHT;
3436 }
3437
3438 Load(node->left());
3439 Load(node->right());
3440 GenericBinaryOperation(node->op(), node->type(), overwrite_mode);
3441 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00003442}
3443
ager@chromium.org3e875802009-06-29 08:26:34 +00003444
3445
3446void CodeGenerator::VisitCompareOperation(CompareOperation* node) {
3447 Comment cmnt(masm_, "[ CompareOperation");
3448
3449 // Get the expressions from the node.
3450 Expression* left = node->left();
3451 Expression* right = node->right();
3452 Token::Value op = node->op();
3453 // To make typeof testing for natives implemented in JavaScript really
3454 // efficient, we generate special code for expressions of the form:
3455 // 'typeof <expression> == <string>'.
3456 UnaryOperation* operation = left->AsUnaryOperation();
3457 if ((op == Token::EQ || op == Token::EQ_STRICT) &&
3458 (operation != NULL && operation->op() == Token::TYPEOF) &&
3459 (right->AsLiteral() != NULL &&
3460 right->AsLiteral()->handle()->IsString())) {
3461 Handle<String> check(Handle<String>::cast(right->AsLiteral()->handle()));
3462
3463 // Load the operand and move it to a register.
3464 LoadTypeofExpression(operation->expression());
3465 Result answer = frame_->Pop();
3466 answer.ToRegister();
3467
3468 if (check->Equals(Heap::number_symbol())) {
ager@chromium.org4af710e2009-09-15 12:20:11 +00003469 Condition is_smi = masm_->CheckSmi(answer.reg());
3470 destination()->true_target()->Branch(is_smi);
ager@chromium.org3e875802009-06-29 08:26:34 +00003471 frame_->Spill(answer.reg());
3472 __ movq(answer.reg(), FieldOperand(answer.reg(), HeapObject::kMapOffset));
ager@chromium.org18ad94b2009-09-02 08:22:29 +00003473 __ CompareRoot(answer.reg(), Heap::kHeapNumberMapRootIndex);
ager@chromium.org3e875802009-06-29 08:26:34 +00003474 answer.Unuse();
3475 destination()->Split(equal);
3476
3477 } else if (check->Equals(Heap::string_symbol())) {
ager@chromium.org4af710e2009-09-15 12:20:11 +00003478 Condition is_smi = masm_->CheckSmi(answer.reg());
3479 destination()->false_target()->Branch(is_smi);
ager@chromium.org3e875802009-06-29 08:26:34 +00003480
3481 // It can be an undetectable string object.
3482 __ movq(kScratchRegister,
3483 FieldOperand(answer.reg(), HeapObject::kMapOffset));
3484 __ testb(FieldOperand(kScratchRegister, Map::kBitFieldOffset),
3485 Immediate(1 << Map::kIsUndetectable));
3486 destination()->false_target()->Branch(not_zero);
3487 __ CmpInstanceType(kScratchRegister, FIRST_NONSTRING_TYPE);
3488 answer.Unuse();
3489 destination()->Split(below); // Unsigned byte comparison needed.
3490
3491 } else if (check->Equals(Heap::boolean_symbol())) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00003492 __ CompareRoot(answer.reg(), Heap::kTrueValueRootIndex);
ager@chromium.org3e875802009-06-29 08:26:34 +00003493 destination()->true_target()->Branch(equal);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00003494 __ CompareRoot(answer.reg(), Heap::kFalseValueRootIndex);
ager@chromium.org3e875802009-06-29 08:26:34 +00003495 answer.Unuse();
3496 destination()->Split(equal);
3497
3498 } else if (check->Equals(Heap::undefined_symbol())) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00003499 __ CompareRoot(answer.reg(), Heap::kUndefinedValueRootIndex);
ager@chromium.org3e875802009-06-29 08:26:34 +00003500 destination()->true_target()->Branch(equal);
3501
ager@chromium.org4af710e2009-09-15 12:20:11 +00003502 Condition is_smi = masm_->CheckSmi(answer.reg());
3503 destination()->false_target()->Branch(is_smi);
ager@chromium.org3e875802009-06-29 08:26:34 +00003504
3505 // It can be an undetectable object.
3506 __ movq(kScratchRegister,
3507 FieldOperand(answer.reg(), HeapObject::kMapOffset));
3508 __ testb(FieldOperand(kScratchRegister, Map::kBitFieldOffset),
3509 Immediate(1 << Map::kIsUndetectable));
3510 answer.Unuse();
3511 destination()->Split(not_zero);
3512
3513 } else if (check->Equals(Heap::function_symbol())) {
ager@chromium.org4af710e2009-09-15 12:20:11 +00003514 Condition is_smi = masm_->CheckSmi(answer.reg());
3515 destination()->false_target()->Branch(is_smi);
ager@chromium.org3e875802009-06-29 08:26:34 +00003516 frame_->Spill(answer.reg());
3517 __ CmpObjectType(answer.reg(), JS_FUNCTION_TYPE, answer.reg());
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003518 destination()->true_target()->Branch(equal);
3519 // Regular expressions are callable so typeof == 'function'.
3520 __ CmpInstanceType(answer.reg(), JS_REGEXP_TYPE);
ager@chromium.org3e875802009-06-29 08:26:34 +00003521 answer.Unuse();
3522 destination()->Split(equal);
3523
3524 } else if (check->Equals(Heap::object_symbol())) {
ager@chromium.org4af710e2009-09-15 12:20:11 +00003525 Condition is_smi = masm_->CheckSmi(answer.reg());
3526 destination()->false_target()->Branch(is_smi);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00003527 __ CompareRoot(answer.reg(), Heap::kNullValueRootIndex);
ager@chromium.org3e875802009-06-29 08:26:34 +00003528 destination()->true_target()->Branch(equal);
3529
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003530 // Regular expressions are typeof == 'function', not 'object'.
3531 __ CmpObjectType(answer.reg(), JS_REGEXP_TYPE, kScratchRegister);
3532 destination()->false_target()->Branch(equal);
3533
ager@chromium.org3e875802009-06-29 08:26:34 +00003534 // It can be an undetectable object.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00003535 __ testb(FieldOperand(kScratchRegister, Map::kBitFieldOffset),
3536 Immediate(1 << Map::kIsUndetectable));
ager@chromium.org3e875802009-06-29 08:26:34 +00003537 destination()->false_target()->Branch(not_zero);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003538 __ CmpInstanceType(kScratchRegister, FIRST_JS_OBJECT_TYPE);
ager@chromium.org3e875802009-06-29 08:26:34 +00003539 destination()->false_target()->Branch(below);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003540 __ CmpInstanceType(kScratchRegister, LAST_JS_OBJECT_TYPE);
ager@chromium.org3e875802009-06-29 08:26:34 +00003541 answer.Unuse();
3542 destination()->Split(below_equal);
3543 } else {
3544 // Uncommon case: typeof testing against a string literal that is
3545 // never returned from the typeof operator.
3546 answer.Unuse();
3547 destination()->Goto(false);
3548 }
3549 return;
3550 }
3551
3552 Condition cc = no_condition;
3553 bool strict = false;
3554 switch (op) {
3555 case Token::EQ_STRICT:
3556 strict = true;
3557 // Fall through
3558 case Token::EQ:
3559 cc = equal;
3560 break;
3561 case Token::LT:
3562 cc = less;
3563 break;
3564 case Token::GT:
3565 cc = greater;
3566 break;
3567 case Token::LTE:
3568 cc = less_equal;
3569 break;
3570 case Token::GTE:
3571 cc = greater_equal;
3572 break;
3573 case Token::IN: {
3574 Load(left);
3575 Load(right);
3576 Result answer = frame_->InvokeBuiltin(Builtins::IN, CALL_FUNCTION, 2);
3577 frame_->Push(&answer); // push the result
3578 return;
3579 }
3580 case Token::INSTANCEOF: {
3581 Load(left);
3582 Load(right);
3583 InstanceofStub stub;
3584 Result answer = frame_->CallStub(&stub, 2);
3585 answer.ToRegister();
3586 __ testq(answer.reg(), answer.reg());
3587 answer.Unuse();
3588 destination()->Split(zero);
3589 return;
3590 }
3591 default:
3592 UNREACHABLE();
3593 }
3594 Load(left);
3595 Load(right);
ager@chromium.org5c838252010-02-19 08:53:10 +00003596 Comparison(node, cc, strict, destination());
ager@chromium.org3e875802009-06-29 08:26:34 +00003597}
3598
3599
3600void CodeGenerator::VisitThisFunction(ThisFunction* node) {
3601 frame_->PushFunction();
3602}
3603
3604
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00003605void CodeGenerator::GenerateArguments(ZoneList<Expression*>* args) {
ager@chromium.org3e875802009-06-29 08:26:34 +00003606 ASSERT(args->length() == 1);
3607
3608 // ArgumentsAccessStub expects the key in rdx and the formal
3609 // parameter count in rax.
3610 Load(args->at(0));
3611 Result key = frame_->Pop();
3612 // Explicitly create a constant result.
ager@chromium.org5c838252010-02-19 08:53:10 +00003613 Result count(Handle<Smi>(Smi::FromInt(scope()->num_parameters())));
ager@chromium.org3e875802009-06-29 08:26:34 +00003614 // Call the shared stub to get to arguments[key].
3615 ArgumentsAccessStub stub(ArgumentsAccessStub::READ_ELEMENT);
3616 Result result = frame_->CallStub(&stub, &key, &count);
3617 frame_->Push(&result);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00003618}
3619
ager@chromium.orgeadaf222009-06-16 09:43:10 +00003620
ager@chromium.org5aa501c2009-06-23 07:57:28 +00003621void CodeGenerator::GenerateIsArray(ZoneList<Expression*>* args) {
ager@chromium.org3e875802009-06-29 08:26:34 +00003622 ASSERT(args->length() == 1);
3623 Load(args->at(0));
3624 Result value = frame_->Pop();
3625 value.ToRegister();
3626 ASSERT(value.is_valid());
ager@chromium.org4af710e2009-09-15 12:20:11 +00003627 Condition is_smi = masm_->CheckSmi(value.reg());
3628 destination()->false_target()->Branch(is_smi);
ager@chromium.org3e875802009-06-29 08:26:34 +00003629 // It is a heap object - get map.
3630 // Check if the object is a JS array or not.
3631 __ CmpObjectType(value.reg(), JS_ARRAY_TYPE, kScratchRegister);
3632 value.Unuse();
3633 destination()->Split(equal);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00003634}
3635
ager@chromium.org3e875802009-06-29 08:26:34 +00003636
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00003637void CodeGenerator::GenerateIsRegExp(ZoneList<Expression*>* args) {
3638 ASSERT(args->length() == 1);
3639 Load(args->at(0));
3640 Result value = frame_->Pop();
3641 value.ToRegister();
3642 ASSERT(value.is_valid());
3643 Condition is_smi = masm_->CheckSmi(value.reg());
3644 destination()->false_target()->Branch(is_smi);
3645 // It is a heap object - get map.
3646 // Check if the object is a regexp.
3647 __ CmpObjectType(value.reg(), JS_REGEXP_TYPE, kScratchRegister);
3648 value.Unuse();
3649 destination()->Split(equal);
3650}
3651
3652
ager@chromium.org6141cbe2009-11-20 12:14:52 +00003653void CodeGenerator::GenerateIsObject(ZoneList<Expression*>* args) {
3654 // This generates a fast version of:
3655 // (typeof(arg) === 'object' || %_ClassOf(arg) == 'RegExp')
3656 ASSERT(args->length() == 1);
3657 Load(args->at(0));
3658 Result obj = frame_->Pop();
3659 obj.ToRegister();
3660 Condition is_smi = masm_->CheckSmi(obj.reg());
3661 destination()->false_target()->Branch(is_smi);
3662
3663 __ Move(kScratchRegister, Factory::null_value());
3664 __ cmpq(obj.reg(), kScratchRegister);
3665 destination()->true_target()->Branch(equal);
3666
3667 __ movq(kScratchRegister, FieldOperand(obj.reg(), HeapObject::kMapOffset));
3668 // Undetectable objects behave like undefined when tested with typeof.
3669 __ testb(FieldOperand(kScratchRegister, Map::kBitFieldOffset),
3670 Immediate(1 << Map::kIsUndetectable));
3671 destination()->false_target()->Branch(not_zero);
3672 __ CmpInstanceType(kScratchRegister, FIRST_JS_OBJECT_TYPE);
3673 destination()->false_target()->Branch(less);
3674 __ CmpInstanceType(kScratchRegister, LAST_JS_OBJECT_TYPE);
3675 obj.Unuse();
3676 destination()->Split(less_equal);
3677}
3678
3679
3680void CodeGenerator::GenerateIsFunction(ZoneList<Expression*>* args) {
3681 // This generates a fast version of:
3682 // (%_ClassOf(arg) === 'Function')
3683 ASSERT(args->length() == 1);
3684 Load(args->at(0));
3685 Result obj = frame_->Pop();
3686 obj.ToRegister();
3687 Condition is_smi = masm_->CheckSmi(obj.reg());
3688 destination()->false_target()->Branch(is_smi);
3689 __ CmpObjectType(obj.reg(), JS_FUNCTION_TYPE, kScratchRegister);
3690 obj.Unuse();
3691 destination()->Split(equal);
3692}
3693
3694
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003695void CodeGenerator::GenerateIsUndetectableObject(ZoneList<Expression*>* args) {
3696 ASSERT(args->length() == 1);
3697 Load(args->at(0));
3698 Result obj = frame_->Pop();
3699 obj.ToRegister();
3700 Condition is_smi = masm_->CheckSmi(obj.reg());
3701 destination()->false_target()->Branch(is_smi);
3702 __ movq(kScratchRegister, FieldOperand(obj.reg(), HeapObject::kMapOffset));
3703 __ movzxbl(kScratchRegister,
3704 FieldOperand(kScratchRegister, Map::kBitFieldOffset));
3705 __ testl(kScratchRegister, Immediate(1 << Map::kIsUndetectable));
3706 obj.Unuse();
3707 destination()->Split(not_zero);
3708}
3709
3710
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00003711void CodeGenerator::GenerateIsConstructCall(ZoneList<Expression*>* args) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00003712 ASSERT(args->length() == 0);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003713
3714 // Get the frame pointer for the calling frame.
3715 Result fp = allocator()->Allocate();
3716 __ movq(fp.reg(), Operand(rbp, StandardFrameConstants::kCallerFPOffset));
3717
3718 // Skip the arguments adaptor frame if it exists.
3719 Label check_frame_marker;
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00003720 __ SmiCompare(Operand(fp.reg(), StandardFrameConstants::kContextOffset),
3721 Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR));
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003722 __ j(not_equal, &check_frame_marker);
3723 __ movq(fp.reg(), Operand(fp.reg(), StandardFrameConstants::kCallerFPOffset));
3724
3725 // Check the marker in the calling frame.
3726 __ bind(&check_frame_marker);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00003727 __ SmiCompare(Operand(fp.reg(), StandardFrameConstants::kMarkerOffset),
3728 Smi::FromInt(StackFrame::CONSTRUCT));
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003729 fp.Unuse();
3730 destination()->Split(equal);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00003731}
3732
3733
ager@chromium.org3e875802009-06-29 08:26:34 +00003734void CodeGenerator::GenerateArgumentsLength(ZoneList<Expression*>* args) {
3735 ASSERT(args->length() == 0);
3736 // ArgumentsAccessStub takes the parameter count as an input argument
3737 // in register eax. Create a constant result for it.
ager@chromium.org5c838252010-02-19 08:53:10 +00003738 Result count(Handle<Smi>(Smi::FromInt(scope()->num_parameters())));
ager@chromium.org3e875802009-06-29 08:26:34 +00003739 // Call the shared stub to get to the arguments.length.
3740 ArgumentsAccessStub stub(ArgumentsAccessStub::READ_LENGTH);
3741 Result result = frame_->CallStub(&stub, &count);
3742 frame_->Push(&result);
3743}
3744
3745
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00003746void CodeGenerator::GenerateFastCharCodeAt(ZoneList<Expression*>* args) {
3747 Comment(masm_, "[ GenerateFastCharCodeAt");
3748 ASSERT(args->length() == 2);
3749
3750 Label slow_case;
3751 Label end;
3752 Label not_a_flat_string;
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00003753 Label try_again_with_new_string;
3754 Label ascii_string;
3755 Label got_char_code;
3756
3757 Load(args->at(0));
3758 Load(args->at(1));
3759 Result index = frame_->Pop();
3760 Result object = frame_->Pop();
3761
3762 // Get register rcx to use as shift amount later.
3763 Result shift_amount;
3764 if (object.is_register() && object.reg().is(rcx)) {
3765 Result fresh = allocator_->Allocate();
3766 shift_amount = object;
3767 object = fresh;
3768 __ movq(object.reg(), rcx);
3769 }
3770 if (index.is_register() && index.reg().is(rcx)) {
3771 Result fresh = allocator_->Allocate();
3772 shift_amount = index;
3773 index = fresh;
3774 __ movq(index.reg(), rcx);
3775 }
3776 // There could be references to ecx in the frame. Allocating will
3777 // spill them, otherwise spill explicitly.
3778 if (shift_amount.is_valid()) {
3779 frame_->Spill(rcx);
3780 } else {
3781 shift_amount = allocator()->Allocate(rcx);
3782 }
3783 ASSERT(shift_amount.is_register());
3784 ASSERT(shift_amount.reg().is(rcx));
3785 ASSERT(allocator_->count(rcx) == 1);
3786
3787 // We will mutate the index register and possibly the object register.
3788 // The case where they are somehow the same register is handled
3789 // because we only mutate them in the case where the receiver is a
3790 // heap object and the index is not.
3791 object.ToRegister();
3792 index.ToRegister();
3793 frame_->Spill(object.reg());
3794 frame_->Spill(index.reg());
3795
3796 // We need a single extra temporary register.
3797 Result temp = allocator()->Allocate();
3798 ASSERT(temp.is_valid());
3799
3800 // There is no virtual frame effect from here up to the final result
3801 // push.
3802
3803 // If the receiver is a smi trigger the slow case.
ager@chromium.org4af710e2009-09-15 12:20:11 +00003804 __ JumpIfSmi(object.reg(), &slow_case);
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00003805
3806 // If the index is negative or non-smi trigger the slow case.
ager@chromium.org4af710e2009-09-15 12:20:11 +00003807 __ JumpIfNotPositiveSmi(index.reg(), &slow_case);
3808
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00003809 // Untag the index.
ager@chromium.org4af710e2009-09-15 12:20:11 +00003810 __ SmiToInteger32(index.reg(), index.reg());
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00003811
3812 __ bind(&try_again_with_new_string);
3813 // Fetch the instance type of the receiver into rcx.
3814 __ movq(rcx, FieldOperand(object.reg(), HeapObject::kMapOffset));
3815 __ movzxbl(rcx, FieldOperand(rcx, Map::kInstanceTypeOffset));
3816 // If the receiver is not a string trigger the slow case.
3817 __ testb(rcx, Immediate(kIsNotStringMask));
3818 __ j(not_zero, &slow_case);
3819
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00003820 // Check for index out of range.
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00003821 __ cmpl(index.reg(), FieldOperand(object.reg(), String::kLengthOffset));
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00003822 __ j(greater_equal, &slow_case);
3823 // Reload the instance type (into the temp register this time)..
3824 __ movq(temp.reg(), FieldOperand(object.reg(), HeapObject::kMapOffset));
3825 __ movzxbl(temp.reg(), FieldOperand(temp.reg(), Map::kInstanceTypeOffset));
3826
3827 // We need special handling for non-flat strings.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003828 ASSERT_EQ(0, kSeqStringTag);
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00003829 __ testb(temp.reg(), Immediate(kStringRepresentationMask));
3830 __ j(not_zero, &not_a_flat_string);
3831 // Check for 1-byte or 2-byte string.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003832 ASSERT_EQ(0, kTwoByteStringTag);
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00003833 __ testb(temp.reg(), Immediate(kStringEncodingMask));
3834 __ j(not_zero, &ascii_string);
3835
3836 // 2-byte string.
3837 // Load the 2-byte character code into the temp register.
3838 __ movzxwl(temp.reg(), FieldOperand(object.reg(),
3839 index.reg(),
3840 times_2,
3841 SeqTwoByteString::kHeaderSize));
3842 __ jmp(&got_char_code);
3843
3844 // ASCII string.
3845 __ bind(&ascii_string);
3846 // Load the byte into the temp register.
3847 __ movzxbl(temp.reg(), FieldOperand(object.reg(),
3848 index.reg(),
3849 times_1,
3850 SeqAsciiString::kHeaderSize));
3851 __ bind(&got_char_code);
ager@chromium.org4af710e2009-09-15 12:20:11 +00003852 __ Integer32ToSmi(temp.reg(), temp.reg());
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00003853 __ jmp(&end);
3854
3855 // Handle non-flat strings.
3856 __ bind(&not_a_flat_string);
3857 __ and_(temp.reg(), Immediate(kStringRepresentationMask));
3858 __ cmpb(temp.reg(), Immediate(kConsStringTag));
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00003859 __ j(not_equal, &slow_case);
3860
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00003861 // ConsString.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003862 // Check that the right hand side is the empty string (ie if this is really a
3863 // flat string in a cons string). If that is not the case we would rather go
3864 // to the runtime system now, to flatten the string.
3865 __ movq(temp.reg(), FieldOperand(object.reg(), ConsString::kSecondOffset));
3866 __ CompareRoot(temp.reg(), Heap::kEmptyStringRootIndex);
3867 __ j(not_equal, &slow_case);
3868 // Get the first of the two strings.
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00003869 __ movq(object.reg(), FieldOperand(object.reg(), ConsString::kFirstOffset));
3870 __ jmp(&try_again_with_new_string);
3871
3872 __ bind(&slow_case);
3873 // Move the undefined value into the result register, which will
3874 // trigger the slow case.
ager@chromium.org18ad94b2009-09-02 08:22:29 +00003875 __ LoadRoot(temp.reg(), Heap::kUndefinedValueRootIndex);
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00003876
3877 __ bind(&end);
3878 frame_->Push(&temp);
ager@chromium.org3e875802009-06-29 08:26:34 +00003879}
3880
3881
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003882void CodeGenerator::GenerateCharFromCode(ZoneList<Expression*>* args) {
3883 Comment(masm_, "[ GenerateCharFromCode");
3884 ASSERT(args->length() == 1);
3885
3886 Load(args->at(0));
3887 Result code = frame_->Pop();
3888 code.ToRegister();
3889 ASSERT(code.is_valid());
3890
3891 Result temp = allocator()->Allocate();
3892 ASSERT(temp.is_valid());
3893
3894 JumpTarget slow_case;
3895 JumpTarget exit;
3896
3897 // Fast case of Heap::LookupSingleCharacterStringFromCode.
3898 Condition is_smi = __ CheckSmi(code.reg());
3899 slow_case.Branch(NegateCondition(is_smi), &code, not_taken);
3900
3901 __ SmiToInteger32(kScratchRegister, code.reg());
3902 __ cmpl(kScratchRegister, Immediate(String::kMaxAsciiCharCode));
3903 slow_case.Branch(above, &code, not_taken);
3904
3905 __ Move(temp.reg(), Factory::single_character_string_cache());
3906 __ movq(temp.reg(), FieldOperand(temp.reg(),
3907 kScratchRegister, times_pointer_size,
3908 FixedArray::kHeaderSize));
3909 __ CompareRoot(temp.reg(), Heap::kUndefinedValueRootIndex);
3910 slow_case.Branch(equal, &code, not_taken);
3911 code.Unuse();
3912
3913 frame_->Push(&temp);
3914 exit.Jump();
3915
3916 slow_case.Bind(&code);
3917 frame_->Push(&code);
3918 Result result = frame_->CallRuntime(Runtime::kCharFromCode, 1);
3919 frame_->Push(&result);
3920
3921 exit.Bind();
3922}
3923
3924
ager@chromium.org5aa501c2009-06-23 07:57:28 +00003925void CodeGenerator::GenerateIsNonNegativeSmi(ZoneList<Expression*>* args) {
ager@chromium.org3e875802009-06-29 08:26:34 +00003926 ASSERT(args->length() == 1);
3927 Load(args->at(0));
3928 Result value = frame_->Pop();
3929 value.ToRegister();
3930 ASSERT(value.is_valid());
ager@chromium.org4af710e2009-09-15 12:20:11 +00003931 Condition positive_smi = masm_->CheckPositiveSmi(value.reg());
ager@chromium.org3e875802009-06-29 08:26:34 +00003932 value.Unuse();
ager@chromium.org4af710e2009-09-15 12:20:11 +00003933 destination()->Split(positive_smi);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00003934}
3935
ager@chromium.org3e875802009-06-29 08:26:34 +00003936
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003937// Generates the Math.pow method - currently just calls runtime.
3938void CodeGenerator::GenerateMathPow(ZoneList<Expression*>* args) {
3939 ASSERT(args->length() == 2);
3940 Load(args->at(0));
3941 Load(args->at(1));
3942 Result res = frame_->CallRuntime(Runtime::kMath_pow, 2);
3943 frame_->Push(&res);
3944}
3945
3946
3947// Generates the Math.sqrt method - currently just calls runtime.
3948void CodeGenerator::GenerateMathSqrt(ZoneList<Expression*>* args) {
3949 ASSERT(args->length() == 1);
3950 Load(args->at(0));
3951 Result res = frame_->CallRuntime(Runtime::kMath_sqrt, 1);
3952 frame_->Push(&res);
3953}
3954
3955
ager@chromium.org5aa501c2009-06-23 07:57:28 +00003956void CodeGenerator::GenerateIsSmi(ZoneList<Expression*>* args) {
ager@chromium.org3e875802009-06-29 08:26:34 +00003957 ASSERT(args->length() == 1);
3958 Load(args->at(0));
3959 Result value = frame_->Pop();
3960 value.ToRegister();
3961 ASSERT(value.is_valid());
ager@chromium.org4af710e2009-09-15 12:20:11 +00003962 Condition is_smi = masm_->CheckSmi(value.reg());
ager@chromium.org3e875802009-06-29 08:26:34 +00003963 value.Unuse();
ager@chromium.org4af710e2009-09-15 12:20:11 +00003964 destination()->Split(is_smi);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00003965}
3966
ager@chromium.org3e875802009-06-29 08:26:34 +00003967
3968void CodeGenerator::GenerateLog(ZoneList<Expression*>* args) {
3969 // Conditionally generate a log call.
3970 // Args:
3971 // 0 (literal string): The type of logging (corresponds to the flags).
3972 // This is used to determine whether or not to generate the log call.
3973 // 1 (string): Format string. Access the string at argument index 2
3974 // with '%2s' (see Logger::LogRuntime for all the formats).
3975 // 2 (array): Arguments to the format string.
3976 ASSERT_EQ(args->length(), 3);
3977#ifdef ENABLE_LOGGING_AND_PROFILING
3978 if (ShouldGenerateLog(args->at(0))) {
3979 Load(args->at(1));
3980 Load(args->at(2));
3981 frame_->CallRuntime(Runtime::kLog, 2);
3982 }
3983#endif
3984 // Finally, we're expected to leave a value on the top of the stack.
3985 frame_->Push(Factory::undefined_value());
ager@chromium.orgeadaf222009-06-16 09:43:10 +00003986}
3987
ager@chromium.org3e875802009-06-29 08:26:34 +00003988
ager@chromium.org5aa501c2009-06-23 07:57:28 +00003989void CodeGenerator::GenerateObjectEquals(ZoneList<Expression*>* args) {
ager@chromium.org3e875802009-06-29 08:26:34 +00003990 ASSERT(args->length() == 2);
3991
3992 // Load the two objects into registers and perform the comparison.
3993 Load(args->at(0));
3994 Load(args->at(1));
3995 Result right = frame_->Pop();
3996 Result left = frame_->Pop();
3997 right.ToRegister();
3998 left.ToRegister();
3999 __ cmpq(right.reg(), left.reg());
4000 right.Unuse();
4001 left.Unuse();
4002 destination()->Split(equal);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00004003}
4004
ager@chromium.org3e875802009-06-29 08:26:34 +00004005
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +00004006void CodeGenerator::GenerateGetFramePointer(ZoneList<Expression*>* args) {
4007 ASSERT(args->length() == 0);
ager@chromium.org4af710e2009-09-15 12:20:11 +00004008 // RBP value is aligned, so it should be tagged as a smi (without necesarily
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004009 // being padded as a smi, so it should not be treated as a smi.).
ager@chromium.org4af710e2009-09-15 12:20:11 +00004010 ASSERT(kSmiTag == 0 && kSmiTagSize == 1);
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +00004011 Result rbp_as_smi = allocator_->Allocate();
4012 ASSERT(rbp_as_smi.is_valid());
4013 __ movq(rbp_as_smi.reg(), rbp);
4014 frame_->Push(&rbp_as_smi);
4015}
4016
4017
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004018void CodeGenerator::GenerateRandomPositiveSmi(ZoneList<Expression*>* args) {
4019 ASSERT(args->length() == 0);
4020 frame_->SpillAll();
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +00004021 __ push(rsi);
ager@chromium.org3e875802009-06-29 08:26:34 +00004022
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004023 static const int num_arguments = 0;
4024 __ PrepareCallCFunction(num_arguments);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004025
4026 // Call V8::RandomPositiveSmi().
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004027 __ CallCFunction(ExternalReference::random_positive_smi_function(),
4028 num_arguments);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004029
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +00004030 __ pop(rsi);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004031 Result result = allocator_->Allocate(rax);
4032 frame_->Push(&result);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00004033}
4034
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004035
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004036void CodeGenerator::GenerateRegExpExec(ZoneList<Expression*>* args) {
4037 ASSERT_EQ(args->length(), 4);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00004038
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004039 // Load the arguments on the stack and call the runtime system.
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00004040 Load(args->at(0));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004041 Load(args->at(1));
4042 Load(args->at(2));
4043 Load(args->at(3));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004044 RegExpExecStub stub;
4045 Result result = frame_->CallStub(&stub, 4);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004046 frame_->Push(&result);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00004047}
4048
ager@chromium.org3e875802009-06-29 08:26:34 +00004049
ager@chromium.org5c838252010-02-19 08:53:10 +00004050void CodeGenerator::GenerateNumberToString(ZoneList<Expression*>* args) {
4051 ASSERT_EQ(args->length(), 1);
4052
4053 // Load the argument on the stack and jump to the runtime.
4054 Load(args->at(0));
4055
fschneider@chromium.org086aac62010-03-17 13:18:24 +00004056 NumberToStringStub stub;
4057 Result result = frame_->CallStub(&stub, 1);
4058 frame_->Push(&result);
ager@chromium.org5c838252010-02-19 08:53:10 +00004059}
4060
4061
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004062void CodeGenerator::GenerateMathSin(ZoneList<Expression*>* args) {
4063 ASSERT_EQ(args->length(), 1);
4064 // Load the argument on the stack and jump to the runtime.
4065 Load(args->at(0));
4066 Result answer = frame_->CallRuntime(Runtime::kMath_sin, 1);
4067 frame_->Push(&answer);
4068}
4069
4070
4071void CodeGenerator::GenerateMathCos(ZoneList<Expression*>* args) {
4072 ASSERT_EQ(args->length(), 1);
4073 // Load the argument on the stack and jump to the runtime.
4074 Load(args->at(0));
4075 Result answer = frame_->CallRuntime(Runtime::kMath_cos, 1);
4076 frame_->Push(&answer);
4077}
4078
4079
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00004080void CodeGenerator::GenerateStringAdd(ZoneList<Expression*>* args) {
4081 ASSERT_EQ(2, args->length());
4082
4083 Load(args->at(0));
4084 Load(args->at(1));
4085
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00004086 StringAddStub stub(NO_STRING_ADD_FLAGS);
4087 Result answer = frame_->CallStub(&stub, 2);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00004088 frame_->Push(&answer);
4089}
4090
4091
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004092void CodeGenerator::GenerateSubString(ZoneList<Expression*>* args) {
4093 ASSERT_EQ(3, args->length());
4094
4095 Load(args->at(0));
4096 Load(args->at(1));
4097 Load(args->at(2));
4098
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004099 SubStringStub stub;
4100 Result answer = frame_->CallStub(&stub, 3);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004101 frame_->Push(&answer);
4102}
4103
4104
4105void CodeGenerator::GenerateStringCompare(ZoneList<Expression*>* args) {
4106 ASSERT_EQ(2, args->length());
4107
4108 Load(args->at(0));
4109 Load(args->at(1));
4110
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004111 StringCompareStub stub;
4112 Result answer = frame_->CallStub(&stub, 2);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004113 frame_->Push(&answer);
4114}
4115
4116
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004117void CodeGenerator::GenerateClassOf(ZoneList<Expression*>* args) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004118 ASSERT(args->length() == 1);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00004119 JumpTarget leave, null, function, non_function_constructor;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004120 Load(args->at(0)); // Load the object.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00004121 Result obj = frame_->Pop();
4122 obj.ToRegister();
4123 frame_->Spill(obj.reg());
4124
4125 // If the object is a smi, we return null.
ager@chromium.org4af710e2009-09-15 12:20:11 +00004126 Condition is_smi = masm_->CheckSmi(obj.reg());
4127 null.Branch(is_smi);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00004128
4129 // Check that the object is a JS object but take special care of JS
4130 // functions to make sure they have 'Function' as their class.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00004131
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00004132 __ CmpObjectType(obj.reg(), FIRST_JS_OBJECT_TYPE, obj.reg());
kasperl@chromium.orge959c182009-07-27 08:59:04 +00004133 null.Branch(below);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00004134
4135 // As long as JS_FUNCTION_TYPE is the last instance type and it is
4136 // right after LAST_JS_OBJECT_TYPE, we can avoid checking for
4137 // LAST_JS_OBJECT_TYPE.
4138 ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
4139 ASSERT(JS_FUNCTION_TYPE == LAST_JS_OBJECT_TYPE + 1);
4140 __ CmpInstanceType(obj.reg(), JS_FUNCTION_TYPE);
4141 function.Branch(equal);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00004142
4143 // Check if the constructor in the map is a function.
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00004144 __ movq(obj.reg(), FieldOperand(obj.reg(), Map::kConstructorOffset));
4145 __ CmpObjectType(obj.reg(), JS_FUNCTION_TYPE, kScratchRegister);
4146 non_function_constructor.Branch(not_equal);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00004147
4148 // The obj register now contains the constructor function. Grab the
4149 // instance class name from there.
4150 __ movq(obj.reg(),
4151 FieldOperand(obj.reg(), JSFunction::kSharedFunctionInfoOffset));
4152 __ movq(obj.reg(),
4153 FieldOperand(obj.reg(),
4154 SharedFunctionInfo::kInstanceClassNameOffset));
4155 frame_->Push(&obj);
4156 leave.Jump();
4157
4158 // Functions have class 'Function'.
4159 function.Bind();
4160 frame_->Push(Factory::function_class_symbol());
4161 leave.Jump();
4162
4163 // Objects with a non-function constructor have class 'Object'.
4164 non_function_constructor.Bind();
4165 frame_->Push(Factory::Object_symbol());
4166 leave.Jump();
4167
4168 // Non-JS objects have class null.
4169 null.Bind();
4170 frame_->Push(Factory::null_value());
4171
4172 // All done.
4173 leave.Bind();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004174}
4175
4176
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004177void CodeGenerator::GenerateSetValueOf(ZoneList<Expression*>* args) {
ager@chromium.org3e875802009-06-29 08:26:34 +00004178 ASSERT(args->length() == 2);
4179 JumpTarget leave;
4180 Load(args->at(0)); // Load the object.
4181 Load(args->at(1)); // Load the value.
4182 Result value = frame_->Pop();
4183 Result object = frame_->Pop();
4184 value.ToRegister();
4185 object.ToRegister();
4186
4187 // if (object->IsSmi()) return value.
ager@chromium.org4af710e2009-09-15 12:20:11 +00004188 Condition is_smi = masm_->CheckSmi(object.reg());
4189 leave.Branch(is_smi, &value);
ager@chromium.org3e875802009-06-29 08:26:34 +00004190
4191 // It is a heap object - get its map.
4192 Result scratch = allocator_->Allocate();
4193 ASSERT(scratch.is_valid());
4194 // if (!object->IsJSValue()) return value.
4195 __ CmpObjectType(object.reg(), JS_VALUE_TYPE, scratch.reg());
4196 leave.Branch(not_equal, &value);
4197
4198 // Store the value.
4199 __ movq(FieldOperand(object.reg(), JSValue::kValueOffset), value.reg());
4200 // Update the write barrier. Save the value as it will be
4201 // overwritten by the write barrier code and is needed afterward.
4202 Result duplicate_value = allocator_->Allocate();
4203 ASSERT(duplicate_value.is_valid());
4204 __ movq(duplicate_value.reg(), value.reg());
4205 // The object register is also overwritten by the write barrier and
4206 // possibly aliased in the frame.
4207 frame_->Spill(object.reg());
4208 __ RecordWrite(object.reg(), JSValue::kValueOffset, duplicate_value.reg(),
4209 scratch.reg());
4210 object.Unuse();
4211 scratch.Unuse();
4212 duplicate_value.Unuse();
4213
4214 // Leave.
4215 leave.Bind(&value);
4216 frame_->Push(&value);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00004217}
4218
ager@chromium.org3e875802009-06-29 08:26:34 +00004219
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004220void CodeGenerator::GenerateValueOf(ZoneList<Expression*>* args) {
ager@chromium.org3e875802009-06-29 08:26:34 +00004221 ASSERT(args->length() == 1);
4222 JumpTarget leave;
4223 Load(args->at(0)); // Load the object.
4224 frame_->Dup();
4225 Result object = frame_->Pop();
4226 object.ToRegister();
4227 ASSERT(object.is_valid());
4228 // if (object->IsSmi()) return object.
ager@chromium.org4af710e2009-09-15 12:20:11 +00004229 Condition is_smi = masm_->CheckSmi(object.reg());
4230 leave.Branch(is_smi);
ager@chromium.org3e875802009-06-29 08:26:34 +00004231 // It is a heap object - get map.
4232 Result temp = allocator()->Allocate();
4233 ASSERT(temp.is_valid());
4234 // if (!object->IsJSValue()) return object.
4235 __ CmpObjectType(object.reg(), JS_VALUE_TYPE, temp.reg());
4236 leave.Branch(not_equal);
4237 __ movq(temp.reg(), FieldOperand(object.reg(), JSValue::kValueOffset));
4238 object.Unuse();
4239 frame_->SetElementAt(0, &temp);
4240 leave.Bind();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00004241}
4242
ager@chromium.org3e875802009-06-29 08:26:34 +00004243
ager@chromium.orgeadaf222009-06-16 09:43:10 +00004244// -----------------------------------------------------------------------------
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004245// CodeGenerator implementation of Expressions
ager@chromium.orgeadaf222009-06-16 09:43:10 +00004246
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004247void CodeGenerator::LoadAndSpill(Expression* expression) {
ager@chromium.org3e875802009-06-29 08:26:34 +00004248 // TODO(x64): No architecture specific code. Move to shared location.
4249 ASSERT(in_spilled_code());
4250 set_in_spilled_code(false);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004251 Load(expression);
ager@chromium.org3e875802009-06-29 08:26:34 +00004252 frame_->SpillAll();
4253 set_in_spilled_code(true);
4254}
4255
4256
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004257void CodeGenerator::Load(Expression* expr) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004258#ifdef DEBUG
4259 int original_height = frame_->height();
4260#endif
4261 ASSERT(!in_spilled_code());
4262 JumpTarget true_target;
4263 JumpTarget false_target;
4264 ControlDestination dest(&true_target, &false_target, true);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004265 LoadCondition(expr, &dest, false);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004266
4267 if (dest.false_was_fall_through()) {
4268 // The false target was just bound.
4269 JumpTarget loaded;
4270 frame_->Push(Factory::false_value());
4271 // There may be dangling jumps to the true target.
4272 if (true_target.is_linked()) {
4273 loaded.Jump();
4274 true_target.Bind();
4275 frame_->Push(Factory::true_value());
4276 loaded.Bind();
4277 }
4278
4279 } else if (dest.is_used()) {
4280 // There is true, and possibly false, control flow (with true as
4281 // the fall through).
4282 JumpTarget loaded;
4283 frame_->Push(Factory::true_value());
4284 if (false_target.is_linked()) {
4285 loaded.Jump();
4286 false_target.Bind();
4287 frame_->Push(Factory::false_value());
4288 loaded.Bind();
4289 }
4290
4291 } else {
4292 // We have a valid value on top of the frame, but we still may
4293 // have dangling jumps to the true and false targets from nested
4294 // subexpressions (eg, the left subexpressions of the
4295 // short-circuited boolean operators).
4296 ASSERT(has_valid_frame());
4297 if (true_target.is_linked() || false_target.is_linked()) {
4298 JumpTarget loaded;
4299 loaded.Jump(); // Don't lose the current TOS.
4300 if (true_target.is_linked()) {
4301 true_target.Bind();
4302 frame_->Push(Factory::true_value());
4303 if (false_target.is_linked()) {
4304 loaded.Jump();
4305 }
4306 }
4307 if (false_target.is_linked()) {
4308 false_target.Bind();
4309 frame_->Push(Factory::false_value());
4310 }
4311 loaded.Bind();
4312 }
4313 }
4314
4315 ASSERT(has_valid_frame());
4316 ASSERT(frame_->height() == original_height + 1);
4317}
4318
4319
4320// Emit code to load the value of an expression to the top of the
4321// frame. If the expression is boolean-valued it may be compiled (or
4322// partially compiled) into control flow to the control destination.
4323// If force_control is true, control flow is forced.
4324void CodeGenerator::LoadCondition(Expression* x,
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004325 ControlDestination* dest,
4326 bool force_control) {
4327 ASSERT(!in_spilled_code());
4328 int original_height = frame_->height();
4329
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004330 { CodeGenState new_state(this, dest);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004331 Visit(x);
4332
4333 // If we hit a stack overflow, we may not have actually visited
4334 // the expression. In that case, we ensure that we have a
4335 // valid-looking frame state because we will continue to generate
4336 // code as we unwind the C++ stack.
4337 //
4338 // It's possible to have both a stack overflow and a valid frame
4339 // state (eg, a subexpression overflowed, visiting it returned
4340 // with a dummied frame state, and visiting this expression
4341 // returned with a normal-looking state).
4342 if (HasStackOverflow() &&
4343 !dest->is_used() &&
4344 frame_->height() == original_height) {
4345 dest->Goto(true);
4346 }
4347 }
4348
4349 if (force_control && !dest->is_used()) {
4350 // Convert the TOS value into flow to the control destination.
4351 // TODO(X64): Make control flow to control destinations work.
4352 ToBoolean(dest);
4353 }
4354
4355 ASSERT(!(force_control && !dest->is_used()));
4356 ASSERT(dest->is_used() || frame_->height() == original_height + 1);
4357}
4358
4359
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004360// ECMA-262, section 9.2, page 30: ToBoolean(). Pop the top of stack and
4361// convert it to a boolean in the condition code register or jump to
4362// 'false_target'/'true_target' as appropriate.
4363void CodeGenerator::ToBoolean(ControlDestination* dest) {
4364 Comment cmnt(masm_, "[ ToBoolean");
4365
4366 // The value to convert should be popped from the frame.
4367 Result value = frame_->Pop();
4368 value.ToRegister();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004369
ager@chromium.org5c838252010-02-19 08:53:10 +00004370 if (value.is_number()) {
4371 Comment cmnt(masm_, "ONLY_NUMBER");
4372 // Fast case if NumberInfo indicates only numbers.
4373 if (FLAG_debug_code) {
4374 __ AbortIfNotNumber(value.reg(), "ToBoolean operand is not a number.");
4375 }
4376 // Smi => false iff zero.
4377 __ SmiCompare(value.reg(), Smi::FromInt(0));
4378 dest->false_target()->Branch(equal);
4379 Condition is_smi = masm_->CheckSmi(value.reg());
4380 dest->true_target()->Branch(is_smi);
4381 __ fldz();
4382 __ fld_d(FieldOperand(value.reg(), HeapNumber::kValueOffset));
4383 __ FCmp();
4384 value.Unuse();
4385 dest->Split(not_zero);
4386 } else {
4387 // Fast case checks.
4388 // 'false' => false.
4389 __ CompareRoot(value.reg(), Heap::kFalseValueRootIndex);
4390 dest->false_target()->Branch(equal);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004391
ager@chromium.org5c838252010-02-19 08:53:10 +00004392 // 'true' => true.
4393 __ CompareRoot(value.reg(), Heap::kTrueValueRootIndex);
4394 dest->true_target()->Branch(equal);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004395
ager@chromium.org5c838252010-02-19 08:53:10 +00004396 // 'undefined' => false.
4397 __ CompareRoot(value.reg(), Heap::kUndefinedValueRootIndex);
4398 dest->false_target()->Branch(equal);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004399
ager@chromium.org5c838252010-02-19 08:53:10 +00004400 // Smi => false iff zero.
4401 __ SmiCompare(value.reg(), Smi::FromInt(0));
4402 dest->false_target()->Branch(equal);
4403 Condition is_smi = masm_->CheckSmi(value.reg());
4404 dest->true_target()->Branch(is_smi);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004405
ager@chromium.org5c838252010-02-19 08:53:10 +00004406 // Call the stub for all other cases.
4407 frame_->Push(&value); // Undo the Pop() from above.
4408 ToBooleanStub stub;
4409 Result temp = frame_->CallStub(&stub, 1);
4410 // Convert the result to a condition code.
4411 __ testq(temp.reg(), temp.reg());
4412 temp.Unuse();
4413 dest->Split(not_equal);
4414 }
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004415}
4416
4417
4418void CodeGenerator::LoadUnsafeSmi(Register target, Handle<Object> value) {
4419 UNIMPLEMENTED();
4420 // TODO(X64): Implement security policy for loads of smis.
4421}
4422
4423
4424bool CodeGenerator::IsUnsafeSmi(Handle<Object> value) {
4425 return false;
4426}
4427
4428//------------------------------------------------------------------------------
4429// CodeGenerator implementation of variables, lookups, and stores.
4430
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004431Reference::Reference(CodeGenerator* cgen,
4432 Expression* expression,
4433 bool persist_after_get)
4434 : cgen_(cgen),
4435 expression_(expression),
4436 type_(ILLEGAL),
4437 persist_after_get_(persist_after_get) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004438 cgen->LoadReference(this);
4439}
4440
4441
4442Reference::~Reference() {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004443 ASSERT(is_unloaded() || is_illegal());
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004444}
4445
4446
4447void CodeGenerator::LoadReference(Reference* ref) {
4448 // References are loaded from both spilled and unspilled code. Set the
4449 // state to unspilled to allow that (and explicitly spill after
4450 // construction at the construction sites).
4451 bool was_in_spilled_code = in_spilled_code_;
4452 in_spilled_code_ = false;
4453
4454 Comment cmnt(masm_, "[ LoadReference");
4455 Expression* e = ref->expression();
4456 Property* property = e->AsProperty();
4457 Variable* var = e->AsVariableProxy()->AsVariable();
4458
4459 if (property != NULL) {
4460 // The expression is either a property or a variable proxy that rewrites
4461 // to a property.
4462 Load(property->obj());
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004463 if (property->key()->IsPropertyName()) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004464 ref->set_type(Reference::NAMED);
4465 } else {
4466 Load(property->key());
4467 ref->set_type(Reference::KEYED);
4468 }
4469 } else if (var != NULL) {
4470 // The expression is a variable proxy that does not rewrite to a
4471 // property. Global variables are treated as named property references.
4472 if (var->is_global()) {
4473 LoadGlobal();
4474 ref->set_type(Reference::NAMED);
4475 } else {
4476 ASSERT(var->slot() != NULL);
4477 ref->set_type(Reference::SLOT);
4478 }
4479 } else {
4480 // Anything else is a runtime error.
4481 Load(e);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00004482 frame_->CallRuntime(Runtime::kThrowReferenceError, 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004483 }
4484
4485 in_spilled_code_ = was_in_spilled_code;
4486}
4487
4488
4489void CodeGenerator::UnloadReference(Reference* ref) {
4490 // Pop a reference from the stack while preserving TOS.
4491 Comment cmnt(masm_, "[ UnloadReference");
4492 frame_->Nip(ref->size());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004493 ref->set_unloaded();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004494}
4495
4496
4497Operand CodeGenerator::SlotOperand(Slot* slot, Register tmp) {
4498 // Currently, this assertion will fail if we try to assign to
4499 // a constant variable that is constant because it is read-only
4500 // (such as the variable referring to a named function expression).
4501 // We need to implement assignments to read-only variables.
4502 // Ideally, we should do this during AST generation (by converting
4503 // such assignments into expression statements); however, in general
4504 // we may not be able to make the decision until past AST generation,
4505 // that is when the entire program is known.
4506 ASSERT(slot != NULL);
4507 int index = slot->index();
4508 switch (slot->type()) {
4509 case Slot::PARAMETER:
4510 return frame_->ParameterAt(index);
4511
4512 case Slot::LOCAL:
4513 return frame_->LocalAt(index);
4514
4515 case Slot::CONTEXT: {
4516 // Follow the context chain if necessary.
4517 ASSERT(!tmp.is(rsi)); // do not overwrite context register
4518 Register context = rsi;
4519 int chain_length = scope()->ContextChainLength(slot->var()->scope());
4520 for (int i = 0; i < chain_length; i++) {
4521 // Load the closure.
4522 // (All contexts, even 'with' contexts, have a closure,
4523 // and it is the same for all contexts inside a function.
4524 // There is no need to go to the function context first.)
4525 __ movq(tmp, ContextOperand(context, Context::CLOSURE_INDEX));
4526 // Load the function context (which is the incoming, outer context).
4527 __ movq(tmp, FieldOperand(tmp, JSFunction::kContextOffset));
4528 context = tmp;
4529 }
4530 // We may have a 'with' context now. Get the function context.
4531 // (In fact this mov may never be the needed, since the scope analysis
4532 // may not permit a direct context access in this case and thus we are
4533 // always at a function context. However it is safe to dereference be-
4534 // cause the function context of a function context is itself. Before
4535 // deleting this mov we should try to create a counter-example first,
4536 // though...)
4537 __ movq(tmp, ContextOperand(context, Context::FCONTEXT_INDEX));
4538 return ContextOperand(tmp, index);
4539 }
4540
4541 default:
4542 UNREACHABLE();
4543 return Operand(rsp, 0);
4544 }
4545}
4546
4547
4548Operand CodeGenerator::ContextSlotOperandCheckExtensions(Slot* slot,
4549 Result tmp,
4550 JumpTarget* slow) {
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00004551 ASSERT(slot->type() == Slot::CONTEXT);
4552 ASSERT(tmp.is_register());
4553 Register context = rsi;
4554
4555 for (Scope* s = scope(); s != slot->var()->scope(); s = s->outer_scope()) {
4556 if (s->num_heap_slots() > 0) {
4557 if (s->calls_eval()) {
4558 // Check that extension is NULL.
4559 __ cmpq(ContextOperand(context, Context::EXTENSION_INDEX),
4560 Immediate(0));
4561 slow->Branch(not_equal, not_taken);
4562 }
4563 __ movq(tmp.reg(), ContextOperand(context, Context::CLOSURE_INDEX));
4564 __ movq(tmp.reg(), FieldOperand(tmp.reg(), JSFunction::kContextOffset));
4565 context = tmp.reg();
4566 }
4567 }
4568 // Check that last extension is NULL.
4569 __ cmpq(ContextOperand(context, Context::EXTENSION_INDEX), Immediate(0));
4570 slow->Branch(not_equal, not_taken);
4571 __ movq(tmp.reg(), ContextOperand(context, Context::FCONTEXT_INDEX));
4572 return ContextOperand(tmp.reg(), slot->index());
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004573}
4574
4575
4576void CodeGenerator::LoadFromSlot(Slot* slot, TypeofState typeof_state) {
4577 if (slot->type() == Slot::LOOKUP) {
4578 ASSERT(slot->var()->is_dynamic());
4579
4580 JumpTarget slow;
4581 JumpTarget done;
4582 Result value;
4583
4584 // Generate fast-case code for variables that might be shadowed by
4585 // eval-introduced variables. Eval is used a lot without
4586 // introducing variables. In those cases, we do not want to
4587 // perform a runtime call for all variables in the scope
4588 // containing the eval.
4589 if (slot->var()->mode() == Variable::DYNAMIC_GLOBAL) {
4590 value = LoadFromGlobalSlotCheckExtensions(slot, typeof_state, &slow);
4591 // If there was no control flow to slow, we can exit early.
4592 if (!slow.is_linked()) {
4593 frame_->Push(&value);
4594 return;
4595 }
4596
4597 done.Jump(&value);
4598
4599 } else if (slot->var()->mode() == Variable::DYNAMIC_LOCAL) {
4600 Slot* potential_slot = slot->var()->local_if_not_shadowed()->slot();
4601 // Only generate the fast case for locals that rewrite to slots.
4602 // This rules out argument loads.
4603 if (potential_slot != NULL) {
4604 // Allocate a fresh register to use as a temp in
4605 // ContextSlotOperandCheckExtensions and to hold the result
4606 // value.
4607 value = allocator_->Allocate();
4608 ASSERT(value.is_valid());
4609 __ movq(value.reg(),
4610 ContextSlotOperandCheckExtensions(potential_slot,
4611 value,
4612 &slow));
4613 if (potential_slot->var()->mode() == Variable::CONST) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004614 __ CompareRoot(value.reg(), Heap::kTheHoleValueRootIndex);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004615 done.Branch(not_equal, &value);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004616 __ LoadRoot(value.reg(), Heap::kUndefinedValueRootIndex);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004617 }
4618 // There is always control flow to slow from
4619 // ContextSlotOperandCheckExtensions so we have to jump around
4620 // it.
4621 done.Jump(&value);
4622 }
4623 }
4624
4625 slow.Bind();
4626 // A runtime call is inevitable. We eagerly sync frame elements
4627 // to memory so that we can push the arguments directly into place
4628 // on top of the frame.
4629 frame_->SyncRange(0, frame_->element_count() - 1);
4630 frame_->EmitPush(rsi);
4631 __ movq(kScratchRegister, slot->var()->name(), RelocInfo::EMBEDDED_OBJECT);
4632 frame_->EmitPush(kScratchRegister);
4633 if (typeof_state == INSIDE_TYPEOF) {
ager@chromium.org3e875802009-06-29 08:26:34 +00004634 value =
4635 frame_->CallRuntime(Runtime::kLoadContextSlotNoReferenceError, 2);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004636 } else {
ager@chromium.org3e875802009-06-29 08:26:34 +00004637 value = frame_->CallRuntime(Runtime::kLoadContextSlot, 2);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004638 }
4639
4640 done.Bind(&value);
4641 frame_->Push(&value);
4642
4643 } else if (slot->var()->mode() == Variable::CONST) {
4644 // Const slots may contain 'the hole' value (the constant hasn't been
4645 // initialized yet) which needs to be converted into the 'undefined'
4646 // value.
4647 //
4648 // We currently spill the virtual frame because constants use the
4649 // potentially unsafe direct-frame access of SlotOperand.
4650 VirtualFrame::SpilledScope spilled_scope;
4651 Comment cmnt(masm_, "[ Load const");
4652 JumpTarget exit;
4653 __ movq(rcx, SlotOperand(slot, rcx));
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004654 __ CompareRoot(rcx, Heap::kTheHoleValueRootIndex);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004655 exit.Branch(not_equal);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004656 __ LoadRoot(rcx, Heap::kUndefinedValueRootIndex);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004657 exit.Bind();
4658 frame_->EmitPush(rcx);
4659
4660 } else if (slot->type() == Slot::PARAMETER) {
4661 frame_->PushParameterAt(slot->index());
4662
4663 } else if (slot->type() == Slot::LOCAL) {
4664 frame_->PushLocalAt(slot->index());
4665
4666 } else {
4667 // The other remaining slot types (LOOKUP and GLOBAL) cannot reach
4668 // here.
4669 //
4670 // The use of SlotOperand below is safe for an unspilled frame
4671 // because it will always be a context slot.
4672 ASSERT(slot->type() == Slot::CONTEXT);
4673 Result temp = allocator_->Allocate();
4674 ASSERT(temp.is_valid());
4675 __ movq(temp.reg(), SlotOperand(slot, temp.reg()));
4676 frame_->Push(&temp);
4677 }
4678}
4679
4680
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004681void CodeGenerator::LoadFromSlotCheckForArguments(Slot* slot,
4682 TypeofState state) {
4683 LoadFromSlot(slot, state);
4684
4685 // Bail out quickly if we're not using lazy arguments allocation.
4686 if (ArgumentsMode() != LAZY_ARGUMENTS_ALLOCATION) return;
4687
4688 // ... or if the slot isn't a non-parameter arguments slot.
4689 if (slot->type() == Slot::PARAMETER || !slot->is_arguments()) return;
4690
4691 // Pop the loaded value from the stack.
4692 Result value = frame_->Pop();
4693
4694 // If the loaded value is a constant, we know if the arguments
4695 // object has been lazily loaded yet.
4696 if (value.is_constant()) {
4697 if (value.handle()->IsTheHole()) {
4698 Result arguments = StoreArgumentsObject(false);
4699 frame_->Push(&arguments);
4700 } else {
4701 frame_->Push(&value);
4702 }
4703 return;
4704 }
4705
4706 // The loaded value is in a register. If it is the sentinel that
4707 // indicates that we haven't loaded the arguments object yet, we
4708 // need to do it now.
4709 JumpTarget exit;
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004710 __ CompareRoot(value.reg(), Heap::kTheHoleValueRootIndex);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004711 frame_->Push(&value);
4712 exit.Branch(not_equal);
4713 Result arguments = StoreArgumentsObject(false);
4714 frame_->SetElementAt(0, &arguments);
4715 exit.Bind();
4716}
4717
4718
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004719void CodeGenerator::StoreToSlot(Slot* slot, InitState init_state) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004720 if (slot->type() == Slot::LOOKUP) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004721 ASSERT(slot->var()->is_dynamic());
4722
4723 // For now, just do a runtime call. Since the call is inevitable,
4724 // we eagerly sync the virtual frame so we can directly push the
4725 // arguments into place.
4726 frame_->SyncRange(0, frame_->element_count() - 1);
4727
ager@chromium.org3e875802009-06-29 08:26:34 +00004728 frame_->EmitPush(rsi);
4729 frame_->EmitPush(slot->var()->name());
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004730
4731 Result value;
4732 if (init_state == CONST_INIT) {
4733 // Same as the case for a normal store, but ignores attribute
4734 // (e.g. READ_ONLY) of context slot so that we can initialize const
4735 // properties (introduced via eval("const foo = (some expr);")). Also,
4736 // uses the current function context instead of the top context.
4737 //
4738 // Note that we must declare the foo upon entry of eval(), via a
4739 // context slot declaration, but we cannot initialize it at the same
4740 // time, because the const declaration may be at the end of the eval
4741 // code (sigh...) and the const variable may have been used before
4742 // (where its value is 'undefined'). Thus, we can only do the
4743 // initialization when we actually encounter the expression and when
4744 // the expression operands are defined and valid, and thus we need the
4745 // split into 2 operations: declaration of the context slot followed
4746 // by initialization.
4747 value = frame_->CallRuntime(Runtime::kInitializeConstContextSlot, 3);
4748 } else {
4749 value = frame_->CallRuntime(Runtime::kStoreContextSlot, 3);
4750 }
4751 // Storing a variable must keep the (new) value on the expression
4752 // stack. This is necessary for compiling chained assignment
4753 // expressions.
4754 frame_->Push(&value);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004755 } else {
4756 ASSERT(!slot->var()->is_dynamic());
4757
4758 JumpTarget exit;
4759 if (init_state == CONST_INIT) {
4760 ASSERT(slot->var()->mode() == Variable::CONST);
4761 // Only the first const initialization must be executed (the slot
4762 // still contains 'the hole' value). When the assignment is executed,
4763 // the code is identical to a normal store (see below).
4764 //
4765 // We spill the frame in the code below because the direct-frame
4766 // access of SlotOperand is potentially unsafe with an unspilled
4767 // frame.
4768 VirtualFrame::SpilledScope spilled_scope;
4769 Comment cmnt(masm_, "[ Init const");
4770 __ movq(rcx, SlotOperand(slot, rcx));
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004771 __ CompareRoot(rcx, Heap::kTheHoleValueRootIndex);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004772 exit.Branch(not_equal);
4773 }
4774
4775 // We must execute the store. Storing a variable must keep the (new)
4776 // value on the stack. This is necessary for compiling assignment
4777 // expressions.
4778 //
4779 // Note: We will reach here even with slot->var()->mode() ==
4780 // Variable::CONST because of const declarations which will initialize
4781 // consts to 'the hole' value and by doing so, end up calling this code.
4782 if (slot->type() == Slot::PARAMETER) {
4783 frame_->StoreToParameterAt(slot->index());
4784 } else if (slot->type() == Slot::LOCAL) {
4785 frame_->StoreToLocalAt(slot->index());
4786 } else {
4787 // The other slot types (LOOKUP and GLOBAL) cannot reach here.
4788 //
4789 // The use of SlotOperand below is safe for an unspilled frame
4790 // because the slot is a context slot.
4791 ASSERT(slot->type() == Slot::CONTEXT);
4792 frame_->Dup();
4793 Result value = frame_->Pop();
4794 value.ToRegister();
4795 Result start = allocator_->Allocate();
4796 ASSERT(start.is_valid());
4797 __ movq(SlotOperand(slot, start.reg()), value.reg());
4798 // RecordWrite may destroy the value registers.
4799 //
4800 // TODO(204): Avoid actually spilling when the value is not
4801 // needed (probably the common case).
4802 frame_->Spill(value.reg());
4803 int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize;
4804 Result temp = allocator_->Allocate();
4805 ASSERT(temp.is_valid());
4806 __ RecordWrite(start.reg(), offset, value.reg(), temp.reg());
4807 // The results start, value, and temp are unused by going out of
4808 // scope.
4809 }
4810
4811 exit.Bind();
4812 }
4813}
4814
4815
4816Result CodeGenerator::LoadFromGlobalSlotCheckExtensions(
4817 Slot* slot,
4818 TypeofState typeof_state,
4819 JumpTarget* slow) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004820 // Check that no extension objects have been created by calls to
4821 // eval from the current scope to the global scope.
4822 Register context = rsi;
4823 Result tmp = allocator_->Allocate();
4824 ASSERT(tmp.is_valid()); // All non-reserved registers were available.
4825
4826 Scope* s = scope();
4827 while (s != NULL) {
4828 if (s->num_heap_slots() > 0) {
4829 if (s->calls_eval()) {
4830 // Check that extension is NULL.
4831 __ cmpq(ContextOperand(context, Context::EXTENSION_INDEX),
4832 Immediate(0));
4833 slow->Branch(not_equal, not_taken);
4834 }
4835 // Load next context in chain.
4836 __ movq(tmp.reg(), ContextOperand(context, Context::CLOSURE_INDEX));
4837 __ movq(tmp.reg(), FieldOperand(tmp.reg(), JSFunction::kContextOffset));
4838 context = tmp.reg();
4839 }
4840 // If no outer scope calls eval, we do not need to check more
4841 // context extensions. If we have reached an eval scope, we check
4842 // all extensions from this point.
4843 if (!s->outer_scope_calls_eval() || s->is_eval_scope()) break;
4844 s = s->outer_scope();
4845 }
4846
4847 if (s->is_eval_scope()) {
4848 // Loop up the context chain. There is no frame effect so it is
4849 // safe to use raw labels here.
4850 Label next, fast;
4851 if (!context.is(tmp.reg())) {
4852 __ movq(tmp.reg(), context);
4853 }
4854 // Load map for comparison into register, outside loop.
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004855 __ LoadRoot(kScratchRegister, Heap::kGlobalContextMapRootIndex);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004856 __ bind(&next);
4857 // Terminate at global context.
4858 __ cmpq(kScratchRegister, FieldOperand(tmp.reg(), HeapObject::kMapOffset));
4859 __ j(equal, &fast);
4860 // Check that extension is NULL.
4861 __ cmpq(ContextOperand(tmp.reg(), Context::EXTENSION_INDEX), Immediate(0));
4862 slow->Branch(not_equal);
4863 // Load next context in chain.
4864 __ movq(tmp.reg(), ContextOperand(tmp.reg(), Context::CLOSURE_INDEX));
4865 __ movq(tmp.reg(), FieldOperand(tmp.reg(), JSFunction::kContextOffset));
4866 __ jmp(&next);
4867 __ bind(&fast);
4868 }
4869 tmp.Unuse();
4870
4871 // All extension objects were empty and it is safe to use a global
4872 // load IC call.
4873 LoadGlobal();
4874 frame_->Push(slot->var()->name());
4875 RelocInfo::Mode mode = (typeof_state == INSIDE_TYPEOF)
4876 ? RelocInfo::CODE_TARGET
4877 : RelocInfo::CODE_TARGET_CONTEXT;
4878 Result answer = frame_->CallLoadIC(mode);
4879 // A test rax instruction following the call signals that the inobject
ager@chromium.orgab99eea2009-08-25 07:05:41 +00004880 // property case was inlined. Ensure that there is not a test rax
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004881 // instruction here.
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00004882 masm_->nop();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004883 // Discard the global object. The result is in answer.
4884 frame_->Drop();
4885 return answer;
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004886}
4887
4888
4889void CodeGenerator::LoadGlobal() {
4890 if (in_spilled_code()) {
4891 frame_->EmitPush(GlobalObject());
4892 } else {
4893 Result temp = allocator_->Allocate();
4894 __ movq(temp.reg(), GlobalObject());
4895 frame_->Push(&temp);
4896 }
4897}
4898
4899
4900void CodeGenerator::LoadGlobalReceiver() {
4901 Result temp = allocator_->Allocate();
4902 Register reg = temp.reg();
4903 __ movq(reg, GlobalObject());
4904 __ movq(reg, FieldOperand(reg, GlobalObject::kGlobalReceiverOffset));
4905 frame_->Push(&temp);
4906}
4907
ager@chromium.org3e875802009-06-29 08:26:34 +00004908
ager@chromium.org5c838252010-02-19 08:53:10 +00004909ArgumentsAllocationMode CodeGenerator::ArgumentsMode() {
4910 if (scope()->arguments() == NULL) return NO_ARGUMENTS_ALLOCATION;
4911 ASSERT(scope()->arguments_shadow() != NULL);
ager@chromium.org3e875802009-06-29 08:26:34 +00004912 // We don't want to do lazy arguments allocation for functions that
4913 // have heap-allocated contexts, because it interfers with the
4914 // uninitialized const tracking in the context objects.
ager@chromium.org5c838252010-02-19 08:53:10 +00004915 return (scope()->num_heap_slots() > 0)
ager@chromium.org3e875802009-06-29 08:26:34 +00004916 ? EAGER_ARGUMENTS_ALLOCATION
4917 : LAZY_ARGUMENTS_ALLOCATION;
4918}
4919
4920
4921Result CodeGenerator::StoreArgumentsObject(bool initial) {
4922 ArgumentsAllocationMode mode = ArgumentsMode();
4923 ASSERT(mode != NO_ARGUMENTS_ALLOCATION);
4924
4925 Comment cmnt(masm_, "[ store arguments object");
4926 if (mode == LAZY_ARGUMENTS_ALLOCATION && initial) {
4927 // When using lazy arguments allocation, we store the hole value
4928 // as a sentinel indicating that the arguments object hasn't been
4929 // allocated yet.
4930 frame_->Push(Factory::the_hole_value());
4931 } else {
4932 ArgumentsAccessStub stub(ArgumentsAccessStub::NEW_OBJECT);
4933 frame_->PushFunction();
4934 frame_->PushReceiverSlotAddress();
ager@chromium.org5c838252010-02-19 08:53:10 +00004935 frame_->Push(Smi::FromInt(scope()->num_parameters()));
ager@chromium.org3e875802009-06-29 08:26:34 +00004936 Result result = frame_->CallStub(&stub, 3);
4937 frame_->Push(&result);
4938 }
4939
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004940
ager@chromium.org5c838252010-02-19 08:53:10 +00004941 Variable* arguments = scope()->arguments()->var();
4942 Variable* shadow = scope()->arguments_shadow()->var();
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004943 ASSERT(arguments != NULL && arguments->slot() != NULL);
4944 ASSERT(shadow != NULL && shadow->slot() != NULL);
4945 JumpTarget done;
4946 bool skip_arguments = false;
4947 if (mode == LAZY_ARGUMENTS_ALLOCATION && !initial) {
4948 // We have to skip storing into the arguments slot if it has
4949 // already been written to. This can happen if the a function
4950 // has a local variable named 'arguments'.
ager@chromium.org5c838252010-02-19 08:53:10 +00004951 LoadFromSlot(scope()->arguments()->var()->slot(), NOT_INSIDE_TYPEOF);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004952 Result probe = frame_->Pop();
4953 if (probe.is_constant()) {
4954 // We have to skip updating the arguments object if it has been
4955 // assigned a proper value.
4956 skip_arguments = !probe.handle()->IsTheHole();
4957 } else {
4958 __ CompareRoot(probe.reg(), Heap::kTheHoleValueRootIndex);
4959 probe.Unuse();
4960 done.Branch(not_equal);
ager@chromium.org3e875802009-06-29 08:26:34 +00004961 }
ager@chromium.org3e875802009-06-29 08:26:34 +00004962 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004963 if (!skip_arguments) {
4964 StoreToSlot(arguments->slot(), NOT_CONST_INIT);
4965 if (mode == LAZY_ARGUMENTS_ALLOCATION) done.Bind();
4966 }
4967 StoreToSlot(shadow->slot(), NOT_CONST_INIT);
ager@chromium.org3e875802009-06-29 08:26:34 +00004968 return frame_->Pop();
4969}
4970
4971
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004972void CodeGenerator::LoadTypeofExpression(Expression* expr) {
4973 // Special handling of identifiers as subexpressions of typeof.
4974 Variable* variable = expr->AsVariableProxy()->AsVariable();
ager@chromium.org3e875802009-06-29 08:26:34 +00004975 if (variable != NULL && !variable->is_this() && variable->is_global()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004976 // For a global variable we build the property reference
4977 // <global>.<variable> and perform a (regular non-contextual) property
4978 // load to make sure we do not get reference errors.
ager@chromium.org3e875802009-06-29 08:26:34 +00004979 Slot global(variable, Slot::CONTEXT, Context::GLOBAL_INDEX);
4980 Literal key(variable->name());
ager@chromium.org3e875802009-06-29 08:26:34 +00004981 Property property(&global, &key, RelocInfo::kNoPosition);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004982 Reference ref(this, &property);
4983 ref.GetValue();
4984 } else if (variable != NULL && variable->slot() != NULL) {
4985 // For a variable that rewrites to a slot, we signal it is the immediate
4986 // subexpression of a typeof.
4987 LoadFromSlotCheckForArguments(variable->slot(), INSIDE_TYPEOF);
ager@chromium.org3e875802009-06-29 08:26:34 +00004988 } else {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004989 // Anything else can be handled normally.
4990 Load(expr);
ager@chromium.org3e875802009-06-29 08:26:34 +00004991 }
4992}
4993
4994
ager@chromium.org5c838252010-02-19 08:53:10 +00004995void CodeGenerator::Comparison(AstNode* node,
4996 Condition cc,
ager@chromium.org3e875802009-06-29 08:26:34 +00004997 bool strict,
4998 ControlDestination* dest) {
4999 // Strict only makes sense for equality comparisons.
5000 ASSERT(!strict || cc == equal);
5001
5002 Result left_side;
5003 Result right_side;
5004 // Implement '>' and '<=' by reversal to obtain ECMA-262 conversion order.
5005 if (cc == greater || cc == less_equal) {
5006 cc = ReverseCondition(cc);
5007 left_side = frame_->Pop();
5008 right_side = frame_->Pop();
5009 } else {
5010 right_side = frame_->Pop();
5011 left_side = frame_->Pop();
5012 }
5013 ASSERT(cc == less || cc == equal || cc == greater_equal);
5014
5015 // If either side is a constant smi, optimize the comparison.
5016 bool left_side_constant_smi =
5017 left_side.is_constant() && left_side.handle()->IsSmi();
5018 bool right_side_constant_smi =
5019 right_side.is_constant() && right_side.handle()->IsSmi();
5020 bool left_side_constant_null =
5021 left_side.is_constant() && left_side.handle()->IsNull();
5022 bool right_side_constant_null =
5023 right_side.is_constant() && right_side.handle()->IsNull();
5024
5025 if (left_side_constant_smi || right_side_constant_smi) {
5026 if (left_side_constant_smi && right_side_constant_smi) {
5027 // Trivial case, comparing two constants.
5028 int left_value = Smi::cast(*left_side.handle())->value();
5029 int right_value = Smi::cast(*right_side.handle())->value();
5030 switch (cc) {
5031 case less:
5032 dest->Goto(left_value < right_value);
5033 break;
5034 case equal:
5035 dest->Goto(left_value == right_value);
5036 break;
5037 case greater_equal:
5038 dest->Goto(left_value >= right_value);
5039 break;
5040 default:
5041 UNREACHABLE();
5042 }
ager@chromium.org5c838252010-02-19 08:53:10 +00005043 } else {
5044 // Only one side is a constant Smi.
ager@chromium.org3e875802009-06-29 08:26:34 +00005045 // If left side is a constant Smi, reverse the operands.
5046 // Since one side is a constant Smi, conversion order does not matter.
5047 if (left_side_constant_smi) {
5048 Result temp = left_side;
5049 left_side = right_side;
5050 right_side = temp;
5051 cc = ReverseCondition(cc);
5052 // This may reintroduce greater or less_equal as the value of cc.
5053 // CompareStub and the inline code both support all values of cc.
5054 }
5055 // Implement comparison against a constant Smi, inlining the case
5056 // where both sides are Smis.
5057 left_side.ToRegister();
ager@chromium.org5c838252010-02-19 08:53:10 +00005058 Register left_reg = left_side.reg();
5059 Handle<Object> right_val = right_side.handle();
ager@chromium.org3e875802009-06-29 08:26:34 +00005060
5061 // Here we split control flow to the stub call and inlined cases
5062 // before finally splitting it to the control destination. We use
5063 // a jump target and branching to duplicate the virtual frame at
5064 // the first split. We manually handle the off-frame references
5065 // by reconstituting them on the non-fall-through path.
5066 JumpTarget is_smi;
ager@chromium.org4af710e2009-09-15 12:20:11 +00005067
5068 Condition left_is_smi = masm_->CheckSmi(left_side.reg());
5069 is_smi.Branch(left_is_smi);
ager@chromium.org3e875802009-06-29 08:26:34 +00005070
ager@chromium.org5c838252010-02-19 08:53:10 +00005071 bool is_for_loop_compare = (node->AsCompareOperation() != NULL)
5072 && node->AsCompareOperation()->is_for_loop_condition();
5073 if (!is_for_loop_compare && right_val->IsSmi()) {
5074 // Right side is a constant smi and left side has been checked
5075 // not to be a smi.
5076 JumpTarget not_number;
5077 __ Cmp(FieldOperand(left_reg, HeapObject::kMapOffset),
5078 Factory::heap_number_map());
5079 not_number.Branch(not_equal, &left_side);
5080 __ movsd(xmm1,
5081 FieldOperand(left_reg, HeapNumber::kValueOffset));
5082 int value = Smi::cast(*right_val)->value();
5083 if (value == 0) {
5084 __ xorpd(xmm0, xmm0);
5085 } else {
5086 Result temp = allocator()->Allocate();
5087 __ movl(temp.reg(), Immediate(value));
5088 __ cvtlsi2sd(xmm0, temp.reg());
5089 temp.Unuse();
5090 }
5091 __ ucomisd(xmm1, xmm0);
5092 // Jump to builtin for NaN.
5093 not_number.Branch(parity_even, &left_side);
5094 left_side.Unuse();
5095 Condition double_cc = cc;
5096 switch (cc) {
5097 case less: double_cc = below; break;
5098 case equal: double_cc = equal; break;
5099 case less_equal: double_cc = below_equal; break;
5100 case greater: double_cc = above; break;
5101 case greater_equal: double_cc = above_equal; break;
5102 default: UNREACHABLE();
5103 }
5104 dest->true_target()->Branch(double_cc);
5105 dest->false_target()->Jump();
5106 not_number.Bind(&left_side);
5107 }
5108
ager@chromium.org3e875802009-06-29 08:26:34 +00005109 // Setup and call the compare stub.
5110 CompareStub stub(cc, strict);
5111 Result result = frame_->CallStub(&stub, &left_side, &right_side);
5112 result.ToRegister();
5113 __ testq(result.reg(), result.reg());
5114 result.Unuse();
5115 dest->true_target()->Branch(cc);
5116 dest->false_target()->Jump();
5117
5118 is_smi.Bind();
5119 left_side = Result(left_reg);
5120 right_side = Result(right_val);
5121 // Test smi equality and comparison by signed int comparison.
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00005122 // Both sides are smis, so we can use an Immediate.
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005123 __ SmiCompare(left_side.reg(), Smi::cast(*right_side.handle()));
ager@chromium.org3e875802009-06-29 08:26:34 +00005124 left_side.Unuse();
5125 right_side.Unuse();
5126 dest->Split(cc);
5127 }
5128 } else if (cc == equal &&
5129 (left_side_constant_null || right_side_constant_null)) {
5130 // To make null checks efficient, we check if either the left side or
5131 // the right side is the constant 'null'.
5132 // If so, we optimize the code by inlining a null check instead of
5133 // calling the (very) general runtime routine for checking equality.
5134 Result operand = left_side_constant_null ? right_side : left_side;
5135 right_side.Unuse();
5136 left_side.Unuse();
5137 operand.ToRegister();
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005138 __ CompareRoot(operand.reg(), Heap::kNullValueRootIndex);
ager@chromium.org3e875802009-06-29 08:26:34 +00005139 if (strict) {
5140 operand.Unuse();
5141 dest->Split(equal);
5142 } else {
5143 // The 'null' value is only equal to 'undefined' if using non-strict
5144 // comparisons.
5145 dest->true_target()->Branch(equal);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005146 __ CompareRoot(operand.reg(), Heap::kUndefinedValueRootIndex);
ager@chromium.org3e875802009-06-29 08:26:34 +00005147 dest->true_target()->Branch(equal);
ager@chromium.org4af710e2009-09-15 12:20:11 +00005148 Condition is_smi = masm_->CheckSmi(operand.reg());
5149 dest->false_target()->Branch(is_smi);
ager@chromium.org3e875802009-06-29 08:26:34 +00005150
5151 // It can be an undetectable object.
5152 // Use a scratch register in preference to spilling operand.reg().
5153 Result temp = allocator()->Allocate();
5154 ASSERT(temp.is_valid());
5155 __ movq(temp.reg(),
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005156 FieldOperand(operand.reg(), HeapObject::kMapOffset));
ager@chromium.org3e875802009-06-29 08:26:34 +00005157 __ testb(FieldOperand(temp.reg(), Map::kBitFieldOffset),
5158 Immediate(1 << Map::kIsUndetectable));
5159 temp.Unuse();
5160 operand.Unuse();
5161 dest->Split(not_zero);
5162 }
5163 } else { // Neither side is a constant Smi or null.
5164 // If either side is a non-smi constant, skip the smi check.
5165 bool known_non_smi =
5166 (left_side.is_constant() && !left_side.handle()->IsSmi()) ||
5167 (right_side.is_constant() && !right_side.handle()->IsSmi());
5168 left_side.ToRegister();
5169 right_side.ToRegister();
5170
5171 if (known_non_smi) {
5172 // When non-smi, call out to the compare stub.
5173 CompareStub stub(cc, strict);
5174 Result answer = frame_->CallStub(&stub, &left_side, &right_side);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00005175 // The result is a Smi, which is negative, zero, or positive.
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005176 __ SmiTest(answer.reg()); // Sets both zero and sign flag.
ager@chromium.org3e875802009-06-29 08:26:34 +00005177 answer.Unuse();
5178 dest->Split(cc);
5179 } else {
5180 // Here we split control flow to the stub call and inlined cases
5181 // before finally splitting it to the control destination. We use
5182 // a jump target and branching to duplicate the virtual frame at
5183 // the first split. We manually handle the off-frame references
5184 // by reconstituting them on the non-fall-through path.
5185 JumpTarget is_smi;
5186 Register left_reg = left_side.reg();
5187 Register right_reg = right_side.reg();
5188
ager@chromium.org4af710e2009-09-15 12:20:11 +00005189 Condition both_smi = masm_->CheckBothSmi(left_reg, right_reg);
5190 is_smi.Branch(both_smi);
ager@chromium.org3e875802009-06-29 08:26:34 +00005191 // When non-smi, call out to the compare stub.
5192 CompareStub stub(cc, strict);
5193 Result answer = frame_->CallStub(&stub, &left_side, &right_side);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005194 __ SmiTest(answer.reg()); // Sets both zero and sign flags.
ager@chromium.org3e875802009-06-29 08:26:34 +00005195 answer.Unuse();
5196 dest->true_target()->Branch(cc);
5197 dest->false_target()->Jump();
5198
5199 is_smi.Bind();
5200 left_side = Result(left_reg);
5201 right_side = Result(right_reg);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005202 __ SmiCompare(left_side.reg(), right_side.reg());
ager@chromium.org3e875802009-06-29 08:26:34 +00005203 right_side.Unuse();
5204 left_side.Unuse();
5205 dest->Split(cc);
5206 }
5207 }
5208}
5209
5210
ager@chromium.org3e875802009-06-29 08:26:34 +00005211class DeferredInlineBinaryOperation: public DeferredCode {
5212 public:
5213 DeferredInlineBinaryOperation(Token::Value op,
5214 Register dst,
5215 Register left,
5216 Register right,
5217 OverwriteMode mode)
5218 : op_(op), dst_(dst), left_(left), right_(right), mode_(mode) {
5219 set_comment("[ DeferredInlineBinaryOperation");
5220 }
5221
5222 virtual void Generate();
5223
5224 private:
5225 Token::Value op_;
5226 Register dst_;
5227 Register left_;
5228 Register right_;
5229 OverwriteMode mode_;
5230};
5231
5232
5233void DeferredInlineBinaryOperation::Generate() {
ager@chromium.org3811b432009-10-28 14:53:37 +00005234 GenericBinaryOpStub stub(op_, mode_, NO_SMI_CODE_IN_STUB);
5235 stub.GenerateCall(masm_, left_, right_);
ager@chromium.org3e875802009-06-29 08:26:34 +00005236 if (!dst_.is(rax)) __ movq(dst_, rax);
5237}
5238
5239
5240void CodeGenerator::GenericBinaryOperation(Token::Value op,
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00005241 StaticType* type,
ager@chromium.org3e875802009-06-29 08:26:34 +00005242 OverwriteMode overwrite_mode) {
5243 Comment cmnt(masm_, "[ BinaryOperation");
5244 Comment cmnt_token(masm_, Token::String(op));
5245
5246 if (op == Token::COMMA) {
5247 // Simply discard left value.
5248 frame_->Nip(1);
5249 return;
5250 }
5251
ager@chromium.org3e875802009-06-29 08:26:34 +00005252 Result right = frame_->Pop();
5253 Result left = frame_->Pop();
5254
5255 if (op == Token::ADD) {
5256 bool left_is_string = left.is_constant() && left.handle()->IsString();
5257 bool right_is_string = right.is_constant() && right.handle()->IsString();
5258 if (left_is_string || right_is_string) {
5259 frame_->Push(&left);
5260 frame_->Push(&right);
5261 Result answer;
5262 if (left_is_string) {
5263 if (right_is_string) {
5264 // TODO(lrn): if both are constant strings
5265 // -- do a compile time cons, if allocation during codegen is allowed.
5266 answer = frame_->CallRuntime(Runtime::kStringAdd, 2);
5267 } else {
5268 answer =
5269 frame_->InvokeBuiltin(Builtins::STRING_ADD_LEFT, CALL_FUNCTION, 2);
5270 }
5271 } else if (right_is_string) {
5272 answer =
5273 frame_->InvokeBuiltin(Builtins::STRING_ADD_RIGHT, CALL_FUNCTION, 2);
5274 }
5275 frame_->Push(&answer);
5276 return;
5277 }
5278 // Neither operand is known to be a string.
5279 }
5280
ager@chromium.org5c838252010-02-19 08:53:10 +00005281 bool left_is_smi_constant = left.is_constant() && left.handle()->IsSmi();
5282 bool left_is_non_smi_constant = left.is_constant() && !left.handle()->IsSmi();
5283 bool right_is_smi_constant = right.is_constant() && right.handle()->IsSmi();
5284 bool right_is_non_smi_constant =
5285 right.is_constant() && !right.handle()->IsSmi();
ager@chromium.org3e875802009-06-29 08:26:34 +00005286
ager@chromium.org5c838252010-02-19 08:53:10 +00005287 if (left_is_smi_constant && right_is_smi_constant) {
ager@chromium.org3e875802009-06-29 08:26:34 +00005288 // Compute the constant result at compile time, and leave it on the frame.
5289 int left_int = Smi::cast(*left.handle())->value();
5290 int right_int = Smi::cast(*right.handle())->value();
5291 if (FoldConstantSmis(op, left_int, right_int)) return;
5292 }
5293
ager@chromium.org5c838252010-02-19 08:53:10 +00005294 // Get number type of left and right sub-expressions.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005295 NumberInfo operands_type =
ager@chromium.org5c838252010-02-19 08:53:10 +00005296 NumberInfo::Combine(left.number_info(), right.number_info());
5297
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005298 Result answer;
ager@chromium.org5c838252010-02-19 08:53:10 +00005299 if (left_is_non_smi_constant || right_is_non_smi_constant) {
5300 GenericBinaryOpStub stub(op,
5301 overwrite_mode,
5302 NO_SMI_CODE_IN_STUB,
5303 operands_type);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005304 answer = stub.GenerateCall(masm_, frame_, &left, &right);
ager@chromium.org5c838252010-02-19 08:53:10 +00005305 } else if (right_is_smi_constant) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005306 answer = ConstantSmiBinaryOperation(op, &left, right.handle(),
5307 type, false, overwrite_mode);
ager@chromium.org5c838252010-02-19 08:53:10 +00005308 } else if (left_is_smi_constant) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005309 answer = ConstantSmiBinaryOperation(op, &right, left.handle(),
5310 type, true, overwrite_mode);
ager@chromium.org3e875802009-06-29 08:26:34 +00005311 } else {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005312 // Set the flags based on the operation, type and loop nesting level.
5313 // Bit operations always assume they likely operate on Smis. Still only
5314 // generate the inline Smi check code if this operation is part of a loop.
5315 // For all other operations only inline the Smi check code for likely smis
5316 // if the operation is part of a loop.
5317 if (loop_nesting() > 0 && (Token::IsBitOp(op) || type->IsLikelySmi())) {
5318 answer = LikelySmiBinaryOperation(op, &left, &right, overwrite_mode);
5319 } else {
ager@chromium.org5c838252010-02-19 08:53:10 +00005320 GenericBinaryOpStub stub(op,
5321 overwrite_mode,
5322 NO_GENERIC_BINARY_FLAGS,
5323 operands_type);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005324 answer = stub.GenerateCall(masm_, frame_, &left, &right);
ager@chromium.org3e875802009-06-29 08:26:34 +00005325 }
ager@chromium.org3e875802009-06-29 08:26:34 +00005326 }
ager@chromium.org5c838252010-02-19 08:53:10 +00005327
5328 // Set NumberInfo of result according to the operation performed.
5329 // We rely on the fact that smis have a 32 bit payload on x64.
5330 ASSERT(kSmiValueSize == 32);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005331 NumberInfo result_type = NumberInfo::Unknown();
ager@chromium.org5c838252010-02-19 08:53:10 +00005332 switch (op) {
5333 case Token::COMMA:
5334 result_type = right.number_info();
5335 break;
5336 case Token::OR:
5337 case Token::AND:
5338 // Result type can be either of the two input types.
5339 result_type = operands_type;
5340 break;
5341 case Token::BIT_OR:
5342 case Token::BIT_XOR:
5343 case Token::BIT_AND:
5344 // Result is always a smi.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005345 result_type = NumberInfo::Smi();
ager@chromium.org5c838252010-02-19 08:53:10 +00005346 break;
5347 case Token::SAR:
5348 case Token::SHL:
5349 // Result is always a smi.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005350 result_type = NumberInfo::Smi();
ager@chromium.org5c838252010-02-19 08:53:10 +00005351 break;
5352 case Token::SHR:
5353 // Result of x >>> y is always a smi if y >= 1, otherwise a number.
5354 result_type = (right.is_constant() && right.handle()->IsSmi()
5355 && Smi::cast(*right.handle())->value() >= 1)
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005356 ? NumberInfo::Smi()
5357 : NumberInfo::Number();
ager@chromium.org5c838252010-02-19 08:53:10 +00005358 break;
5359 case Token::ADD:
5360 // Result could be a string or a number. Check types of inputs.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005361 result_type = operands_type.IsNumber()
5362 ? NumberInfo::Number()
5363 : NumberInfo::Unknown();
ager@chromium.org5c838252010-02-19 08:53:10 +00005364 break;
5365 case Token::SUB:
5366 case Token::MUL:
5367 case Token::DIV:
5368 case Token::MOD:
5369 // Result is always a number.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005370 result_type = NumberInfo::Number();
ager@chromium.org5c838252010-02-19 08:53:10 +00005371 break;
5372 default:
5373 UNREACHABLE();
5374 }
5375 answer.set_number_info(result_type);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005376 frame_->Push(&answer);
ager@chromium.org3e875802009-06-29 08:26:34 +00005377}
5378
5379
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005380// Emit a LoadIC call to get the value from receiver and leave it in
5381// dst. The receiver register is restored after the call.
5382class DeferredReferenceGetNamedValue: public DeferredCode {
5383 public:
5384 DeferredReferenceGetNamedValue(Register dst,
5385 Register receiver,
5386 Handle<String> name)
5387 : dst_(dst), receiver_(receiver), name_(name) {
5388 set_comment("[ DeferredReferenceGetNamedValue");
5389 }
5390
5391 virtual void Generate();
5392
5393 Label* patch_site() { return &patch_site_; }
5394
5395 private:
5396 Label patch_site_;
5397 Register dst_;
5398 Register receiver_;
5399 Handle<String> name_;
5400};
5401
5402
5403void DeferredReferenceGetNamedValue::Generate() {
5404 __ push(receiver_);
5405 __ Move(rcx, name_);
5406 Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize));
5407 __ Call(ic, RelocInfo::CODE_TARGET);
ager@chromium.org3e875802009-06-29 08:26:34 +00005408 // The call must be followed by a test rax instruction to indicate
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005409 // that the inobject property case was inlined.
5410 //
5411 // Store the delta to the map check instruction here in the test
5412 // instruction. Use masm_-> instead of the __ macro since the
5413 // latter can't return a value.
5414 int delta_to_patch_site = masm_->SizeOfCodeGeneratedSince(patch_site());
5415 // Here we use masm_-> instead of the __ macro because this is the
5416 // instruction that gets patched and coverage code gets in the way.
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005417 masm_->testl(rax, Immediate(-delta_to_patch_site));
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005418 __ IncrementCounter(&Counters::named_load_inline_miss, 1);
5419
5420 if (!dst_.is(rax)) __ movq(dst_, rax);
5421 __ pop(receiver_);
5422}
5423
5424
ager@chromium.org3e875802009-06-29 08:26:34 +00005425void DeferredInlineSmiAdd::Generate() {
ager@chromium.org3811b432009-10-28 14:53:37 +00005426 GenericBinaryOpStub igostub(Token::ADD, overwrite_mode_, NO_SMI_CODE_IN_STUB);
5427 igostub.GenerateCall(masm_, dst_, value_);
ager@chromium.org3e875802009-06-29 08:26:34 +00005428 if (!dst_.is(rax)) __ movq(dst_, rax);
5429}
5430
5431
ager@chromium.org3e875802009-06-29 08:26:34 +00005432void DeferredInlineSmiAddReversed::Generate() {
ager@chromium.org3811b432009-10-28 14:53:37 +00005433 GenericBinaryOpStub igostub(Token::ADD, overwrite_mode_, NO_SMI_CODE_IN_STUB);
5434 igostub.GenerateCall(masm_, value_, dst_);
ager@chromium.org3e875802009-06-29 08:26:34 +00005435 if (!dst_.is(rax)) __ movq(dst_, rax);
5436}
5437
5438
ager@chromium.org3e875802009-06-29 08:26:34 +00005439void DeferredInlineSmiSub::Generate() {
ager@chromium.org3811b432009-10-28 14:53:37 +00005440 GenericBinaryOpStub igostub(Token::SUB, overwrite_mode_, NO_SMI_CODE_IN_STUB);
5441 igostub.GenerateCall(masm_, dst_, value_);
ager@chromium.org3e875802009-06-29 08:26:34 +00005442 if (!dst_.is(rax)) __ movq(dst_, rax);
5443}
5444
5445
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00005446void DeferredInlineSmiOperation::Generate() {
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00005447 // For mod we don't generate all the Smi code inline.
5448 GenericBinaryOpStub stub(
5449 op_,
5450 overwrite_mode_,
ager@chromium.org3811b432009-10-28 14:53:37 +00005451 (op_ == Token::MOD) ? NO_GENERIC_BINARY_FLAGS : NO_SMI_CODE_IN_STUB);
5452 stub.GenerateCall(masm_, src_, value_);
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00005453 if (!dst_.is(rax)) __ movq(dst_, rax);
5454}
5455
5456
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005457Result CodeGenerator::ConstantSmiBinaryOperation(Token::Value op,
5458 Result* operand,
5459 Handle<Object> value,
5460 StaticType* type,
5461 bool reversed,
5462 OverwriteMode overwrite_mode) {
ager@chromium.org3e875802009-06-29 08:26:34 +00005463 // NOTE: This is an attempt to inline (a bit) more of the code for
5464 // some possible smi operations (like + and -) when (at least) one
5465 // of the operands is a constant smi.
5466 // Consumes the argument "operand".
5467
5468 // TODO(199): Optimize some special cases of operations involving a
5469 // smi literal (multiply by 2, shift by 0, etc.).
5470 if (IsUnsafeSmi(value)) {
5471 Result unsafe_operand(value);
5472 if (reversed) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005473 return LikelySmiBinaryOperation(op, &unsafe_operand, operand,
ager@chromium.org3e875802009-06-29 08:26:34 +00005474 overwrite_mode);
5475 } else {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005476 return LikelySmiBinaryOperation(op, operand, &unsafe_operand,
ager@chromium.org3e875802009-06-29 08:26:34 +00005477 overwrite_mode);
5478 }
ager@chromium.org3e875802009-06-29 08:26:34 +00005479 }
5480
5481 // Get the literal value.
5482 Smi* smi_value = Smi::cast(*value);
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00005483 int int_value = smi_value->value();
ager@chromium.org3e875802009-06-29 08:26:34 +00005484
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005485 Result answer;
ager@chromium.org3e875802009-06-29 08:26:34 +00005486 switch (op) {
5487 case Token::ADD: {
5488 operand->ToRegister();
5489 frame_->Spill(operand->reg());
ager@chromium.org3e875802009-06-29 08:26:34 +00005490 DeferredCode* deferred = NULL;
5491 if (reversed) {
5492 deferred = new DeferredInlineSmiAddReversed(operand->reg(),
5493 smi_value,
5494 overwrite_mode);
5495 } else {
5496 deferred = new DeferredInlineSmiAdd(operand->reg(),
5497 smi_value,
5498 overwrite_mode);
5499 }
ager@chromium.org4af710e2009-09-15 12:20:11 +00005500 __ JumpIfNotSmi(operand->reg(), deferred->entry_label());
5501 __ SmiAddConstant(operand->reg(),
5502 operand->reg(),
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005503 smi_value,
ager@chromium.org4af710e2009-09-15 12:20:11 +00005504 deferred->entry_label());
ager@chromium.org3e875802009-06-29 08:26:34 +00005505 deferred->BindExit();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005506 answer = *operand;
ager@chromium.org3e875802009-06-29 08:26:34 +00005507 break;
5508 }
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00005509
5510 case Token::SUB: {
5511 if (reversed) {
5512 Result constant_operand(value);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005513 answer = LikelySmiBinaryOperation(op, &constant_operand, operand,
5514 overwrite_mode);
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00005515 } else {
5516 operand->ToRegister();
5517 frame_->Spill(operand->reg());
5518 DeferredCode* deferred = new DeferredInlineSmiSub(operand->reg(),
5519 smi_value,
5520 overwrite_mode);
ager@chromium.org4af710e2009-09-15 12:20:11 +00005521 __ JumpIfNotSmi(operand->reg(), deferred->entry_label());
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00005522 // A smi currently fits in a 32-bit Immediate.
ager@chromium.org4af710e2009-09-15 12:20:11 +00005523 __ SmiSubConstant(operand->reg(),
5524 operand->reg(),
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005525 smi_value,
ager@chromium.org4af710e2009-09-15 12:20:11 +00005526 deferred->entry_label());
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00005527 deferred->BindExit();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005528 answer = *operand;
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00005529 }
5530 break;
5531 }
5532
5533 case Token::SAR:
5534 if (reversed) {
5535 Result constant_operand(value);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005536 answer = LikelySmiBinaryOperation(op, &constant_operand, operand,
5537 overwrite_mode);
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00005538 } else {
5539 // Only the least significant 5 bits of the shift value are used.
5540 // In the slow case, this masking is done inside the runtime call.
5541 int shift_value = int_value & 0x1f;
5542 operand->ToRegister();
5543 frame_->Spill(operand->reg());
5544 DeferredInlineSmiOperation* deferred =
5545 new DeferredInlineSmiOperation(op,
5546 operand->reg(),
5547 operand->reg(),
5548 smi_value,
5549 overwrite_mode);
ager@chromium.org4af710e2009-09-15 12:20:11 +00005550 __ JumpIfNotSmi(operand->reg(), deferred->entry_label());
5551 __ SmiShiftArithmeticRightConstant(operand->reg(),
5552 operand->reg(),
5553 shift_value);
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00005554 deferred->BindExit();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005555 answer = *operand;
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00005556 }
5557 break;
5558
5559 case Token::SHR:
5560 if (reversed) {
5561 Result constant_operand(value);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005562 answer = LikelySmiBinaryOperation(op, &constant_operand, operand,
5563 overwrite_mode);
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00005564 } else {
5565 // Only the least significant 5 bits of the shift value are used.
5566 // In the slow case, this masking is done inside the runtime call.
5567 int shift_value = int_value & 0x1f;
5568 operand->ToRegister();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005569 answer = allocator()->Allocate();
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00005570 ASSERT(answer.is_valid());
5571 DeferredInlineSmiOperation* deferred =
5572 new DeferredInlineSmiOperation(op,
5573 answer.reg(),
5574 operand->reg(),
5575 smi_value,
5576 overwrite_mode);
ager@chromium.org4af710e2009-09-15 12:20:11 +00005577 __ JumpIfNotSmi(operand->reg(), deferred->entry_label());
5578 __ SmiShiftLogicalRightConstant(answer.reg(),
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005579 operand->reg(),
5580 shift_value,
5581 deferred->entry_label());
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00005582 deferred->BindExit();
ager@chromium.org4af710e2009-09-15 12:20:11 +00005583 operand->Unuse();
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00005584 }
5585 break;
5586
ager@chromium.orgadd848f2009-08-13 12:44:13 +00005587 case Token::SHL:
5588 if (reversed) {
5589 Result constant_operand(value);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005590 answer = LikelySmiBinaryOperation(op, &constant_operand, operand,
5591 overwrite_mode);
ager@chromium.orgadd848f2009-08-13 12:44:13 +00005592 } else {
5593 // Only the least significant 5 bits of the shift value are used.
5594 // In the slow case, this masking is done inside the runtime call.
5595 int shift_value = int_value & 0x1f;
5596 operand->ToRegister();
5597 if (shift_value == 0) {
5598 // Spill operand so it can be overwritten in the slow case.
5599 frame_->Spill(operand->reg());
5600 DeferredInlineSmiOperation* deferred =
5601 new DeferredInlineSmiOperation(op,
5602 operand->reg(),
5603 operand->reg(),
5604 smi_value,
5605 overwrite_mode);
ager@chromium.org4af710e2009-09-15 12:20:11 +00005606 __ JumpIfNotSmi(operand->reg(), deferred->entry_label());
ager@chromium.orgadd848f2009-08-13 12:44:13 +00005607 deferred->BindExit();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005608 answer = *operand;
ager@chromium.orgadd848f2009-08-13 12:44:13 +00005609 } else {
5610 // Use a fresh temporary for nonzero shift values.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005611 answer = allocator()->Allocate();
ager@chromium.orgadd848f2009-08-13 12:44:13 +00005612 ASSERT(answer.is_valid());
5613 DeferredInlineSmiOperation* deferred =
5614 new DeferredInlineSmiOperation(op,
5615 answer.reg(),
5616 operand->reg(),
5617 smi_value,
5618 overwrite_mode);
ager@chromium.org4af710e2009-09-15 12:20:11 +00005619 __ JumpIfNotSmi(operand->reg(), deferred->entry_label());
5620 __ SmiShiftLeftConstant(answer.reg(),
5621 operand->reg(),
5622 shift_value,
5623 deferred->entry_label());
ager@chromium.orgadd848f2009-08-13 12:44:13 +00005624 deferred->BindExit();
5625 operand->Unuse();
ager@chromium.orgadd848f2009-08-13 12:44:13 +00005626 }
5627 }
5628 break;
5629
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00005630 case Token::BIT_OR:
5631 case Token::BIT_XOR:
5632 case Token::BIT_AND: {
5633 operand->ToRegister();
5634 frame_->Spill(operand->reg());
5635 if (reversed) {
5636 // Bit operations with a constant smi are commutative.
5637 // We can swap left and right operands with no problem.
5638 // Swap left and right overwrite modes. 0->0, 1->2, 2->1.
5639 overwrite_mode = static_cast<OverwriteMode>((2 * overwrite_mode) % 3);
5640 }
5641 DeferredCode* deferred = new DeferredInlineSmiOperation(op,
5642 operand->reg(),
5643 operand->reg(),
5644 smi_value,
5645 overwrite_mode);
ager@chromium.org4af710e2009-09-15 12:20:11 +00005646 __ JumpIfNotSmi(operand->reg(), deferred->entry_label());
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00005647 if (op == Token::BIT_AND) {
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005648 __ SmiAndConstant(operand->reg(), operand->reg(), smi_value);
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00005649 } else if (op == Token::BIT_XOR) {
5650 if (int_value != 0) {
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005651 __ SmiXorConstant(operand->reg(), operand->reg(), smi_value);
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00005652 }
5653 } else {
5654 ASSERT(op == Token::BIT_OR);
5655 if (int_value != 0) {
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005656 __ SmiOrConstant(operand->reg(), operand->reg(), smi_value);
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00005657 }
5658 }
5659 deferred->BindExit();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005660 answer = *operand;
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00005661 break;
5662 }
5663
5664 // Generate inline code for mod of powers of 2 and negative powers of 2.
5665 case Token::MOD:
5666 if (!reversed &&
5667 int_value != 0 &&
5668 (IsPowerOf2(int_value) || IsPowerOf2(-int_value))) {
5669 operand->ToRegister();
5670 frame_->Spill(operand->reg());
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005671 DeferredCode* deferred =
5672 new DeferredInlineSmiOperation(op,
5673 operand->reg(),
5674 operand->reg(),
5675 smi_value,
5676 overwrite_mode);
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00005677 // Check for negative or non-Smi left hand side.
ager@chromium.org4af710e2009-09-15 12:20:11 +00005678 __ JumpIfNotPositiveSmi(operand->reg(), deferred->entry_label());
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00005679 if (int_value < 0) int_value = -int_value;
5680 if (int_value == 1) {
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005681 __ Move(operand->reg(), Smi::FromInt(0));
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00005682 } else {
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005683 __ SmiAndConstant(operand->reg(),
5684 operand->reg(),
5685 Smi::FromInt(int_value - 1));
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00005686 }
5687 deferred->BindExit();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005688 answer = *operand;
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00005689 break; // This break only applies if we generated code for MOD.
5690 }
5691 // Fall through if we did not find a power of 2 on the right hand side!
5692 // The next case must be the default.
5693
ager@chromium.org3e875802009-06-29 08:26:34 +00005694 default: {
5695 Result constant_operand(value);
5696 if (reversed) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005697 answer = LikelySmiBinaryOperation(op, &constant_operand, operand,
5698 overwrite_mode);
ager@chromium.org3e875802009-06-29 08:26:34 +00005699 } else {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005700 answer = LikelySmiBinaryOperation(op, operand, &constant_operand,
5701 overwrite_mode);
ager@chromium.org3e875802009-06-29 08:26:34 +00005702 }
5703 break;
5704 }
5705 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005706 ASSERT(answer.is_valid());
5707 return answer;
ager@chromium.org3e875802009-06-29 08:26:34 +00005708}
5709
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005710Result CodeGenerator::LikelySmiBinaryOperation(Token::Value op,
5711 Result* left,
5712 Result* right,
5713 OverwriteMode overwrite_mode) {
5714 Result answer;
ager@chromium.org3e875802009-06-29 08:26:34 +00005715 // Special handling of div and mod because they use fixed registers.
5716 if (op == Token::DIV || op == Token::MOD) {
5717 // We need rax as the quotient register, rdx as the remainder
5718 // register, neither left nor right in rax or rdx, and left copied
5719 // to rax.
5720 Result quotient;
5721 Result remainder;
5722 bool left_is_in_rax = false;
5723 // Step 1: get rax for quotient.
5724 if ((left->is_register() && left->reg().is(rax)) ||
5725 (right->is_register() && right->reg().is(rax))) {
5726 // One or both is in rax. Use a fresh non-rdx register for
5727 // them.
5728 Result fresh = allocator_->Allocate();
5729 ASSERT(fresh.is_valid());
5730 if (fresh.reg().is(rdx)) {
5731 remainder = fresh;
5732 fresh = allocator_->Allocate();
5733 ASSERT(fresh.is_valid());
5734 }
5735 if (left->is_register() && left->reg().is(rax)) {
5736 quotient = *left;
5737 *left = fresh;
5738 left_is_in_rax = true;
5739 }
5740 if (right->is_register() && right->reg().is(rax)) {
5741 quotient = *right;
5742 *right = fresh;
5743 }
5744 __ movq(fresh.reg(), rax);
5745 } else {
5746 // Neither left nor right is in rax.
5747 quotient = allocator_->Allocate(rax);
5748 }
5749 ASSERT(quotient.is_register() && quotient.reg().is(rax));
5750 ASSERT(!(left->is_register() && left->reg().is(rax)));
5751 ASSERT(!(right->is_register() && right->reg().is(rax)));
5752
5753 // Step 2: get rdx for remainder if necessary.
5754 if (!remainder.is_valid()) {
5755 if ((left->is_register() && left->reg().is(rdx)) ||
5756 (right->is_register() && right->reg().is(rdx))) {
5757 Result fresh = allocator_->Allocate();
5758 ASSERT(fresh.is_valid());
5759 if (left->is_register() && left->reg().is(rdx)) {
5760 remainder = *left;
5761 *left = fresh;
5762 }
5763 if (right->is_register() && right->reg().is(rdx)) {
5764 remainder = *right;
5765 *right = fresh;
5766 }
5767 __ movq(fresh.reg(), rdx);
5768 } else {
5769 // Neither left nor right is in rdx.
5770 remainder = allocator_->Allocate(rdx);
5771 }
5772 }
5773 ASSERT(remainder.is_register() && remainder.reg().is(rdx));
5774 ASSERT(!(left->is_register() && left->reg().is(rdx)));
5775 ASSERT(!(right->is_register() && right->reg().is(rdx)));
5776
5777 left->ToRegister();
5778 right->ToRegister();
5779 frame_->Spill(rax);
5780 frame_->Spill(rdx);
5781
5782 // Check that left and right are smi tagged.
5783 DeferredInlineBinaryOperation* deferred =
5784 new DeferredInlineBinaryOperation(op,
5785 (op == Token::DIV) ? rax : rdx,
5786 left->reg(),
5787 right->reg(),
5788 overwrite_mode);
ager@chromium.org4af710e2009-09-15 12:20:11 +00005789 __ JumpIfNotBothSmi(left->reg(), right->reg(), deferred->entry_label());
ager@chromium.org3e875802009-06-29 08:26:34 +00005790
ager@chromium.org3e875802009-06-29 08:26:34 +00005791 if (op == Token::DIV) {
ager@chromium.org4af710e2009-09-15 12:20:11 +00005792 __ SmiDiv(rax, left->reg(), right->reg(), deferred->entry_label());
ager@chromium.org3e875802009-06-29 08:26:34 +00005793 deferred->BindExit();
5794 left->Unuse();
5795 right->Unuse();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005796 answer = quotient;
ager@chromium.org3e875802009-06-29 08:26:34 +00005797 } else {
5798 ASSERT(op == Token::MOD);
ager@chromium.org4af710e2009-09-15 12:20:11 +00005799 __ SmiMod(rdx, left->reg(), right->reg(), deferred->entry_label());
ager@chromium.org3e875802009-06-29 08:26:34 +00005800 deferred->BindExit();
5801 left->Unuse();
5802 right->Unuse();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005803 answer = remainder;
ager@chromium.org3e875802009-06-29 08:26:34 +00005804 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005805 ASSERT(answer.is_valid());
5806 return answer;
ager@chromium.org3e875802009-06-29 08:26:34 +00005807 }
5808
5809 // Special handling of shift operations because they use fixed
5810 // registers.
5811 if (op == Token::SHL || op == Token::SHR || op == Token::SAR) {
5812 // Move left out of rcx if necessary.
5813 if (left->is_register() && left->reg().is(rcx)) {
5814 *left = allocator_->Allocate();
5815 ASSERT(left->is_valid());
5816 __ movq(left->reg(), rcx);
5817 }
5818 right->ToRegister(rcx);
5819 left->ToRegister();
5820 ASSERT(left->is_register() && !left->reg().is(rcx));
5821 ASSERT(right->is_register() && right->reg().is(rcx));
5822
5823 // We will modify right, it must be spilled.
5824 frame_->Spill(rcx);
5825
5826 // Use a fresh answer register to avoid spilling the left operand.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005827 answer = allocator_->Allocate();
ager@chromium.org3e875802009-06-29 08:26:34 +00005828 ASSERT(answer.is_valid());
5829 // Check that both operands are smis using the answer register as a
5830 // temporary.
5831 DeferredInlineBinaryOperation* deferred =
5832 new DeferredInlineBinaryOperation(op,
5833 answer.reg(),
5834 left->reg(),
5835 rcx,
5836 overwrite_mode);
5837 __ movq(answer.reg(), left->reg());
5838 __ or_(answer.reg(), rcx);
ager@chromium.org4af710e2009-09-15 12:20:11 +00005839 __ JumpIfNotSmi(answer.reg(), deferred->entry_label());
ager@chromium.org3e875802009-06-29 08:26:34 +00005840
ager@chromium.org3e875802009-06-29 08:26:34 +00005841 // Perform the operation.
5842 switch (op) {
5843 case Token::SAR:
ager@chromium.org4af710e2009-09-15 12:20:11 +00005844 __ SmiShiftArithmeticRight(answer.reg(), left->reg(), rcx);
ager@chromium.org3e875802009-06-29 08:26:34 +00005845 break;
5846 case Token::SHR: {
ager@chromium.org4af710e2009-09-15 12:20:11 +00005847 __ SmiShiftLogicalRight(answer.reg(),
5848 left->reg(),
5849 rcx,
5850 deferred->entry_label());
ager@chromium.org3e875802009-06-29 08:26:34 +00005851 break;
5852 }
5853 case Token::SHL: {
ager@chromium.org4af710e2009-09-15 12:20:11 +00005854 __ SmiShiftLeft(answer.reg(),
5855 left->reg(),
5856 rcx,
5857 deferred->entry_label());
ager@chromium.org3e875802009-06-29 08:26:34 +00005858 break;
5859 }
5860 default:
5861 UNREACHABLE();
5862 }
ager@chromium.org3e875802009-06-29 08:26:34 +00005863 deferred->BindExit();
5864 left->Unuse();
5865 right->Unuse();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005866 ASSERT(answer.is_valid());
5867 return answer;
ager@chromium.org3e875802009-06-29 08:26:34 +00005868 }
5869
5870 // Handle the other binary operations.
5871 left->ToRegister();
5872 right->ToRegister();
5873 // A newly allocated register answer is used to hold the answer. The
5874 // registers containing left and right are not modified so they don't
5875 // need to be spilled in the fast case.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005876 answer = allocator_->Allocate();
ager@chromium.org3e875802009-06-29 08:26:34 +00005877 ASSERT(answer.is_valid());
5878
5879 // Perform the smi tag check.
5880 DeferredInlineBinaryOperation* deferred =
5881 new DeferredInlineBinaryOperation(op,
5882 answer.reg(),
5883 left->reg(),
5884 right->reg(),
5885 overwrite_mode);
ager@chromium.org4af710e2009-09-15 12:20:11 +00005886 __ JumpIfNotBothSmi(left->reg(), right->reg(), deferred->entry_label());
5887
ager@chromium.org3e875802009-06-29 08:26:34 +00005888 switch (op) {
5889 case Token::ADD:
ager@chromium.org4af710e2009-09-15 12:20:11 +00005890 __ SmiAdd(answer.reg(),
5891 left->reg(),
5892 right->reg(),
5893 deferred->entry_label());
ager@chromium.org3e875802009-06-29 08:26:34 +00005894 break;
5895
5896 case Token::SUB:
ager@chromium.org4af710e2009-09-15 12:20:11 +00005897 __ SmiSub(answer.reg(),
5898 left->reg(),
5899 right->reg(),
5900 deferred->entry_label());
ager@chromium.org3e875802009-06-29 08:26:34 +00005901 break;
5902
5903 case Token::MUL: {
ager@chromium.org4af710e2009-09-15 12:20:11 +00005904 __ SmiMul(answer.reg(),
5905 left->reg(),
5906 right->reg(),
5907 deferred->entry_label());
ager@chromium.org3e875802009-06-29 08:26:34 +00005908 break;
5909 }
5910
5911 case Token::BIT_OR:
ager@chromium.org4af710e2009-09-15 12:20:11 +00005912 __ SmiOr(answer.reg(), left->reg(), right->reg());
ager@chromium.org3e875802009-06-29 08:26:34 +00005913 break;
5914
5915 case Token::BIT_AND:
ager@chromium.org4af710e2009-09-15 12:20:11 +00005916 __ SmiAnd(answer.reg(), left->reg(), right->reg());
ager@chromium.org3e875802009-06-29 08:26:34 +00005917 break;
5918
5919 case Token::BIT_XOR:
ager@chromium.org4af710e2009-09-15 12:20:11 +00005920 __ SmiXor(answer.reg(), left->reg(), right->reg());
ager@chromium.org3e875802009-06-29 08:26:34 +00005921 break;
5922
5923 default:
5924 UNREACHABLE();
5925 break;
5926 }
5927 deferred->BindExit();
5928 left->Unuse();
5929 right->Unuse();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005930 ASSERT(answer.is_valid());
5931 return answer;
5932}
5933
5934
5935Result CodeGenerator::EmitKeyedLoad(bool is_global) {
5936 Comment cmnt(masm_, "[ Load from keyed Property");
5937 // Inline array load code if inside of a loop. We do not know
5938 // the receiver map yet, so we initially generate the code with
5939 // a check against an invalid map. In the inline cache code, we
5940 // patch the map check if appropriate.
5941 if (loop_nesting() > 0) {
5942 Comment cmnt(masm_, "[ Inlined load from keyed Property");
5943
5944 Result key = frame_->Pop();
5945 Result receiver = frame_->Pop();
5946 key.ToRegister();
5947 receiver.ToRegister();
5948
5949 // Use a fresh temporary to load the elements without destroying
5950 // the receiver which is needed for the deferred slow case.
5951 Result elements = allocator()->Allocate();
5952 ASSERT(elements.is_valid());
5953
5954 // Use a fresh temporary for the index and later the loaded
5955 // value.
5956 Result index = allocator()->Allocate();
5957 ASSERT(index.is_valid());
5958
5959 DeferredReferenceGetKeyedValue* deferred =
5960 new DeferredReferenceGetKeyedValue(index.reg(),
5961 receiver.reg(),
5962 key.reg(),
5963 is_global);
5964
5965 // Check that the receiver is not a smi (only needed if this
5966 // is not a load from the global context) and that it has the
5967 // expected map.
5968 if (!is_global) {
5969 __ JumpIfSmi(receiver.reg(), deferred->entry_label());
5970 }
5971
5972 // Initially, use an invalid map. The map is patched in the IC
5973 // initialization code.
5974 __ bind(deferred->patch_site());
5975 // Use masm-> here instead of the double underscore macro since extra
5976 // coverage code can interfere with the patching. Do not use
5977 // root array to load null_value, since it must be patched with
5978 // the expected receiver map.
5979 masm_->movq(kScratchRegister, Factory::null_value(),
5980 RelocInfo::EMBEDDED_OBJECT);
5981 masm_->cmpq(FieldOperand(receiver.reg(), HeapObject::kMapOffset),
5982 kScratchRegister);
5983 deferred->Branch(not_equal);
5984
5985 // Check that the key is a non-negative smi.
5986 __ JumpIfNotPositiveSmi(key.reg(), deferred->entry_label());
5987
5988 // Get the elements array from the receiver and check that it
5989 // is not a dictionary.
5990 __ movq(elements.reg(),
5991 FieldOperand(receiver.reg(), JSObject::kElementsOffset));
5992 __ Cmp(FieldOperand(elements.reg(), HeapObject::kMapOffset),
5993 Factory::fixed_array_map());
5994 deferred->Branch(not_equal);
5995
5996 // Shift the key to get the actual index value and check that
5997 // it is within bounds.
5998 __ SmiToInteger32(index.reg(), key.reg());
5999 __ cmpl(index.reg(),
6000 FieldOperand(elements.reg(), FixedArray::kLengthOffset));
6001 deferred->Branch(above_equal);
6002
6003 // The index register holds the un-smi-tagged key. It has been
6004 // zero-extended to 64-bits, so it can be used directly as index in the
6005 // operand below.
6006 // Load and check that the result is not the hole. We could
6007 // reuse the index or elements register for the value.
6008 //
6009 // TODO(206): Consider whether it makes sense to try some
6010 // heuristic about which register to reuse. For example, if
6011 // one is rax, the we can reuse that one because the value
6012 // coming from the deferred code will be in rax.
6013 Result value = index;
6014 __ movq(value.reg(),
6015 Operand(elements.reg(),
6016 index.reg(),
6017 times_pointer_size,
6018 FixedArray::kHeaderSize - kHeapObjectTag));
6019 elements.Unuse();
6020 index.Unuse();
6021 __ CompareRoot(value.reg(), Heap::kTheHoleValueRootIndex);
6022 deferred->Branch(equal);
6023 __ IncrementCounter(&Counters::keyed_load_inline, 1);
6024
6025 deferred->BindExit();
6026 // Restore the receiver and key to the frame and push the
6027 // result on top of it.
6028 frame_->Push(&receiver);
6029 frame_->Push(&key);
6030 return value;
6031
6032 } else {
6033 Comment cmnt(masm_, "[ Load from keyed Property");
6034 RelocInfo::Mode mode = is_global
6035 ? RelocInfo::CODE_TARGET_CONTEXT
6036 : RelocInfo::CODE_TARGET;
6037 Result answer = frame_->CallKeyedLoadIC(mode);
6038 // Make sure that we do not have a test instruction after the
6039 // call. A test instruction after the call is used to
6040 // indicate that we have generated an inline version of the
6041 // keyed load. The explicit nop instruction is here because
6042 // the push that follows might be peep-hole optimized away.
6043 __ nop();
6044 return answer;
6045 }
ager@chromium.org3e875802009-06-29 08:26:34 +00006046}
6047
6048
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006049#undef __
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006050#define __ ACCESS_MASM(masm)
6051
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006052
6053Handle<String> Reference::GetName() {
6054 ASSERT(type_ == NAMED);
6055 Property* property = expression_->AsProperty();
6056 if (property == NULL) {
6057 // Global variable reference treated as a named property reference.
6058 VariableProxy* proxy = expression_->AsVariableProxy();
6059 ASSERT(proxy->AsVariable() != NULL);
6060 ASSERT(proxy->AsVariable()->is_global());
6061 return proxy->name();
6062 } else {
6063 Literal* raw_name = property->key()->AsLiteral();
6064 ASSERT(raw_name != NULL);
6065 return Handle<String>(String::cast(*raw_name->handle()));
6066 }
6067}
6068
6069
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006070void Reference::GetValue() {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006071 ASSERT(!cgen_->in_spilled_code());
6072 ASSERT(cgen_->HasValidEntryRegisters());
6073 ASSERT(!is_illegal());
6074 MacroAssembler* masm = cgen_->masm();
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00006075
6076 // Record the source position for the property load.
6077 Property* property = expression_->AsProperty();
6078 if (property != NULL) {
6079 cgen_->CodeForSourcePosition(property->position());
6080 }
6081
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006082 switch (type_) {
6083 case SLOT: {
6084 Comment cmnt(masm, "[ Load from Slot");
6085 Slot* slot = expression_->AsVariableProxy()->AsVariable()->slot();
6086 ASSERT(slot != NULL);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006087 cgen_->LoadFromSlotCheckForArguments(slot, NOT_INSIDE_TYPEOF);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006088 break;
6089 }
6090
6091 case NAMED: {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006092 Variable* var = expression_->AsVariableProxy()->AsVariable();
6093 bool is_global = var != NULL;
6094 ASSERT(!is_global || var->is_global());
6095
6096 // Do not inline the inobject property case for loads from the global
6097 // object. Also do not inline for unoptimized code. This saves time
6098 // in the code generator. Unoptimized code is toplevel code or code
6099 // that is not in a loop.
6100 if (is_global ||
6101 cgen_->scope()->is_global_scope() ||
6102 cgen_->loop_nesting() == 0) {
6103 Comment cmnt(masm, "[ Load from named Property");
6104 cgen_->frame()->Push(GetName());
6105
6106 RelocInfo::Mode mode = is_global
6107 ? RelocInfo::CODE_TARGET_CONTEXT
6108 : RelocInfo::CODE_TARGET;
6109 Result answer = cgen_->frame()->CallLoadIC(mode);
ager@chromium.org3e875802009-06-29 08:26:34 +00006110 // A test rax instruction following the call signals that the
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006111 // inobject property case was inlined. Ensure that there is not
ager@chromium.org3e875802009-06-29 08:26:34 +00006112 // a test rax instruction here.
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006113 __ nop();
6114 cgen_->frame()->Push(&answer);
6115 } else {
6116 // Inline the inobject property case.
6117 Comment cmnt(masm, "[ Inlined named property load");
6118 Result receiver = cgen_->frame()->Pop();
6119 receiver.ToRegister();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006120 Result value = cgen_->allocator()->Allocate();
6121 ASSERT(value.is_valid());
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00006122 // Cannot use r12 for receiver, because that changes
6123 // the distance between a call and a fixup location,
6124 // due to a special encoding of r12 as r/m in a ModR/M byte.
6125 if (receiver.reg().is(r12)) {
6126 // Swap receiver and value.
6127 __ movq(value.reg(), receiver.reg());
6128 Result temp = receiver;
6129 receiver = value;
6130 value = temp;
6131 cgen_->frame()->Spill(value.reg()); // r12 may have been shared.
6132 }
6133
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006134 DeferredReferenceGetNamedValue* deferred =
6135 new DeferredReferenceGetNamedValue(value.reg(),
6136 receiver.reg(),
6137 GetName());
6138
6139 // Check that the receiver is a heap object.
ager@chromium.org4af710e2009-09-15 12:20:11 +00006140 __ JumpIfSmi(receiver.reg(), deferred->entry_label());
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006141
6142 __ bind(deferred->patch_site());
6143 // This is the map check instruction that will be patched (so we can't
6144 // use the double underscore macro that may insert instructions).
6145 // Initially use an invalid map to force a failure.
6146 masm->Move(kScratchRegister, Factory::null_value());
6147 masm->cmpq(FieldOperand(receiver.reg(), HeapObject::kMapOffset),
6148 kScratchRegister);
6149 // This branch is always a forwards branch so it's always a fixed
6150 // size which allows the assert below to succeed and patching to work.
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00006151 // Don't use deferred->Branch(...), since that might add coverage code.
6152 masm->j(not_equal, deferred->entry_label());
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006153
6154 // The delta from the patch label to the load offset must be
6155 // statically known.
6156 ASSERT(masm->SizeOfCodeGeneratedSince(deferred->patch_site()) ==
6157 LoadIC::kOffsetToLoadInstruction);
6158 // The initial (invalid) offset has to be large enough to force
6159 // a 32-bit instruction encoding to allow patching with an
6160 // arbitrary offset. Use kMaxInt (minus kHeapObjectTag).
6161 int offset = kMaxInt;
6162 masm->movq(value.reg(), FieldOperand(receiver.reg(), offset));
6163
6164 __ IncrementCounter(&Counters::named_load_inline, 1);
6165 deferred->BindExit();
6166 cgen_->frame()->Push(&receiver);
6167 cgen_->frame()->Push(&value);
6168 }
6169 break;
6170 }
6171
6172 case KEYED: {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006173 Comment cmnt(masm, "[ Load from keyed Property");
6174 Variable* var = expression_->AsVariableProxy()->AsVariable();
6175 bool is_global = var != NULL;
6176 ASSERT(!is_global || var->is_global());
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00006177
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006178 Result value = cgen_->EmitKeyedLoad(is_global);
6179 cgen_->frame()->Push(&value);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006180 break;
6181 }
6182
6183 default:
6184 UNREACHABLE();
6185 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006186
6187 if (!persist_after_get_) {
6188 cgen_->UnloadReference(this);
6189 }
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006190}
6191
6192
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006193void Reference::TakeValue() {
ager@chromium.org3e875802009-06-29 08:26:34 +00006194 // TODO(X64): This function is completely architecture independent. Move
6195 // it somewhere shared.
6196
6197 // For non-constant frame-allocated slots, we invalidate the value in the
6198 // slot. For all others, we fall back on GetValue.
6199 ASSERT(!cgen_->in_spilled_code());
6200 ASSERT(!is_illegal());
6201 if (type_ != SLOT) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006202 GetValue();
ager@chromium.org3e875802009-06-29 08:26:34 +00006203 return;
6204 }
6205
6206 Slot* slot = expression_->AsVariableProxy()->AsVariable()->slot();
6207 ASSERT(slot != NULL);
6208 if (slot->type() == Slot::LOOKUP ||
6209 slot->type() == Slot::CONTEXT ||
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +00006210 slot->var()->mode() == Variable::CONST ||
6211 slot->is_arguments()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006212 GetValue();
ager@chromium.org3e875802009-06-29 08:26:34 +00006213 return;
6214 }
6215
6216 // Only non-constant, frame-allocated parameters and locals can reach
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +00006217 // here. Be careful not to use the optimizations for arguments
6218 // object access since it may not have been initialized yet.
6219 ASSERT(!slot->is_arguments());
ager@chromium.org3e875802009-06-29 08:26:34 +00006220 if (slot->type() == Slot::PARAMETER) {
6221 cgen_->frame()->TakeParameterAt(slot->index());
6222 } else {
6223 ASSERT(slot->type() == Slot::LOCAL);
6224 cgen_->frame()->TakeLocalAt(slot->index());
6225 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006226
6227 ASSERT(persist_after_get_);
6228 // Do not unload the reference, because it is used in SetValue.
ager@chromium.org3e875802009-06-29 08:26:34 +00006229}
6230
6231
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006232void Reference::SetValue(InitState init_state) {
6233 ASSERT(cgen_->HasValidEntryRegisters());
6234 ASSERT(!is_illegal());
6235 MacroAssembler* masm = cgen_->masm();
6236 switch (type_) {
6237 case SLOT: {
6238 Comment cmnt(masm, "[ Store to Slot");
6239 Slot* slot = expression_->AsVariableProxy()->AsVariable()->slot();
6240 ASSERT(slot != NULL);
6241 cgen_->StoreToSlot(slot, init_state);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006242 cgen_->UnloadReference(this);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006243 break;
6244 }
6245
6246 case NAMED: {
6247 Comment cmnt(masm, "[ Store to named Property");
6248 cgen_->frame()->Push(GetName());
6249 Result answer = cgen_->frame()->CallStoreIC();
6250 cgen_->frame()->Push(&answer);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006251 set_unloaded();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006252 break;
6253 }
6254
6255 case KEYED: {
6256 Comment cmnt(masm, "[ Store to keyed Property");
6257
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00006258 // Generate inlined version of the keyed store if the code is in
6259 // a loop and the key is likely to be a smi.
6260 Property* property = expression()->AsProperty();
6261 ASSERT(property != NULL);
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00006262 StaticType* key_smi_analysis = property->key()->type();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006263
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00006264 if (cgen_->loop_nesting() > 0 && key_smi_analysis->IsLikelySmi()) {
6265 Comment cmnt(masm, "[ Inlined store to keyed Property");
6266
6267 // Get the receiver, key and value into registers.
6268 Result value = cgen_->frame()->Pop();
6269 Result key = cgen_->frame()->Pop();
6270 Result receiver = cgen_->frame()->Pop();
6271
6272 Result tmp = cgen_->allocator_->Allocate();
6273 ASSERT(tmp.is_valid());
6274
6275 // Determine whether the value is a constant before putting it
6276 // in a register.
6277 bool value_is_constant = value.is_constant();
6278
6279 // Make sure that value, key and receiver are in registers.
6280 value.ToRegister();
6281 key.ToRegister();
6282 receiver.ToRegister();
6283
6284 DeferredReferenceSetKeyedValue* deferred =
6285 new DeferredReferenceSetKeyedValue(value.reg(),
6286 key.reg(),
6287 receiver.reg());
6288
6289 // Check that the value is a smi if it is not a constant.
6290 // We can skip the write barrier for smis and constants.
6291 if (!value_is_constant) {
ager@chromium.org4af710e2009-09-15 12:20:11 +00006292 __ JumpIfNotSmi(value.reg(), deferred->entry_label());
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00006293 }
6294
6295 // Check that the key is a non-negative smi.
ager@chromium.org4af710e2009-09-15 12:20:11 +00006296 __ JumpIfNotPositiveSmi(key.reg(), deferred->entry_label());
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00006297
6298 // Check that the receiver is not a smi.
ager@chromium.org4af710e2009-09-15 12:20:11 +00006299 __ JumpIfSmi(receiver.reg(), deferred->entry_label());
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00006300
6301 // Check that the receiver is a JSArray.
6302 __ CmpObjectType(receiver.reg(), JS_ARRAY_TYPE, kScratchRegister);
6303 deferred->Branch(not_equal);
6304
6305 // Check that the key is within bounds. Both the key and the
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00006306 // length of the JSArray are smis.
6307 __ SmiCompare(FieldOperand(receiver.reg(), JSArray::kLengthOffset),
6308 key.reg());
6309 deferred->Branch(less_equal);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00006310
6311 // Get the elements array from the receiver and check that it
6312 // is a flat array (not a dictionary).
6313 __ movq(tmp.reg(),
6314 FieldOperand(receiver.reg(), JSObject::kElementsOffset));
6315 // Bind the deferred code patch site to be able to locate the
6316 // fixed array map comparison. When debugging, we patch this
6317 // comparison to always fail so that we will hit the IC call
6318 // in the deferred code which will allow the debugger to
6319 // break for fast case stores.
6320 __ bind(deferred->patch_site());
6321 // Avoid using __ to ensure the distance from patch_site
6322 // to the map address is always the same.
6323 masm->movq(kScratchRegister, Factory::fixed_array_map(),
6324 RelocInfo::EMBEDDED_OBJECT);
6325 __ cmpq(FieldOperand(tmp.reg(), HeapObject::kMapOffset),
6326 kScratchRegister);
6327 deferred->Branch(not_equal);
6328
6329 // Store the value.
ager@chromium.org4af710e2009-09-15 12:20:11 +00006330 SmiIndex index =
6331 masm->SmiToIndex(kScratchRegister, key.reg(), kPointerSizeLog2);
6332 __ movq(Operand(tmp.reg(),
6333 index.reg,
6334 index.scale,
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00006335 FixedArray::kHeaderSize - kHeapObjectTag),
6336 value.reg());
6337 __ IncrementCounter(&Counters::keyed_store_inline, 1);
6338
6339 deferred->BindExit();
6340
6341 cgen_->frame()->Push(&receiver);
6342 cgen_->frame()->Push(&key);
6343 cgen_->frame()->Push(&value);
6344 } else {
6345 Result answer = cgen_->frame()->CallKeyedStoreIC();
6346 // Make sure that we do not have a test instruction after the
6347 // call. A test instruction after the call is used to
6348 // indicate that we have generated an inline version of the
6349 // keyed store.
6350 masm->nop();
6351 cgen_->frame()->Push(&answer);
6352 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006353 cgen_->UnloadReference(this);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006354 break;
6355 }
6356
6357 default:
6358 UNREACHABLE();
6359 }
6360}
6361
6362
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006363void FastNewClosureStub::Generate(MacroAssembler* masm) {
6364 // Clone the boilerplate in new space. Set the context to the
6365 // current context in rsi.
6366 Label gc;
6367 __ AllocateInNewSpace(JSFunction::kSize, rax, rbx, rcx, &gc, TAG_OBJECT);
6368
6369 // Get the boilerplate function from the stack.
6370 __ movq(rdx, Operand(rsp, 1 * kPointerSize));
6371
6372 // Compute the function map in the current global context and set that
6373 // as the map of the allocated object.
6374 __ movq(rcx, Operand(rsi, Context::SlotOffset(Context::GLOBAL_INDEX)));
6375 __ movq(rcx, FieldOperand(rcx, GlobalObject::kGlobalContextOffset));
6376 __ movq(rcx, Operand(rcx, Context::SlotOffset(Context::FUNCTION_MAP_INDEX)));
6377 __ movq(FieldOperand(rax, JSObject::kMapOffset), rcx);
6378
6379 // Clone the rest of the boilerplate fields. We don't have to update
6380 // the write barrier because the allocated object is in new space.
6381 for (int offset = kPointerSize;
6382 offset < JSFunction::kSize;
6383 offset += kPointerSize) {
6384 if (offset == JSFunction::kContextOffset) {
6385 __ movq(FieldOperand(rax, offset), rsi);
6386 } else {
6387 __ movq(rbx, FieldOperand(rdx, offset));
6388 __ movq(FieldOperand(rax, offset), rbx);
6389 }
6390 }
6391
6392 // Return and remove the on-stack parameter.
6393 __ ret(1 * kPointerSize);
6394
6395 // Create a new closure through the slower runtime call.
6396 __ bind(&gc);
6397 __ pop(rcx); // Temporarily remove return address.
6398 __ pop(rdx);
6399 __ push(rsi);
6400 __ push(rdx);
6401 __ push(rcx); // Restore return address.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006402 __ TailCallRuntime(Runtime::kNewClosure, 2, 1);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006403}
6404
6405
6406void FastNewContextStub::Generate(MacroAssembler* masm) {
6407 // Try to allocate the context in new space.
6408 Label gc;
6409 int length = slots_ + Context::MIN_CONTEXT_SLOTS;
6410 __ AllocateInNewSpace((length * kPointerSize) + FixedArray::kHeaderSize,
6411 rax, rbx, rcx, &gc, TAG_OBJECT);
6412
6413 // Get the function from the stack.
6414 __ movq(rcx, Operand(rsp, 1 * kPointerSize));
6415
6416 // Setup the object header.
6417 __ LoadRoot(kScratchRegister, Heap::kContextMapRootIndex);
6418 __ movq(FieldOperand(rax, HeapObject::kMapOffset), kScratchRegister);
6419 __ movl(FieldOperand(rax, Array::kLengthOffset), Immediate(length));
6420
6421 // Setup the fixed slots.
6422 __ xor_(rbx, rbx); // Set to NULL.
6423 __ movq(Operand(rax, Context::SlotOffset(Context::CLOSURE_INDEX)), rcx);
6424 __ movq(Operand(rax, Context::SlotOffset(Context::FCONTEXT_INDEX)), rax);
6425 __ movq(Operand(rax, Context::SlotOffset(Context::PREVIOUS_INDEX)), rbx);
6426 __ movq(Operand(rax, Context::SlotOffset(Context::EXTENSION_INDEX)), rbx);
6427
6428 // Copy the global object from the surrounding context.
6429 __ movq(rbx, Operand(rsi, Context::SlotOffset(Context::GLOBAL_INDEX)));
6430 __ movq(Operand(rax, Context::SlotOffset(Context::GLOBAL_INDEX)), rbx);
6431
6432 // Initialize the rest of the slots to undefined.
6433 __ LoadRoot(rbx, Heap::kUndefinedValueRootIndex);
6434 for (int i = Context::MIN_CONTEXT_SLOTS; i < length; i++) {
6435 __ movq(Operand(rax, Context::SlotOffset(i)), rbx);
6436 }
6437
6438 // Return and remove the on-stack parameter.
6439 __ movq(rsi, rax);
6440 __ ret(1 * kPointerSize);
6441
6442 // Need to collect. Call into runtime system.
6443 __ bind(&gc);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006444 __ TailCallRuntime(Runtime::kNewContext, 1, 1);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006445}
6446
6447
ager@chromium.org5c838252010-02-19 08:53:10 +00006448void FastCloneShallowArrayStub::Generate(MacroAssembler* masm) {
6449 // Stack layout on entry:
6450 //
6451 // [rsp + kPointerSize]: constant elements.
6452 // [rsp + (2 * kPointerSize)]: literal index.
6453 // [rsp + (3 * kPointerSize)]: literals array.
6454
6455 // All sizes here are multiples of kPointerSize.
6456 int elements_size = (length_ > 0) ? FixedArray::SizeFor(length_) : 0;
6457 int size = JSArray::kSize + elements_size;
6458
6459 // Load boilerplate object into rcx and check if we need to create a
6460 // boilerplate.
6461 Label slow_case;
6462 __ movq(rcx, Operand(rsp, 3 * kPointerSize));
6463 __ movq(rax, Operand(rsp, 2 * kPointerSize));
6464 SmiIndex index = masm->SmiToIndex(rax, rax, kPointerSizeLog2);
6465 __ movq(rcx,
6466 FieldOperand(rcx, index.reg, index.scale, FixedArray::kHeaderSize));
6467 __ CompareRoot(rcx, Heap::kUndefinedValueRootIndex);
6468 __ j(equal, &slow_case);
6469
6470 // Allocate both the JS array and the elements array in one big
6471 // allocation. This avoids multiple limit checks.
6472 __ AllocateInNewSpace(size, rax, rbx, rdx, &slow_case, TAG_OBJECT);
6473
6474 // Copy the JS array part.
6475 for (int i = 0; i < JSArray::kSize; i += kPointerSize) {
6476 if ((i != JSArray::kElementsOffset) || (length_ == 0)) {
6477 __ movq(rbx, FieldOperand(rcx, i));
6478 __ movq(FieldOperand(rax, i), rbx);
6479 }
6480 }
6481
6482 if (length_ > 0) {
6483 // Get hold of the elements array of the boilerplate and setup the
6484 // elements pointer in the resulting object.
6485 __ movq(rcx, FieldOperand(rcx, JSArray::kElementsOffset));
6486 __ lea(rdx, Operand(rax, JSArray::kSize));
6487 __ movq(FieldOperand(rax, JSArray::kElementsOffset), rdx);
6488
6489 // Copy the elements array.
6490 for (int i = 0; i < elements_size; i += kPointerSize) {
6491 __ movq(rbx, FieldOperand(rcx, i));
6492 __ movq(FieldOperand(rdx, i), rbx);
6493 }
6494 }
6495
6496 // Return and remove the on-stack parameters.
6497 __ ret(3 * kPointerSize);
6498
6499 __ bind(&slow_case);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006500 __ TailCallRuntime(Runtime::kCreateArrayLiteralShallow, 3, 1);
ager@chromium.org5c838252010-02-19 08:53:10 +00006501}
6502
6503
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006504void ToBooleanStub::Generate(MacroAssembler* masm) {
6505 Label false_result, true_result, not_string;
6506 __ movq(rax, Operand(rsp, 1 * kPointerSize));
6507
6508 // 'null' => false.
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006509 __ CompareRoot(rax, Heap::kNullValueRootIndex);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006510 __ j(equal, &false_result);
6511
6512 // Get the map and type of the heap object.
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00006513 // We don't use CmpObjectType because we manipulate the type field.
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006514 __ movq(rdx, FieldOperand(rax, HeapObject::kMapOffset));
6515 __ movzxbq(rcx, FieldOperand(rdx, Map::kInstanceTypeOffset));
6516
6517 // Undetectable => false.
6518 __ movzxbq(rbx, FieldOperand(rdx, Map::kBitFieldOffset));
6519 __ and_(rbx, Immediate(1 << Map::kIsUndetectable));
6520 __ j(not_zero, &false_result);
6521
6522 // JavaScript object => true.
6523 __ cmpq(rcx, Immediate(FIRST_JS_OBJECT_TYPE));
6524 __ j(above_equal, &true_result);
6525
6526 // String value => false iff empty.
6527 __ cmpq(rcx, Immediate(FIRST_NONSTRING_TYPE));
6528 __ j(above_equal, &not_string);
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00006529 __ movl(rdx, FieldOperand(rax, String::kLengthOffset));
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00006530 __ testl(rdx, rdx);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006531 __ j(zero, &false_result);
6532 __ jmp(&true_result);
6533
6534 __ bind(&not_string);
6535 // HeapNumber => false iff +0, -0, or NaN.
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00006536 // These three cases set C3 when compared to zero in the FPU.
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006537 __ CompareRoot(rdx, Heap::kHeapNumberMapRootIndex);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006538 __ j(not_equal, &true_result);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006539 __ fldz(); // Load zero onto fp stack
6540 // Load heap-number double value onto fp stack
6541 __ fld_d(FieldOperand(rax, HeapNumber::kValueOffset));
ager@chromium.org3811b432009-10-28 14:53:37 +00006542 __ FCmp();
6543 __ j(zero, &false_result);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006544 // Fall through to |true_result|.
6545
6546 // Return 1/0 for true/false in rax.
6547 __ bind(&true_result);
6548 __ movq(rax, Immediate(1));
6549 __ ret(1 * kPointerSize);
6550 __ bind(&false_result);
6551 __ xor_(rax, rax);
6552 __ ret(1 * kPointerSize);
6553}
6554
6555
ager@chromium.org3e875802009-06-29 08:26:34 +00006556bool CodeGenerator::FoldConstantSmis(Token::Value op, int left, int right) {
ager@chromium.org3e875802009-06-29 08:26:34 +00006557 Object* answer_object = Heap::undefined_value();
6558 switch (op) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006559 case Token::ADD:
kasperl@chromium.org74e4e5e2010-01-25 10:15:52 +00006560 // Use intptr_t to detect overflow of 32-bit int.
6561 if (Smi::IsValid(static_cast<intptr_t>(left) + right)) {
ager@chromium.org3e875802009-06-29 08:26:34 +00006562 answer_object = Smi::FromInt(left + right);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006563 }
ager@chromium.org3e875802009-06-29 08:26:34 +00006564 break;
6565 case Token::SUB:
kasperl@chromium.org74e4e5e2010-01-25 10:15:52 +00006566 // Use intptr_t to detect overflow of 32-bit int.
6567 if (Smi::IsValid(static_cast<intptr_t>(left) - right)) {
ager@chromium.org3e875802009-06-29 08:26:34 +00006568 answer_object = Smi::FromInt(left - right);
6569 }
6570 break;
6571 case Token::MUL: {
6572 double answer = static_cast<double>(left) * right;
6573 if (answer >= Smi::kMinValue && answer <= Smi::kMaxValue) {
6574 // If the product is zero and the non-zero factor is negative,
6575 // the spec requires us to return floating point negative zero.
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006576 if (answer != 0 || (left + right) >= 0) {
ager@chromium.org3e875802009-06-29 08:26:34 +00006577 answer_object = Smi::FromInt(static_cast<int>(answer));
6578 }
6579 }
6580 }
6581 break;
6582 case Token::DIV:
6583 case Token::MOD:
6584 break;
6585 case Token::BIT_OR:
6586 answer_object = Smi::FromInt(left | right);
6587 break;
6588 case Token::BIT_AND:
6589 answer_object = Smi::FromInt(left & right);
6590 break;
6591 case Token::BIT_XOR:
6592 answer_object = Smi::FromInt(left ^ right);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006593 break;
6594
ager@chromium.org3e875802009-06-29 08:26:34 +00006595 case Token::SHL: {
6596 int shift_amount = right & 0x1F;
6597 if (Smi::IsValid(left << shift_amount)) {
6598 answer_object = Smi::FromInt(left << shift_amount);
6599 }
6600 break;
6601 }
6602 case Token::SHR: {
6603 int shift_amount = right & 0x1F;
6604 unsigned int unsigned_left = left;
6605 unsigned_left >>= shift_amount;
6606 if (unsigned_left <= static_cast<unsigned int>(Smi::kMaxValue)) {
6607 answer_object = Smi::FromInt(unsigned_left);
6608 }
6609 break;
6610 }
6611 case Token::SAR: {
6612 int shift_amount = right & 0x1F;
6613 unsigned int unsigned_left = left;
6614 if (left < 0) {
6615 // Perform arithmetic shift of a negative number by
6616 // complementing number, logical shifting, complementing again.
6617 unsigned_left = ~unsigned_left;
6618 unsigned_left >>= shift_amount;
6619 unsigned_left = ~unsigned_left;
6620 } else {
6621 unsigned_left >>= shift_amount;
6622 }
ager@chromium.orgab99eea2009-08-25 07:05:41 +00006623 ASSERT(Smi::IsValid(static_cast<int32_t>(unsigned_left)));
6624 answer_object = Smi::FromInt(static_cast<int32_t>(unsigned_left));
ager@chromium.org3e875802009-06-29 08:26:34 +00006625 break;
6626 }
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006627 default:
6628 UNREACHABLE();
6629 break;
6630 }
ager@chromium.org3e875802009-06-29 08:26:34 +00006631 if (answer_object == Heap::undefined_value()) {
6632 return false;
6633 }
6634 frame_->Push(Handle<Object>(answer_object));
6635 return true;
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006636}
6637
6638
ager@chromium.org3e875802009-06-29 08:26:34 +00006639// End of CodeGenerator implementation.
6640
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006641// Get the integer part of a heap number. Surprisingly, all this bit twiddling
6642// is faster than using the built-in instructions on floating point registers.
6643// Trashes rdi and rbx. Dest is rcx. Source cannot be rcx or one of the
6644// trashed registers.
6645void IntegerConvert(MacroAssembler* masm,
6646 Register source,
6647 bool use_sse3,
6648 Label* conversion_failure) {
6649 ASSERT(!source.is(rcx) && !source.is(rdi) && !source.is(rbx));
6650 Label done, right_exponent, normal_exponent;
6651 Register scratch = rbx;
6652 Register scratch2 = rdi;
6653 // Get exponent word.
6654 __ movl(scratch, FieldOperand(source, HeapNumber::kExponentOffset));
6655 // Get exponent alone in scratch2.
6656 __ movl(scratch2, scratch);
6657 __ and_(scratch2, Immediate(HeapNumber::kExponentMask));
6658 if (use_sse3) {
6659 CpuFeatures::Scope scope(SSE3);
6660 // Check whether the exponent is too big for a 64 bit signed integer.
6661 static const uint32_t kTooBigExponent =
6662 (HeapNumber::kExponentBias + 63) << HeapNumber::kExponentShift;
6663 __ cmpl(scratch2, Immediate(kTooBigExponent));
6664 __ j(greater_equal, conversion_failure);
6665 // Load x87 register with heap number.
6666 __ fld_d(FieldOperand(source, HeapNumber::kValueOffset));
6667 // Reserve space for 64 bit answer.
6668 __ subq(rsp, Immediate(sizeof(uint64_t))); // Nolint.
6669 // Do conversion, which cannot fail because we checked the exponent.
6670 __ fisttp_d(Operand(rsp, 0));
6671 __ movl(rcx, Operand(rsp, 0)); // Load low word of answer into rcx.
6672 __ addq(rsp, Immediate(sizeof(uint64_t))); // Nolint.
6673 } else {
6674 // Load rcx with zero. We use this either for the final shift or
6675 // for the answer.
6676 __ xor_(rcx, rcx);
6677 // Check whether the exponent matches a 32 bit signed int that cannot be
6678 // represented by a Smi. A non-smi 32 bit integer is 1.xxx * 2^30 so the
6679 // exponent is 30 (biased). This is the exponent that we are fastest at and
6680 // also the highest exponent we can handle here.
6681 const uint32_t non_smi_exponent =
6682 (HeapNumber::kExponentBias + 30) << HeapNumber::kExponentShift;
6683 __ cmpl(scratch2, Immediate(non_smi_exponent));
6684 // If we have a match of the int32-but-not-Smi exponent then skip some
6685 // logic.
6686 __ j(equal, &right_exponent);
6687 // If the exponent is higher than that then go to slow case. This catches
6688 // numbers that don't fit in a signed int32, infinities and NaNs.
6689 __ j(less, &normal_exponent);
6690
6691 {
6692 // Handle a big exponent. The only reason we have this code is that the
6693 // >>> operator has a tendency to generate numbers with an exponent of 31.
6694 const uint32_t big_non_smi_exponent =
6695 (HeapNumber::kExponentBias + 31) << HeapNumber::kExponentShift;
6696 __ cmpl(scratch2, Immediate(big_non_smi_exponent));
6697 __ j(not_equal, conversion_failure);
6698 // We have the big exponent, typically from >>>. This means the number is
6699 // in the range 2^31 to 2^32 - 1. Get the top bits of the mantissa.
6700 __ movl(scratch2, scratch);
6701 __ and_(scratch2, Immediate(HeapNumber::kMantissaMask));
6702 // Put back the implicit 1.
6703 __ or_(scratch2, Immediate(1 << HeapNumber::kExponentShift));
6704 // Shift up the mantissa bits to take up the space the exponent used to
6705 // take. We just orred in the implicit bit so that took care of one and
6706 // we want to use the full unsigned range so we subtract 1 bit from the
6707 // shift distance.
6708 const int big_shift_distance = HeapNumber::kNonMantissaBitsInTopWord - 1;
6709 __ shl(scratch2, Immediate(big_shift_distance));
6710 // Get the second half of the double.
6711 __ movl(rcx, FieldOperand(source, HeapNumber::kMantissaOffset));
6712 // Shift down 21 bits to get the most significant 11 bits or the low
6713 // mantissa word.
6714 __ shr(rcx, Immediate(32 - big_shift_distance));
6715 __ or_(rcx, scratch2);
6716 // We have the answer in rcx, but we may need to negate it.
6717 __ testl(scratch, scratch);
6718 __ j(positive, &done);
6719 __ neg(rcx);
6720 __ jmp(&done);
6721 }
6722
6723 __ bind(&normal_exponent);
6724 // Exponent word in scratch, exponent part of exponent word in scratch2.
6725 // Zero in rcx.
6726 // We know the exponent is smaller than 30 (biased). If it is less than
6727 // 0 (biased) then the number is smaller in magnitude than 1.0 * 2^0, ie
6728 // it rounds to zero.
6729 const uint32_t zero_exponent =
6730 (HeapNumber::kExponentBias + 0) << HeapNumber::kExponentShift;
6731 __ subl(scratch2, Immediate(zero_exponent));
6732 // rcx already has a Smi zero.
6733 __ j(less, &done);
6734
6735 // We have a shifted exponent between 0 and 30 in scratch2.
6736 __ shr(scratch2, Immediate(HeapNumber::kExponentShift));
6737 __ movl(rcx, Immediate(30));
6738 __ subl(rcx, scratch2);
6739
6740 __ bind(&right_exponent);
6741 // Here rcx is the shift, scratch is the exponent word.
6742 // Get the top bits of the mantissa.
6743 __ and_(scratch, Immediate(HeapNumber::kMantissaMask));
6744 // Put back the implicit 1.
6745 __ or_(scratch, Immediate(1 << HeapNumber::kExponentShift));
6746 // Shift up the mantissa bits to take up the space the exponent used to
6747 // take. We have kExponentShift + 1 significant bits int he low end of the
6748 // word. Shift them to the top bits.
6749 const int shift_distance = HeapNumber::kNonMantissaBitsInTopWord - 2;
6750 __ shl(scratch, Immediate(shift_distance));
6751 // Get the second half of the double. For some exponents we don't
6752 // actually need this because the bits get shifted out again, but
6753 // it's probably slower to test than just to do it.
6754 __ movl(scratch2, FieldOperand(source, HeapNumber::kMantissaOffset));
6755 // Shift down 22 bits to get the most significant 10 bits or the low
6756 // mantissa word.
6757 __ shr(scratch2, Immediate(32 - shift_distance));
6758 __ or_(scratch2, scratch);
6759 // Move down according to the exponent.
6760 __ shr_cl(scratch2);
6761 // Now the unsigned answer is in scratch2. We need to move it to rcx and
6762 // we may need to fix the sign.
6763 Label negative;
6764 __ xor_(rcx, rcx);
6765 __ cmpl(rcx, FieldOperand(source, HeapNumber::kExponentOffset));
6766 __ j(greater, &negative);
6767 __ movl(rcx, scratch2);
6768 __ jmp(&done);
6769 __ bind(&negative);
6770 __ subl(rcx, scratch2);
6771 __ bind(&done);
6772 }
6773}
6774
6775
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006776void GenericUnaryOpStub::Generate(MacroAssembler* masm) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006777 Label slow, done;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006778
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006779 if (op_ == Token::SUB) {
6780 // Check whether the value is a smi.
6781 Label try_float;
6782 __ JumpIfNotSmi(rax, &try_float);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006783
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006784 // Enter runtime system if the value of the smi is zero
6785 // to make sure that we switch between 0 and -0.
6786 // Also enter it if the value of the smi is Smi::kMinValue.
6787 __ SmiNeg(rax, rax, &done);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006788
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006789 // Either zero or Smi::kMinValue, neither of which become a smi when
6790 // negated.
6791 __ SmiCompare(rax, Smi::FromInt(0));
6792 __ j(not_equal, &slow);
6793 __ Move(rax, Factory::minus_zero_value());
6794 __ jmp(&done);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006795
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006796 // Try floating point case.
6797 __ bind(&try_float);
6798 __ movq(rdx, FieldOperand(rax, HeapObject::kMapOffset));
6799 __ CompareRoot(rdx, Heap::kHeapNumberMapRootIndex);
6800 __ j(not_equal, &slow);
6801 // Operand is a float, negate its value by flipping sign bit.
6802 __ movq(rdx, FieldOperand(rax, HeapNumber::kValueOffset));
6803 __ movq(kScratchRegister, Immediate(0x01));
6804 __ shl(kScratchRegister, Immediate(63));
6805 __ xor_(rdx, kScratchRegister); // Flip sign.
6806 // rdx is value to store.
6807 if (overwrite_) {
6808 __ movq(FieldOperand(rax, HeapNumber::kValueOffset), rdx);
6809 } else {
6810 __ AllocateHeapNumber(rcx, rbx, &slow);
6811 // rcx: allocated 'empty' number
6812 __ movq(FieldOperand(rcx, HeapNumber::kValueOffset), rdx);
6813 __ movq(rax, rcx);
6814 }
6815 } else if (op_ == Token::BIT_NOT) {
6816 // Check if the operand is a heap number.
6817 __ movq(rdx, FieldOperand(rax, HeapObject::kMapOffset));
6818 __ CompareRoot(rdx, Heap::kHeapNumberMapRootIndex);
6819 __ j(not_equal, &slow);
6820
6821 // Convert the heap number in rax to an untagged integer in rcx.
6822 IntegerConvert(masm, rax, CpuFeatures::IsSupported(SSE3), &slow);
6823
6824 // Do the bitwise operation and check if the result fits in a smi.
6825 Label try_float;
6826 __ not_(rcx);
6827 // Tag the result as a smi and we're done.
6828 ASSERT(kSmiTagSize == 1);
6829 __ Integer32ToSmi(rax, rcx);
6830 }
6831
6832 // Return from the stub.
6833 __ bind(&done);
6834 __ StubReturn(1);
6835
6836 // Handle the slow case by jumping to the JavaScript builtin.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006837 __ bind(&slow);
6838 __ pop(rcx); // pop return address
6839 __ push(rax);
6840 __ push(rcx); // push return address
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006841 switch (op_) {
6842 case Token::SUB:
6843 __ InvokeBuiltin(Builtins::UNARY_MINUS, JUMP_FUNCTION);
6844 break;
6845 case Token::BIT_NOT:
6846 __ InvokeBuiltin(Builtins::BIT_NOT, JUMP_FUNCTION);
6847 break;
6848 default:
6849 UNREACHABLE();
6850 }
6851}
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006852
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006853
6854void RegExpExecStub::Generate(MacroAssembler* masm) {
6855 // Just jump directly to runtime if native RegExp is not selected at compile
6856 // time or if regexp entry in generated code is turned off runtime switch or
6857 // at compilation.
6858#ifndef V8_NATIVE_REGEXP
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006859 __ TailCallRuntime(Runtime::kRegExpExec, 4, 1);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006860#else // V8_NATIVE_REGEXP
6861 if (!FLAG_regexp_entry_native) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006862 __ TailCallRuntime(Runtime::kRegExpExec, 4, 1);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006863 return;
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006864 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006865
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006866 // Stack frame on entry.
6867 // esp[0]: return address
6868 // esp[8]: last_match_info (expected JSArray)
6869 // esp[16]: previous index
6870 // esp[24]: subject string
6871 // esp[32]: JSRegExp object
6872
6873 static const int kLastMatchInfoOffset = 1 * kPointerSize;
6874 static const int kPreviousIndexOffset = 2 * kPointerSize;
6875 static const int kSubjectOffset = 3 * kPointerSize;
6876 static const int kJSRegExpOffset = 4 * kPointerSize;
6877
6878 Label runtime;
6879
6880 // Ensure that a RegExp stack is allocated.
6881 ExternalReference address_of_regexp_stack_memory_address =
6882 ExternalReference::address_of_regexp_stack_memory_address();
6883 ExternalReference address_of_regexp_stack_memory_size =
6884 ExternalReference::address_of_regexp_stack_memory_size();
6885 __ movq(kScratchRegister, address_of_regexp_stack_memory_size);
6886 __ movq(kScratchRegister, Operand(kScratchRegister, 0));
6887 __ testq(kScratchRegister, kScratchRegister);
6888 __ j(zero, &runtime);
6889
6890
6891 // Check that the first argument is a JSRegExp object.
6892 __ movq(rax, Operand(rsp, kJSRegExpOffset));
6893 __ JumpIfSmi(rax, &runtime);
6894 __ CmpObjectType(rax, JS_REGEXP_TYPE, kScratchRegister);
6895 __ j(not_equal, &runtime);
6896 // Check that the RegExp has been compiled (data contains a fixed array).
6897 __ movq(rcx, FieldOperand(rax, JSRegExp::kDataOffset));
6898 if (FLAG_debug_code) {
6899 Condition is_smi = masm->CheckSmi(rcx);
6900 __ Check(NegateCondition(is_smi),
6901 "Unexpected type for RegExp data, FixedArray expected");
6902 __ CmpObjectType(rcx, FIXED_ARRAY_TYPE, kScratchRegister);
6903 __ Check(equal, "Unexpected type for RegExp data, FixedArray expected");
6904 }
6905
6906 // rcx: RegExp data (FixedArray)
6907 // Check the type of the RegExp. Only continue if type is JSRegExp::IRREGEXP.
6908 __ movq(rbx, FieldOperand(rcx, JSRegExp::kDataTagOffset));
6909 __ SmiCompare(rbx, Smi::FromInt(JSRegExp::IRREGEXP));
6910 __ j(not_equal, &runtime);
6911
6912 // rcx: RegExp data (FixedArray)
6913 // Check that the number of captures fit in the static offsets vector buffer.
6914 __ movq(rdx, FieldOperand(rcx, JSRegExp::kIrregexpCaptureCountOffset));
6915 // Calculate number of capture registers (number_of_captures + 1) * 2.
6916 __ PositiveSmiTimesPowerOfTwoToInteger64(rdx, rdx, 1);
6917 __ addq(rdx, Immediate(2)); // rdx was number_of_captures * 2.
6918 // Check that the static offsets vector buffer is large enough.
6919 __ cmpq(rdx, Immediate(OffsetsVector::kStaticOffsetsVectorSize));
6920 __ j(above, &runtime);
6921
6922 // rcx: RegExp data (FixedArray)
6923 // rdx: Number of capture registers
6924 // Check that the second argument is a string.
6925 __ movq(rax, Operand(rsp, kSubjectOffset));
6926 __ JumpIfSmi(rax, &runtime);
6927 Condition is_string = masm->IsObjectStringType(rax, rbx, rbx);
6928 __ j(NegateCondition(is_string), &runtime);
6929 // Get the length of the string to rbx.
6930 __ movl(rbx, FieldOperand(rax, String::kLengthOffset));
6931
6932 // rbx: Length of subject string
6933 // rcx: RegExp data (FixedArray)
6934 // rdx: Number of capture registers
6935 // Check that the third argument is a positive smi less than the string
6936 // length. A negative value will be greater (usigned comparison).
6937 __ movq(rax, Operand(rsp, kPreviousIndexOffset));
6938 __ SmiToInteger32(rax, rax);
6939 __ cmpl(rax, rbx);
6940 __ j(above, &runtime);
6941
6942 // rcx: RegExp data (FixedArray)
6943 // rdx: Number of capture registers
6944 // Check that the fourth object is a JSArray object.
6945 __ movq(rax, Operand(rsp, kLastMatchInfoOffset));
6946 __ JumpIfSmi(rax, &runtime);
6947 __ CmpObjectType(rax, JS_ARRAY_TYPE, kScratchRegister);
6948 __ j(not_equal, &runtime);
6949 // Check that the JSArray is in fast case.
6950 __ movq(rbx, FieldOperand(rax, JSArray::kElementsOffset));
6951 __ movq(rax, FieldOperand(rbx, HeapObject::kMapOffset));
6952 __ Cmp(rax, Factory::fixed_array_map());
6953 __ j(not_equal, &runtime);
6954 // Check that the last match info has space for the capture registers and the
6955 // additional information. Ensure no overflow in add.
6956 ASSERT(FixedArray::kMaxLength < kMaxInt - FixedArray::kLengthOffset);
6957 __ movl(rax, FieldOperand(rbx, FixedArray::kLengthOffset));
6958 __ addl(rdx, Immediate(RegExpImpl::kLastMatchOverhead));
6959 __ cmpl(rdx, rax);
6960 __ j(greater, &runtime);
6961
6962 // ecx: RegExp data (FixedArray)
6963 // Check the representation and encoding of the subject string.
6964 Label seq_string, seq_two_byte_string, check_code;
6965 const int kStringRepresentationEncodingMask =
6966 kIsNotStringMask | kStringRepresentationMask | kStringEncodingMask;
6967 __ movq(rax, Operand(rsp, kSubjectOffset));
6968 __ movq(rbx, FieldOperand(rax, HeapObject::kMapOffset));
6969 __ movzxbl(rbx, FieldOperand(rbx, Map::kInstanceTypeOffset));
6970 __ andb(rbx, Immediate(kStringRepresentationEncodingMask));
6971 // First check for sequential string.
6972 ASSERT_EQ(0, kStringTag);
6973 ASSERT_EQ(0, kSeqStringTag);
6974 __ testb(rbx, Immediate(kIsNotStringMask | kStringRepresentationMask));
6975 __ j(zero, &seq_string);
6976
6977 // Check for flat cons string.
6978 // A flat cons string is a cons string where the second part is the empty
6979 // string. In that case the subject string is just the first part of the cons
6980 // string. Also in this case the first part of the cons string is known to be
6981 // a sequential string or an external string.
6982 __ movl(rdx, rbx);
6983 __ andb(rdx, Immediate(kStringRepresentationMask));
6984 __ cmpb(rdx, Immediate(kConsStringTag));
6985 __ j(not_equal, &runtime);
6986 __ movq(rdx, FieldOperand(rax, ConsString::kSecondOffset));
6987 __ Cmp(rdx, Factory::empty_string());
6988 __ j(not_equal, &runtime);
6989 __ movq(rax, FieldOperand(rax, ConsString::kFirstOffset));
6990 __ movq(rbx, FieldOperand(rax, HeapObject::kMapOffset));
6991 __ movzxbl(rbx, FieldOperand(rbx, Map::kInstanceTypeOffset));
6992 ASSERT_EQ(0, kSeqStringTag);
6993 __ testb(rbx, Immediate(kStringRepresentationMask));
6994 __ j(not_zero, &runtime);
6995 __ andb(rbx, Immediate(kStringRepresentationEncodingMask));
6996
6997 __ bind(&seq_string);
6998 // rax: subject string (sequential either ascii to two byte)
6999 // rbx: suject string type & kStringRepresentationEncodingMask
7000 // rcx: RegExp data (FixedArray)
7001 // Check that the irregexp code has been generated for an ascii string. If
7002 // it has, the field contains a code object otherwise it contains the hole.
7003 __ cmpb(rbx, Immediate(kStringTag | kSeqStringTag | kTwoByteStringTag));
7004 __ j(equal, &seq_two_byte_string);
7005 if (FLAG_debug_code) {
7006 __ cmpb(rbx, Immediate(kStringTag | kSeqStringTag | kAsciiStringTag));
7007 __ Check(equal, "Expected sequential ascii string");
7008 }
7009 __ movq(r12, FieldOperand(rcx, JSRegExp::kDataAsciiCodeOffset));
7010 __ Set(rdi, 1); // Type is ascii.
7011 __ jmp(&check_code);
7012
7013 __ bind(&seq_two_byte_string);
7014 // rax: subject string
7015 // rcx: RegExp data (FixedArray)
7016 __ movq(r12, FieldOperand(rcx, JSRegExp::kDataUC16CodeOffset));
7017 __ Set(rdi, 0); // Type is two byte.
7018
7019 __ bind(&check_code);
7020 // Check that the irregexp code has been generated for the actual string
7021 // encoding. If it has, the field contains a code object otherwise it contains
7022 // the hole.
7023 __ CmpObjectType(r12, CODE_TYPE, kScratchRegister);
7024 __ j(not_equal, &runtime);
7025
7026 // rax: subject string
7027 // rdi: encoding of subject string (1 if ascii, 0 if two_byte);
7028 // r12: code
7029 // Load used arguments before starting to push arguments for call to native
7030 // RegExp code to avoid handling changing stack height.
7031 __ movq(rbx, Operand(rsp, kPreviousIndexOffset));
7032 __ SmiToInteger64(rbx, rbx); // Previous index from smi.
7033
7034 // rax: subject string
7035 // rbx: previous index
7036 // rdi: encoding of subject string (1 if ascii 0 if two_byte);
7037 // r12: code
7038 // All checks done. Now push arguments for native regexp code.
7039 __ IncrementCounter(&Counters::regexp_entry_native, 1);
7040
7041 // rsi is caller save on Windows and used to pass parameter on Linux.
7042 __ push(rsi);
7043
7044 static const int kRegExpExecuteArguments = 7;
7045 __ PrepareCallCFunction(kRegExpExecuteArguments);
7046 int argument_slots_on_stack =
7047 masm->ArgumentStackSlotsForCFunctionCall(kRegExpExecuteArguments);
7048
7049 // Argument 7: Indicate that this is a direct call from JavaScript.
7050 __ movq(Operand(rsp, (argument_slots_on_stack - 1) * kPointerSize),
7051 Immediate(1));
7052
7053 // Argument 6: Start (high end) of backtracking stack memory area.
7054 __ movq(kScratchRegister, address_of_regexp_stack_memory_address);
7055 __ movq(r9, Operand(kScratchRegister, 0));
7056 __ movq(kScratchRegister, address_of_regexp_stack_memory_size);
7057 __ addq(r9, Operand(kScratchRegister, 0));
7058 // Argument 6 passed in r9 on Linux and on the stack on Windows.
7059#ifdef _WIN64
7060 __ movq(Operand(rsp, (argument_slots_on_stack - 2) * kPointerSize), r9);
7061#endif
7062
7063 // Argument 5: static offsets vector buffer.
7064 __ movq(r8, ExternalReference::address_of_static_offsets_vector());
7065 // Argument 5 passed in r8 on Linux and on the stack on Windows.
7066#ifdef _WIN64
7067 __ movq(Operand(rsp, (argument_slots_on_stack - 3) * kPointerSize), r8);
7068#endif
7069
7070 // First four arguments are passed in registers on both Linux and Windows.
7071#ifdef _WIN64
7072 Register arg4 = r9;
7073 Register arg3 = r8;
7074 Register arg2 = rdx;
7075 Register arg1 = rcx;
7076#else
7077 Register arg4 = rcx;
7078 Register arg3 = rdx;
7079 Register arg2 = rsi;
7080 Register arg1 = rdi;
7081#endif
7082
7083 // Keep track on aliasing between argX defined above and the registers used.
7084 // rax: subject string
7085 // rbx: previous index
7086 // rdi: encoding of subject string (1 if ascii 0 if two_byte);
7087 // r12: code
7088
7089 // Argument 4: End of string data
7090 // Argument 3: Start of string data
7091 Label setup_two_byte, setup_rest;
7092 __ testb(rdi, rdi);
7093 __ movl(rdi, FieldOperand(rax, String::kLengthOffset));
7094 __ j(zero, &setup_two_byte);
7095 __ lea(arg4, FieldOperand(rax, rdi, times_1, SeqAsciiString::kHeaderSize));
7096 __ lea(arg3, FieldOperand(rax, rbx, times_1, SeqAsciiString::kHeaderSize));
7097 __ jmp(&setup_rest);
7098 __ bind(&setup_two_byte);
7099 __ lea(arg4, FieldOperand(rax, rdi, times_2, SeqTwoByteString::kHeaderSize));
7100 __ lea(arg3, FieldOperand(rax, rbx, times_2, SeqTwoByteString::kHeaderSize));
7101
7102 __ bind(&setup_rest);
7103 // Argument 2: Previous index.
7104 __ movq(arg2, rbx);
7105
7106 // Argument 1: Subject string.
7107 __ movq(arg1, rax);
7108
7109 // Locate the code entry and call it.
7110 __ addq(r12, Immediate(Code::kHeaderSize - kHeapObjectTag));
7111 __ CallCFunction(r12, kRegExpExecuteArguments);
7112
7113 // rsi is caller save, as it is used to pass parameter.
7114 __ pop(rsi);
7115
7116 // Check the result.
7117 Label success;
7118 __ cmpq(rax, Immediate(NativeRegExpMacroAssembler::SUCCESS));
7119 __ j(equal, &success);
7120 Label failure;
7121 __ cmpq(rax, Immediate(NativeRegExpMacroAssembler::FAILURE));
7122 __ j(equal, &failure);
7123 __ cmpq(rax, Immediate(NativeRegExpMacroAssembler::EXCEPTION));
7124 // If not exception it can only be retry. Handle that in the runtime system.
7125 __ j(not_equal, &runtime);
7126 // Result must now be exception. If there is no pending exception already a
7127 // stack overflow (on the backtrack stack) was detected in RegExp code but
7128 // haven't created the exception yet. Handle that in the runtime system.
7129 // TODO(592) Rerunning the RegExp to get the stack overflow exception.
7130 ExternalReference pending_exception_address(Top::k_pending_exception_address);
7131 __ movq(kScratchRegister, pending_exception_address);
7132 __ Cmp(kScratchRegister, Factory::the_hole_value());
7133 __ j(equal, &runtime);
7134 __ bind(&failure);
7135 // For failure and exception return null.
7136 __ Move(rax, Factory::null_value());
7137 __ ret(4 * kPointerSize);
7138
7139 // Load RegExp data.
7140 __ bind(&success);
7141 __ movq(rax, Operand(rsp, kJSRegExpOffset));
7142 __ movq(rcx, FieldOperand(rax, JSRegExp::kDataOffset));
7143 __ movq(rdx, FieldOperand(rcx, JSRegExp::kIrregexpCaptureCountOffset));
7144 // Calculate number of capture registers (number_of_captures + 1) * 2.
7145 __ PositiveSmiTimesPowerOfTwoToInteger64(rdx, rdx, 1);
7146 __ addq(rdx, Immediate(2)); // rdx was number_of_captures * 2.
7147
7148 // rdx: Number of capture registers
7149 // Load last_match_info which is still known to be a fast case JSArray.
7150 __ movq(rax, Operand(rsp, kLastMatchInfoOffset));
7151 __ movq(rbx, FieldOperand(rax, JSArray::kElementsOffset));
7152
7153 // rbx: last_match_info backing store (FixedArray)
7154 // rdx: number of capture registers
7155 // Store the capture count.
7156 __ Integer32ToSmi(kScratchRegister, rdx);
7157 __ movq(FieldOperand(rbx, RegExpImpl::kLastCaptureCountOffset),
7158 kScratchRegister);
7159 // Store last subject and last input.
7160 __ movq(rax, Operand(rsp, kSubjectOffset));
7161 __ movq(FieldOperand(rbx, RegExpImpl::kLastSubjectOffset), rax);
7162 __ movq(rcx, rbx);
7163 __ RecordWrite(rcx, RegExpImpl::kLastSubjectOffset, rax, rdi);
7164 __ movq(rax, Operand(rsp, kSubjectOffset));
7165 __ movq(FieldOperand(rbx, RegExpImpl::kLastInputOffset), rax);
7166 __ movq(rcx, rbx);
7167 __ RecordWrite(rcx, RegExpImpl::kLastInputOffset, rax, rdi);
7168
7169 // Get the static offsets vector filled by the native regexp code.
7170 __ movq(rcx, ExternalReference::address_of_static_offsets_vector());
7171
7172 // rbx: last_match_info backing store (FixedArray)
7173 // rcx: offsets vector
7174 // rdx: number of capture registers
7175 Label next_capture, done;
7176 __ movq(rax, Operand(rsp, kPreviousIndexOffset));
7177 // Capture register counter starts from number of capture registers and
7178 // counts down until wraping after zero.
7179 __ bind(&next_capture);
7180 __ subq(rdx, Immediate(1));
7181 __ j(negative, &done);
7182 // Read the value from the static offsets vector buffer and make it a smi.
7183 __ movl(rdi, Operand(rcx, rdx, times_int_size, 0));
7184 __ Integer32ToSmi(rdi, rdi, &runtime);
7185 // Add previous index (from its stack slot) if value is not negative.
7186 Label capture_negative;
7187 // Negative flag set by smi convertion above.
7188 __ j(negative, &capture_negative);
7189 __ SmiAdd(rdi, rdi, rax, &runtime); // Add previous index.
7190 __ bind(&capture_negative);
7191 // Store the smi value in the last match info.
7192 __ movq(FieldOperand(rbx,
7193 rdx,
7194 times_pointer_size,
7195 RegExpImpl::kFirstCaptureOffset),
7196 rdi);
7197 __ jmp(&next_capture);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007198 __ bind(&done);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007199
7200 // Return last match info.
7201 __ movq(rax, Operand(rsp, kLastMatchInfoOffset));
7202 __ ret(4 * kPointerSize);
7203
7204 // Do the runtime call to execute the regexp.
7205 __ bind(&runtime);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00007206 __ TailCallRuntime(Runtime::kRegExpExec, 4, 1);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007207#endif // V8_NATIVE_REGEXP
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007208}
7209
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007210
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007211void NumberToStringStub::GenerateLookupNumberStringCache(MacroAssembler* masm,
7212 Register object,
7213 Register result,
7214 Register scratch1,
7215 Register scratch2,
7216 bool object_is_smi,
7217 Label* not_found) {
7218 // Currently only lookup for smis. Check for smi if object is not known to be
7219 // a smi.
7220 if (!object_is_smi) {
7221 __ JumpIfNotSmi(object, not_found);
7222 }
7223
7224 // Use of registers. Register result is used as a temporary.
7225 Register number_string_cache = result;
7226 Register mask = scratch1;
7227 Register scratch = scratch2;
7228
7229 // Load the number string cache.
7230 __ LoadRoot(number_string_cache, Heap::kNumberStringCacheRootIndex);
7231
7232 // Make the hash mask from the length of the number string cache. It
7233 // contains two elements (number and string) for each cache entry.
7234 __ movl(mask, FieldOperand(number_string_cache, FixedArray::kLengthOffset));
7235 __ shrl(mask, Immediate(1)); // Divide length by two (length is not a smi).
7236 __ subl(mask, Immediate(1)); // Make mask.
7237
7238 // Calculate the entry in the number string cache. The hash value in the
7239 // number string cache for smis is just the smi value.
7240 __ movq(scratch, object);
7241 __ SmiToInteger32(scratch, scratch);
7242 __ andl(scratch, mask);
7243
7244 // Each entry in string cache consists of two pointer sized fields,
7245 // but times_twice_pointer_size (multiplication by 16) scale factor
7246 // is not supported by addrmode on x64 platform.
7247 // So we have to premultiply entry index before lookup
7248 __ shl(scratch, Immediate(kPointerSizeLog2 + 1));
7249 // Check if the entry is the smi we are looking for.
7250 __ cmpq(object,
7251 FieldOperand(number_string_cache,
7252 scratch,
7253 times_1,
7254 FixedArray::kHeaderSize));
7255 __ j(not_equal, not_found);
7256
7257 // Get the result from the cache.
7258 __ movq(result,
7259 FieldOperand(number_string_cache,
7260 scratch,
7261 times_1,
7262 FixedArray::kHeaderSize + kPointerSize));
7263 __ IncrementCounter(&Counters::number_to_string_native, 1);
7264}
7265
7266
7267void NumberToStringStub::Generate(MacroAssembler* masm) {
7268 Label runtime;
7269
7270 __ movq(rbx, Operand(rsp, kPointerSize));
7271
7272 // Generate code to lookup number in the number string cache.
7273 GenerateLookupNumberStringCache(masm, rbx, rax, r8, r9, false, &runtime);
7274 __ ret(1 * kPointerSize);
7275
7276 __ bind(&runtime);
7277 // Handle number to string in the runtime system if not found in the cache.
7278 __ TailCallRuntime(Runtime::kNumberToString, 1, 1);
7279}
7280
7281
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007282void CompareStub::Generate(MacroAssembler* masm) {
ager@chromium.org3e875802009-06-29 08:26:34 +00007283 Label call_builtin, done;
7284
7285 // NOTICE! This code is only reached after a smi-fast-case check, so
7286 // it is certain that at least one operand isn't a smi.
7287
7288 if (cc_ == equal) { // Both strict and non-strict.
7289 Label slow; // Fallthrough label.
7290 // Equality is almost reflexive (everything but NaN), so start by testing
7291 // for "identity and not NaN".
7292 {
7293 Label not_identical;
7294 __ cmpq(rax, rdx);
7295 __ j(not_equal, &not_identical);
7296 // Test for NaN. Sadly, we can't just compare to Factory::nan_value(),
7297 // so we do the second best thing - test it ourselves.
7298
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007299 if (never_nan_nan_) {
7300 __ xor_(rax, rax);
7301 __ ret(0);
7302 } else {
7303 Label return_equal;
7304 Label heap_number;
7305 // If it's not a heap number, then return equal.
7306 __ Cmp(FieldOperand(rdx, HeapObject::kMapOffset),
7307 Factory::heap_number_map());
7308 __ j(equal, &heap_number);
7309 __ bind(&return_equal);
7310 __ xor_(rax, rax);
7311 __ ret(0);
ager@chromium.org3e875802009-06-29 08:26:34 +00007312
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007313 __ bind(&heap_number);
7314 // It is a heap number, so return non-equal if it's NaN and equal if
7315 // it's not NaN.
7316 // The representation of NaN values has all exponent bits (52..62) set,
7317 // and not all mantissa bits (0..51) clear.
7318 // We only allow QNaNs, which have bit 51 set (which also rules out
7319 // the value being Infinity).
ager@chromium.org3811b432009-10-28 14:53:37 +00007320
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007321 // Value is a QNaN if value & kQuietNaNMask == kQuietNaNMask, i.e.,
7322 // all bits in the mask are set. We only need to check the word
7323 // that contains the exponent and high bit of the mantissa.
7324 ASSERT_NE(0, (kQuietNaNHighBitsMask << 1) & 0x80000000u);
7325 __ movl(rdx, FieldOperand(rdx, HeapNumber::kExponentOffset));
7326 __ xorl(rax, rax);
7327 __ addl(rdx, rdx); // Shift value and mask so mask applies to top bits.
7328 __ cmpl(rdx, Immediate(kQuietNaNHighBitsMask << 1));
7329 __ setcc(above_equal, rax);
7330 __ ret(0);
7331 }
ager@chromium.org3e875802009-06-29 08:26:34 +00007332
7333 __ bind(&not_identical);
7334 }
7335
7336 // If we're doing a strict equality comparison, we don't have to do
7337 // type conversion, so we generate code to do fast comparison for objects
7338 // and oddballs. Non-smi numbers and strings still go through the usual
7339 // slow-case code.
7340 if (strict_) {
7341 // If either is a Smi (we know that not both are), then they can only
7342 // be equal if the other is a HeapNumber. If so, use the slow case.
7343 {
7344 Label not_smis;
ager@chromium.org4af710e2009-09-15 12:20:11 +00007345 __ SelectNonSmi(rbx, rax, rdx, &not_smis);
ager@chromium.org3e875802009-06-29 08:26:34 +00007346
7347 // Check if the non-smi operand is a heap number.
7348 __ Cmp(FieldOperand(rbx, HeapObject::kMapOffset),
7349 Factory::heap_number_map());
7350 // If heap number, handle it in the slow case.
7351 __ j(equal, &slow);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00007352 // Return non-equal. ebx (the lower half of rbx) is not zero.
ager@chromium.org3e875802009-06-29 08:26:34 +00007353 __ movq(rax, rbx);
7354 __ ret(0);
7355
7356 __ bind(&not_smis);
7357 }
7358
7359 // If either operand is a JSObject or an oddball value, then they are not
7360 // equal since their pointers are different
7361 // There is no test for undetectability in strict equality.
7362
7363 // If the first object is a JS object, we have done pointer comparison.
7364 ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
7365 Label first_non_object;
7366 __ CmpObjectType(rax, FIRST_JS_OBJECT_TYPE, rcx);
7367 __ j(below, &first_non_object);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00007368 // Return non-zero (eax (not rax) is not zero)
ager@chromium.org3e875802009-06-29 08:26:34 +00007369 Label return_not_equal;
7370 ASSERT(kHeapObjectTag != 0);
7371 __ bind(&return_not_equal);
7372 __ ret(0);
7373
7374 __ bind(&first_non_object);
7375 // Check for oddballs: true, false, null, undefined.
7376 __ CmpInstanceType(rcx, ODDBALL_TYPE);
7377 __ j(equal, &return_not_equal);
7378
7379 __ CmpObjectType(rdx, FIRST_JS_OBJECT_TYPE, rcx);
7380 __ j(above_equal, &return_not_equal);
7381
7382 // Check for oddballs: true, false, null, undefined.
7383 __ CmpInstanceType(rcx, ODDBALL_TYPE);
7384 __ j(equal, &return_not_equal);
7385
7386 // Fall through to the general case.
7387 }
7388 __ bind(&slow);
7389 }
7390
7391 // Push arguments below the return address to prepare jump to builtin.
7392 __ pop(rcx);
7393 __ push(rax);
7394 __ push(rdx);
7395 __ push(rcx);
7396
7397 // Inlined floating point compare.
7398 // Call builtin if operands are not floating point or smi.
7399 Label check_for_symbols;
7400 // Push arguments on stack, for helper functions.
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00007401 FloatingPointHelper::CheckNumberOperands(masm, &check_for_symbols);
ager@chromium.org3e875802009-06-29 08:26:34 +00007402 FloatingPointHelper::LoadFloatOperands(masm, rax, rdx);
7403 __ FCmp();
7404
7405 // Jump to builtin for NaN.
7406 __ j(parity_even, &call_builtin);
7407
7408 // TODO(1243847): Use cmov below once CpuFeatures are properly hooked up.
7409 Label below_lbl, above_lbl;
7410 // use rdx, rax to convert unsigned to signed comparison
7411 __ j(below, &below_lbl);
7412 __ j(above, &above_lbl);
7413
7414 __ xor_(rax, rax); // equal
7415 __ ret(2 * kPointerSize);
7416
7417 __ bind(&below_lbl);
7418 __ movq(rax, Immediate(-1));
7419 __ ret(2 * kPointerSize);
7420
7421 __ bind(&above_lbl);
7422 __ movq(rax, Immediate(1));
7423 __ ret(2 * kPointerSize); // rax, rdx were pushed
7424
7425 // Fast negative check for symbol-to-symbol equality.
7426 __ bind(&check_for_symbols);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007427 Label check_for_strings;
ager@chromium.org3e875802009-06-29 08:26:34 +00007428 if (cc_ == equal) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007429 BranchIfNonSymbol(masm, &check_for_strings, rax, kScratchRegister);
7430 BranchIfNonSymbol(masm, &check_for_strings, rdx, kScratchRegister);
ager@chromium.org3e875802009-06-29 08:26:34 +00007431
7432 // We've already checked for object identity, so if both operands
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00007433 // are symbols they aren't equal. Register eax (not rax) already holds a
ager@chromium.org3e875802009-06-29 08:26:34 +00007434 // non-zero value, which indicates not equal, so just return.
7435 __ ret(2 * kPointerSize);
7436 }
7437
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007438 __ bind(&check_for_strings);
7439
7440 __ JumpIfNotBothSequentialAsciiStrings(rdx, rax, rcx, rbx, &call_builtin);
7441
7442 // Inline comparison of ascii strings.
7443 StringCompareStub::GenerateCompareFlatAsciiStrings(masm,
7444 rdx,
7445 rax,
7446 rcx,
7447 rbx,
7448 rdi,
7449 r8);
7450
7451#ifdef DEBUG
7452 __ Abort("Unexpected fall-through from string comparison");
7453#endif
7454
ager@chromium.org3e875802009-06-29 08:26:34 +00007455 __ bind(&call_builtin);
7456 // must swap argument order
7457 __ pop(rcx);
7458 __ pop(rdx);
7459 __ pop(rax);
7460 __ push(rdx);
7461 __ push(rax);
7462
7463 // Figure out which native to call and setup the arguments.
7464 Builtins::JavaScript builtin;
7465 if (cc_ == equal) {
7466 builtin = strict_ ? Builtins::STRICT_EQUALS : Builtins::EQUALS;
7467 } else {
7468 builtin = Builtins::COMPARE;
7469 int ncr; // NaN compare result
7470 if (cc_ == less || cc_ == less_equal) {
7471 ncr = GREATER;
7472 } else {
7473 ASSERT(cc_ == greater || cc_ == greater_equal); // remaining cases
7474 ncr = LESS;
7475 }
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00007476 __ Push(Smi::FromInt(ncr));
ager@chromium.org3e875802009-06-29 08:26:34 +00007477 }
7478
7479 // Restore return address on the stack.
7480 __ push(rcx);
7481
7482 // Call the native; it returns -1 (less), 0 (equal), or 1 (greater)
7483 // tagged as a small integer.
7484 __ InvokeBuiltin(builtin, JUMP_FUNCTION);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007485}
7486
7487
ager@chromium.org3e875802009-06-29 08:26:34 +00007488void CompareStub::BranchIfNonSymbol(MacroAssembler* masm,
7489 Label* label,
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007490 Register object,
7491 Register scratch) {
ager@chromium.org4af710e2009-09-15 12:20:11 +00007492 __ JumpIfSmi(object, label);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007493 __ movq(scratch, FieldOperand(object, HeapObject::kMapOffset));
7494 __ movzxbq(scratch,
7495 FieldOperand(scratch, Map::kInstanceTypeOffset));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007496 // Ensure that no non-strings have the symbol bit set.
7497 ASSERT(kNotStringTag + kIsSymbolMask > LAST_TYPE);
7498 ASSERT(kSymbolTag != 0);
7499 __ testb(scratch, Immediate(kIsSymbolMask));
7500 __ j(zero, label);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007501}
7502
7503
7504// Call the function just below TOS on the stack with the given
7505// arguments. The receiver is the TOS.
7506void CodeGenerator::CallWithArguments(ZoneList<Expression*>* args,
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007507 CallFunctionFlags flags,
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007508 int position) {
7509 // Push the arguments ("left-to-right") on the stack.
7510 int arg_count = args->length();
7511 for (int i = 0; i < arg_count; i++) {
7512 Load(args->at(i));
7513 }
7514
7515 // Record the position for debugging purposes.
7516 CodeForSourcePosition(position);
7517
7518 // Use the shared code stub to call the function.
7519 InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007520 CallFunctionStub call_function(arg_count, in_loop, flags);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007521 Result answer = frame_->CallStub(&call_function, arg_count + 1);
7522 // Restore context and replace function on the stack with the
7523 // result of the stub invocation.
7524 frame_->RestoreContextRegister();
7525 frame_->SetElementAt(0, &answer);
7526}
7527
7528
7529void InstanceofStub::Generate(MacroAssembler* masm) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007530 // Implements "value instanceof function" operator.
7531 // Expected input state:
7532 // rsp[0] : return address
7533 // rsp[1] : function pointer
7534 // rsp[2] : value
7535
7536 // Get the object - go slow case if it's a smi.
7537 Label slow;
7538 __ movq(rax, Operand(rsp, 2 * kPointerSize));
ager@chromium.org4af710e2009-09-15 12:20:11 +00007539 __ JumpIfSmi(rax, &slow);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007540
7541 // Check that the left hand is a JS object. Leave its map in rax.
7542 __ CmpObjectType(rax, FIRST_JS_OBJECT_TYPE, rax);
7543 __ j(below, &slow);
7544 __ CmpInstanceType(rax, LAST_JS_OBJECT_TYPE);
7545 __ j(above, &slow);
7546
7547 // Get the prototype of the function.
7548 __ movq(rdx, Operand(rsp, 1 * kPointerSize));
7549 __ TryGetFunctionPrototype(rdx, rbx, &slow);
7550
7551 // Check that the function prototype is a JS object.
ager@chromium.org4af710e2009-09-15 12:20:11 +00007552 __ JumpIfSmi(rbx, &slow);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007553 __ CmpObjectType(rbx, FIRST_JS_OBJECT_TYPE, kScratchRegister);
7554 __ j(below, &slow);
7555 __ CmpInstanceType(kScratchRegister, LAST_JS_OBJECT_TYPE);
7556 __ j(above, &slow);
7557
7558 // Register mapping: rax is object map and rbx is function prototype.
7559 __ movq(rcx, FieldOperand(rax, Map::kPrototypeOffset));
7560
7561 // Loop through the prototype chain looking for the function prototype.
7562 Label loop, is_instance, is_not_instance;
ager@chromium.org18ad94b2009-09-02 08:22:29 +00007563 __ LoadRoot(kScratchRegister, Heap::kNullValueRootIndex);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007564 __ bind(&loop);
7565 __ cmpq(rcx, rbx);
7566 __ j(equal, &is_instance);
7567 __ cmpq(rcx, kScratchRegister);
7568 __ j(equal, &is_not_instance);
7569 __ movq(rcx, FieldOperand(rcx, HeapObject::kMapOffset));
7570 __ movq(rcx, FieldOperand(rcx, Map::kPrototypeOffset));
7571 __ jmp(&loop);
7572
7573 __ bind(&is_instance);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007574 __ xorl(rax, rax);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007575 __ ret(2 * kPointerSize);
7576
7577 __ bind(&is_not_instance);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007578 __ movl(rax, Immediate(1));
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007579 __ ret(2 * kPointerSize);
7580
7581 // Slow-case: Go through the JavaScript implementation.
7582 __ bind(&slow);
7583 __ InvokeBuiltin(Builtins::INSTANCE_OF, JUMP_FUNCTION);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007584}
7585
7586
7587void ArgumentsAccessStub::GenerateNewObject(MacroAssembler* masm) {
ager@chromium.org5c838252010-02-19 08:53:10 +00007588 // rsp[0] : return address
7589 // rsp[8] : number of parameters
7590 // rsp[16] : receiver displacement
7591 // rsp[24] : function
7592
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007593 // The displacement is used for skipping the return address and the
7594 // frame pointer on the stack. It is the offset of the last
7595 // parameter (if any) relative to the frame pointer.
7596 static const int kDisplacement = 2 * kPointerSize;
7597
7598 // Check if the calling frame is an arguments adaptor frame.
ager@chromium.org5c838252010-02-19 08:53:10 +00007599 Label adaptor_frame, try_allocate, runtime;
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007600 __ movq(rdx, Operand(rbp, StandardFrameConstants::kCallerFPOffset));
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00007601 __ SmiCompare(Operand(rdx, StandardFrameConstants::kContextOffset),
7602 Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR));
ager@chromium.org5c838252010-02-19 08:53:10 +00007603 __ j(equal, &adaptor_frame);
7604
7605 // Get the length from the frame.
7606 __ movq(rcx, Operand(rsp, 1 * kPointerSize));
7607 __ jmp(&try_allocate);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007608
7609 // Patch the arguments.length and the parameters pointer.
ager@chromium.org5c838252010-02-19 08:53:10 +00007610 __ bind(&adaptor_frame);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007611 __ movq(rcx, Operand(rdx, ArgumentsAdaptorFrameConstants::kLengthOffset));
7612 __ movq(Operand(rsp, 1 * kPointerSize), rcx);
ager@chromium.org5c838252010-02-19 08:53:10 +00007613 // Do not clobber the length index for the indexing operation since
7614 // it is used compute the size for allocation later.
7615 SmiIndex index = masm->SmiToIndex(rbx, rcx, kPointerSizeLog2);
ager@chromium.org4af710e2009-09-15 12:20:11 +00007616 __ lea(rdx, Operand(rdx, index.reg, index.scale, kDisplacement));
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007617 __ movq(Operand(rsp, 2 * kPointerSize), rdx);
7618
ager@chromium.org5c838252010-02-19 08:53:10 +00007619 // Try the new space allocation. Start out with computing the size of
7620 // the arguments object and the elements array.
7621 Label add_arguments_object;
7622 __ bind(&try_allocate);
7623 __ testq(rcx, rcx);
7624 __ j(zero, &add_arguments_object);
7625 index = masm->SmiToIndex(rcx, rcx, kPointerSizeLog2);
7626 __ lea(rcx, Operand(index.reg, index.scale, FixedArray::kHeaderSize));
7627 __ bind(&add_arguments_object);
7628 __ addq(rcx, Immediate(Heap::kArgumentsObjectSize));
7629
7630 // Do the allocation of both objects in one go.
7631 __ AllocateInNewSpace(rcx, rax, rdx, rbx, &runtime, TAG_OBJECT);
7632
7633 // Get the arguments boilerplate from the current (global) context.
7634 int offset = Context::SlotOffset(Context::ARGUMENTS_BOILERPLATE_INDEX);
7635 __ movq(rdi, Operand(rsi, Context::SlotOffset(Context::GLOBAL_INDEX)));
7636 __ movq(rdi, FieldOperand(rdi, GlobalObject::kGlobalContextOffset));
7637 __ movq(rdi, Operand(rdi, offset));
7638
7639 // Copy the JS object part.
7640 for (int i = 0; i < JSObject::kHeaderSize; i += kPointerSize) {
7641 __ movq(kScratchRegister, FieldOperand(rdi, i));
7642 __ movq(FieldOperand(rax, i), kScratchRegister);
7643 }
7644
7645 // Setup the callee in-object property.
7646 ASSERT(Heap::arguments_callee_index == 0);
7647 __ movq(kScratchRegister, Operand(rsp, 3 * kPointerSize));
7648 __ movq(FieldOperand(rax, JSObject::kHeaderSize), kScratchRegister);
7649
7650 // Get the length (smi tagged) and set that as an in-object property too.
7651 ASSERT(Heap::arguments_length_index == 1);
7652 __ movq(rcx, Operand(rsp, 1 * kPointerSize));
7653 __ movq(FieldOperand(rax, JSObject::kHeaderSize + kPointerSize), rcx);
7654
7655 // If there are no actual arguments, we're done.
7656 Label done;
7657 __ testq(rcx, rcx);
7658 __ j(zero, &done);
7659
7660 // Get the parameters pointer from the stack and untag the length.
7661 __ movq(rdx, Operand(rsp, 2 * kPointerSize));
7662 __ SmiToInteger32(rcx, rcx);
7663
7664 // Setup the elements pointer in the allocated arguments object and
7665 // initialize the header in the elements fixed array.
7666 __ lea(rdi, Operand(rax, Heap::kArgumentsObjectSize));
7667 __ movq(FieldOperand(rax, JSObject::kElementsOffset), rdi);
7668 __ LoadRoot(kScratchRegister, Heap::kFixedArrayMapRootIndex);
7669 __ movq(FieldOperand(rdi, FixedArray::kMapOffset), kScratchRegister);
7670 __ movq(FieldOperand(rdi, FixedArray::kLengthOffset), rcx);
7671
7672 // Copy the fixed array slots.
7673 Label loop;
7674 __ bind(&loop);
7675 __ movq(kScratchRegister, Operand(rdx, -1 * kPointerSize)); // Skip receiver.
7676 __ movq(FieldOperand(rdi, FixedArray::kHeaderSize), kScratchRegister);
7677 __ addq(rdi, Immediate(kPointerSize));
7678 __ subq(rdx, Immediate(kPointerSize));
7679 __ decq(rcx);
7680 __ j(not_zero, &loop);
7681
7682 // Return and remove the on-stack parameters.
7683 __ bind(&done);
7684 __ ret(3 * kPointerSize);
7685
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007686 // Do the runtime call to allocate the arguments object.
7687 __ bind(&runtime);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00007688 __ TailCallRuntime(Runtime::kNewArgumentsFast, 3, 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007689}
7690
ager@chromium.org3e875802009-06-29 08:26:34 +00007691
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007692void ArgumentsAccessStub::GenerateReadElement(MacroAssembler* masm) {
7693 // The key is in rdx and the parameter count is in rax.
7694
7695 // The displacement is used for skipping the frame pointer on the
7696 // stack. It is the offset of the last parameter (if any) relative
7697 // to the frame pointer.
7698 static const int kDisplacement = 1 * kPointerSize;
7699
7700 // Check that the key is a smi.
7701 Label slow;
ager@chromium.org4af710e2009-09-15 12:20:11 +00007702 __ JumpIfNotSmi(rdx, &slow);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007703
7704 // Check if the calling frame is an arguments adaptor frame.
7705 Label adaptor;
7706 __ movq(rbx, Operand(rbp, StandardFrameConstants::kCallerFPOffset));
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00007707 __ SmiCompare(Operand(rbx, StandardFrameConstants::kContextOffset),
7708 Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR));
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007709 __ j(equal, &adaptor);
7710
7711 // Check index against formal parameters count limit passed in
7712 // through register rax. Use unsigned comparison to get negative
7713 // check for free.
7714 __ cmpq(rdx, rax);
7715 __ j(above_equal, &slow);
7716
7717 // Read the argument from the stack and return it.
ager@chromium.org4af710e2009-09-15 12:20:11 +00007718 SmiIndex index = masm->SmiToIndex(rax, rax, kPointerSizeLog2);
7719 __ lea(rbx, Operand(rbp, index.reg, index.scale, 0));
7720 index = masm->SmiToNegativeIndex(rdx, rdx, kPointerSizeLog2);
7721 __ movq(rax, Operand(rbx, index.reg, index.scale, kDisplacement));
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007722 __ Ret();
7723
7724 // Arguments adaptor case: Check index against actual arguments
7725 // limit found in the arguments adaptor frame. Use unsigned
7726 // comparison to get negative check for free.
7727 __ bind(&adaptor);
7728 __ movq(rcx, Operand(rbx, ArgumentsAdaptorFrameConstants::kLengthOffset));
7729 __ cmpq(rdx, rcx);
7730 __ j(above_equal, &slow);
7731
7732 // Read the argument from the stack and return it.
ager@chromium.org4af710e2009-09-15 12:20:11 +00007733 index = masm->SmiToIndex(rax, rcx, kPointerSizeLog2);
7734 __ lea(rbx, Operand(rbx, index.reg, index.scale, 0));
7735 index = masm->SmiToNegativeIndex(rdx, rdx, kPointerSizeLog2);
7736 __ movq(rax, Operand(rbx, index.reg, index.scale, kDisplacement));
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007737 __ Ret();
7738
7739 // Slow-case: Handle non-smi or out-of-bounds access to arguments
7740 // by calling the runtime system.
7741 __ bind(&slow);
7742 __ pop(rbx); // Return address.
7743 __ push(rdx);
7744 __ push(rbx);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00007745 __ TailCallRuntime(Runtime::kGetArgumentsProperty, 1, 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007746}
7747
7748
7749void ArgumentsAccessStub::GenerateReadLength(MacroAssembler* masm) {
7750 // Check if the calling frame is an arguments adaptor frame.
7751 Label adaptor;
7752 __ movq(rdx, Operand(rbp, StandardFrameConstants::kCallerFPOffset));
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00007753 __ SmiCompare(Operand(rdx, StandardFrameConstants::kContextOffset),
7754 Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR));
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007755
7756 // Arguments adaptor case: Read the arguments length from the
7757 // adaptor frame and return it.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007758 // Otherwise nothing to do: The number of formal parameters has already been
7759 // passed in register eax by calling function. Just return it.
7760 __ cmovq(equal, rax,
7761 Operand(rdx, ArgumentsAdaptorFrameConstants::kLengthOffset));
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007762 __ ret(0);
7763}
7764
7765
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007766void CEntryStub::GenerateThrowTOS(MacroAssembler* masm) {
ager@chromium.org3e875802009-06-29 08:26:34 +00007767 // Check that stack should contain next handler, frame pointer, state and
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007768 // return address in that order.
7769 ASSERT_EQ(StackHandlerConstants::kFPOffset + kPointerSize,
7770 StackHandlerConstants::kStateOffset);
7771 ASSERT_EQ(StackHandlerConstants::kStateOffset + kPointerSize,
7772 StackHandlerConstants::kPCOffset);
7773
7774 ExternalReference handler_address(Top::k_handler_address);
7775 __ movq(kScratchRegister, handler_address);
ager@chromium.org3e875802009-06-29 08:26:34 +00007776 __ movq(rsp, Operand(kScratchRegister, 0));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007777 // get next in chain
ager@chromium.org3e875802009-06-29 08:26:34 +00007778 __ pop(rcx);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007779 __ movq(Operand(kScratchRegister, 0), rcx);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007780 __ pop(rbp); // pop frame pointer
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007781 __ pop(rdx); // remove state
7782
7783 // Before returning we restore the context from the frame pointer if not NULL.
7784 // The frame pointer is NULL in the exception handler of a JS entry frame.
7785 __ xor_(rsi, rsi); // tentatively set context pointer to NULL
7786 Label skip;
7787 __ cmpq(rbp, Immediate(0));
7788 __ j(equal, &skip);
7789 __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
7790 __ bind(&skip);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007791 __ ret(0);
7792}
7793
7794
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007795void CEntryStub::GenerateCore(MacroAssembler* masm,
7796 Label* throw_normal_exception,
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00007797 Label* throw_termination_exception,
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007798 Label* throw_out_of_memory_exception,
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007799 bool do_gc,
7800 bool always_allocate_scope) {
7801 // rax: result parameter for PerformGC, if any.
7802 // rbx: pointer to C function (C callee-saved).
7803 // rbp: frame pointer (restored after C call).
7804 // rsp: stack pointer (restored after C call).
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007805 // r14: number of arguments including receiver (C callee-saved).
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007806 // r15: pointer to the first argument (C callee-saved).
7807 // This pointer is reused in LeaveExitFrame(), so it is stored in a
7808 // callee-saved register.
7809
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007810 // Simple results returned in rax (both AMD64 and Win64 calling conventions).
7811 // Complex results must be written to address passed as first argument.
7812 // AMD64 calling convention: a struct of two pointers in rax+rdx
7813
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007814 if (do_gc) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007815 // Pass failure code returned from last attempt as first argument to GC.
ager@chromium.org96c75b52009-08-26 09:13:16 +00007816#ifdef _WIN64
7817 __ movq(rcx, rax);
7818#else // ! defined(_WIN64)
7819 __ movq(rdi, rax);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007820#endif
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007821 __ movq(kScratchRegister,
7822 FUNCTION_ADDR(Runtime::PerformGC),
7823 RelocInfo::RUNTIME_ENTRY);
7824 __ call(kScratchRegister);
7825 }
7826
7827 ExternalReference scope_depth =
7828 ExternalReference::heap_always_allocate_scope_depth();
7829 if (always_allocate_scope) {
7830 __ movq(kScratchRegister, scope_depth);
7831 __ incl(Operand(kScratchRegister, 0));
7832 }
7833
7834 // Call C function.
ager@chromium.org96c75b52009-08-26 09:13:16 +00007835#ifdef _WIN64
7836 // Windows 64-bit ABI passes arguments in rcx, rdx, r8, r9
ager@chromium.org18ad94b2009-09-02 08:22:29 +00007837 // Store Arguments object on stack, below the 4 WIN64 ABI parameter slots.
7838 __ movq(Operand(rsp, 4 * kPointerSize), r14); // argc.
7839 __ movq(Operand(rsp, 5 * kPointerSize), r15); // argv.
ager@chromium.orga1645e22009-09-09 19:27:10 +00007840 if (result_size_ < 2) {
7841 // Pass a pointer to the Arguments object as the first argument.
7842 // Return result in single register (rax).
7843 __ lea(rcx, Operand(rsp, 4 * kPointerSize));
7844 } else {
7845 ASSERT_EQ(2, result_size_);
7846 // Pass a pointer to the result location as the first argument.
7847 __ lea(rcx, Operand(rsp, 6 * kPointerSize));
7848 // Pass a pointer to the Arguments object as the second argument.
7849 __ lea(rdx, Operand(rsp, 4 * kPointerSize));
7850 }
7851
ager@chromium.org96c75b52009-08-26 09:13:16 +00007852#else // ! defined(_WIN64)
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007853 // GCC passes arguments in rdi, rsi, rdx, rcx, r8, r9.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007854 __ movq(rdi, r14); // argc.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007855 __ movq(rsi, r15); // argv.
7856#endif
7857 __ call(rbx);
7858 // Result is in rax - do not destroy this register!
7859
7860 if (always_allocate_scope) {
7861 __ movq(kScratchRegister, scope_depth);
7862 __ decl(Operand(kScratchRegister, 0));
7863 }
7864
7865 // Check for failure result.
7866 Label failure_returned;
7867 ASSERT(((kFailureTag + 1) & kFailureTagMask) == 0);
ager@chromium.org3811b432009-10-28 14:53:37 +00007868#ifdef _WIN64
7869 // If return value is on the stack, pop it to registers.
7870 if (result_size_ > 1) {
7871 ASSERT_EQ(2, result_size_);
7872 // Read result values stored on stack. Result is stored
7873 // above the four argument mirror slots and the two
7874 // Arguments object slots.
7875 __ movq(rax, Operand(rsp, 6 * kPointerSize));
7876 __ movq(rdx, Operand(rsp, 7 * kPointerSize));
7877 }
7878#endif
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007879 __ lea(rcx, Operand(rax, 1));
7880 // Lower 2 bits of rcx are 0 iff rax has failure tag.
7881 __ testl(rcx, Immediate(kFailureTagMask));
7882 __ j(zero, &failure_returned);
7883
7884 // Exit the JavaScript to C++ exit frame.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007885 __ LeaveExitFrame(mode_, result_size_);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007886 __ ret(0);
7887
7888 // Handling of failure.
7889 __ bind(&failure_returned);
7890
7891 Label retry;
7892 // If the returned exception is RETRY_AFTER_GC continue at retry label
7893 ASSERT(Failure::RETRY_AFTER_GC == 0);
ager@chromium.org3e875802009-06-29 08:26:34 +00007894 __ testl(rax, Immediate(((1 << kFailureTypeTagSize) - 1) << kFailureTagSize));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007895 __ j(zero, &retry);
7896
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00007897 // Special handling of out of memory exceptions.
7898 __ movq(kScratchRegister, Failure::OutOfMemoryException(), RelocInfo::NONE);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007899 __ cmpq(rax, kScratchRegister);
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00007900 __ j(equal, throw_out_of_memory_exception);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007901
7902 // Retrieve the pending exception and clear the variable.
7903 ExternalReference pending_exception_address(Top::k_pending_exception_address);
7904 __ movq(kScratchRegister, pending_exception_address);
7905 __ movq(rax, Operand(kScratchRegister, 0));
7906 __ movq(rdx, ExternalReference::the_hole_value_location());
7907 __ movq(rdx, Operand(rdx, 0));
7908 __ movq(Operand(kScratchRegister, 0), rdx);
7909
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00007910 // Special handling of termination exceptions which are uncatchable
7911 // by javascript code.
ager@chromium.org18ad94b2009-09-02 08:22:29 +00007912 __ CompareRoot(rax, Heap::kTerminationExceptionRootIndex);
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00007913 __ j(equal, throw_termination_exception);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007914
7915 // Handle normal exception.
7916 __ jmp(throw_normal_exception);
7917
7918 // Retry.
7919 __ bind(&retry);
7920}
7921
7922
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00007923void CEntryStub::GenerateThrowUncatchable(MacroAssembler* masm,
7924 UncatchableExceptionType type) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007925 // Fetch top stack handler.
7926 ExternalReference handler_address(Top::k_handler_address);
7927 __ movq(kScratchRegister, handler_address);
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +00007928 __ movq(rsp, Operand(kScratchRegister, 0));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007929
7930 // Unwind the handlers until the ENTRY handler is found.
7931 Label loop, done;
7932 __ bind(&loop);
7933 // Load the type of the current stack handler.
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00007934 const int kStateOffset = StackHandlerConstants::kStateOffset;
7935 __ cmpq(Operand(rsp, kStateOffset), Immediate(StackHandler::ENTRY));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007936 __ j(equal, &done);
7937 // Fetch the next handler in the list.
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00007938 const int kNextOffset = StackHandlerConstants::kNextOffset;
7939 __ movq(rsp, Operand(rsp, kNextOffset));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007940 __ jmp(&loop);
7941 __ bind(&done);
7942
7943 // Set the top handler address to next handler past the current ENTRY handler.
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00007944 __ movq(kScratchRegister, handler_address);
7945 __ pop(Operand(kScratchRegister, 0));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007946
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00007947 if (type == OUT_OF_MEMORY) {
7948 // Set external caught exception to false.
7949 ExternalReference external_caught(Top::k_external_caught_exception_address);
7950 __ movq(rax, Immediate(false));
7951 __ store_rax(external_caught);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007952
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00007953 // Set pending exception and rax to out of memory exception.
7954 ExternalReference pending_exception(Top::k_pending_exception_address);
7955 __ movq(rax, Failure::OutOfMemoryException(), RelocInfo::NONE);
7956 __ store_rax(pending_exception);
7957 }
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007958
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00007959 // Clear the context pointer.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007960 __ xor_(rsi, rsi);
7961
7962 // Restore registers from handler.
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +00007963 ASSERT_EQ(StackHandlerConstants::kNextOffset + kPointerSize,
7964 StackHandlerConstants::kFPOffset);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007965 __ pop(rbp); // FP
7966 ASSERT_EQ(StackHandlerConstants::kFPOffset + kPointerSize,
7967 StackHandlerConstants::kStateOffset);
7968 __ pop(rdx); // State
7969
7970 ASSERT_EQ(StackHandlerConstants::kStateOffset + kPointerSize,
7971 StackHandlerConstants::kPCOffset);
7972 __ ret(0);
7973}
7974
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007975
ager@chromium.org3e875802009-06-29 08:26:34 +00007976void CallFunctionStub::Generate(MacroAssembler* masm) {
7977 Label slow;
7978
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007979 // If the receiver might be a value (string, number or boolean) check for this
7980 // and box it if it is.
7981 if (ReceiverMightBeValue()) {
7982 // Get the receiver from the stack.
7983 // +1 ~ return address
7984 Label receiver_is_value, receiver_is_js_object;
7985 __ movq(rax, Operand(rsp, (argc_ + 1) * kPointerSize));
7986
7987 // Check if receiver is a smi (which is a number value).
7988 __ JumpIfSmi(rax, &receiver_is_value);
7989
7990 // Check if the receiver is a valid JS object.
7991 __ CmpObjectType(rax, FIRST_JS_OBJECT_TYPE, rdi);
7992 __ j(above_equal, &receiver_is_js_object);
7993
7994 // Call the runtime to box the value.
7995 __ bind(&receiver_is_value);
7996 __ EnterInternalFrame();
7997 __ push(rax);
7998 __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION);
7999 __ LeaveInternalFrame();
8000 __ movq(Operand(rsp, (argc_ + 1) * kPointerSize), rax);
8001
8002 __ bind(&receiver_is_js_object);
8003 }
8004
ager@chromium.org3e875802009-06-29 08:26:34 +00008005 // Get the function to call from the stack.
8006 // +2 ~ receiver, return address
8007 __ movq(rdi, Operand(rsp, (argc_ + 2) * kPointerSize));
8008
8009 // Check that the function really is a JavaScript function.
ager@chromium.org4af710e2009-09-15 12:20:11 +00008010 __ JumpIfSmi(rdi, &slow);
ager@chromium.org3e875802009-06-29 08:26:34 +00008011 // Goto slow case if we do not have a function.
8012 __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rcx);
8013 __ j(not_equal, &slow);
8014
8015 // Fast-case: Just invoke the function.
8016 ParameterCount actual(argc_);
8017 __ InvokeFunction(rdi, actual, JUMP_FUNCTION);
8018
8019 // Slow-case: Non-function called.
8020 __ bind(&slow);
ager@chromium.org5c838252010-02-19 08:53:10 +00008021 // CALL_NON_FUNCTION expects the non-function callee as receiver (instead
8022 // of the original receiver from the call site).
8023 __ movq(Operand(rsp, (argc_ + 1) * kPointerSize), rdi);
ager@chromium.org3e875802009-06-29 08:26:34 +00008024 __ Set(rax, argc_);
8025 __ Set(rbx, 0);
8026 __ GetBuiltinEntry(rdx, Builtins::CALL_NON_FUNCTION);
8027 Handle<Code> adaptor(Builtins::builtin(Builtins::ArgumentsAdaptorTrampoline));
8028 __ Jump(adaptor, RelocInfo::CODE_TARGET);
8029}
8030
8031
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008032void CEntryStub::Generate(MacroAssembler* masm) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008033 // rax: number of arguments including receiver
8034 // rbx: pointer to C function (C callee-saved)
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008035 // rbp: frame pointer of calling JS frame (restored after C call)
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008036 // rsp: stack pointer (restored after C call)
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008037 // rsi: current context (restored)
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008038
8039 // NOTE: Invocations of builtins may return failure objects
8040 // instead of a proper result. The builtin entry handles
8041 // this by performing a garbage collection and retrying the
8042 // builtin once.
8043
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008044 // Enter the exit frame that transitions from JavaScript to C++.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008045 __ EnterExitFrame(mode_, result_size_);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008046
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008047 // rax: Holds the context at this point, but should not be used.
8048 // On entry to code generated by GenerateCore, it must hold
8049 // a failure result if the collect_garbage argument to GenerateCore
8050 // is true. This failure result can be the result of code
8051 // generated by a previous call to GenerateCore. The value
8052 // of rax is then passed to Runtime::PerformGC.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008053 // rbx: pointer to builtin function (C callee-saved).
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008054 // rbp: frame pointer of exit frame (restored after C call).
8055 // rsp: stack pointer (restored after C call).
8056 // r14: number of arguments including receiver (C callee-saved).
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008057 // r15: argv pointer (C callee-saved).
8058
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008059 Label throw_normal_exception;
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00008060 Label throw_termination_exception;
8061 Label throw_out_of_memory_exception;
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008062
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00008063 // Call into the runtime system.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008064 GenerateCore(masm,
8065 &throw_normal_exception,
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00008066 &throw_termination_exception,
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008067 &throw_out_of_memory_exception,
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00008068 false,
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008069 false);
8070
8071 // Do space-specific GC and retry runtime call.
8072 GenerateCore(masm,
8073 &throw_normal_exception,
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00008074 &throw_termination_exception,
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008075 &throw_out_of_memory_exception,
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008076 true,
8077 false);
8078
8079 // Do full GC and retry runtime call one final time.
8080 Failure* failure = Failure::InternalError();
8081 __ movq(rax, failure, RelocInfo::NONE);
8082 GenerateCore(masm,
8083 &throw_normal_exception,
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00008084 &throw_termination_exception,
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008085 &throw_out_of_memory_exception,
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008086 true,
8087 true);
8088
8089 __ bind(&throw_out_of_memory_exception);
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00008090 GenerateThrowUncatchable(masm, OUT_OF_MEMORY);
8091
8092 __ bind(&throw_termination_exception);
8093 GenerateThrowUncatchable(masm, TERMINATION);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008094
8095 __ bind(&throw_normal_exception);
8096 GenerateThrowTOS(masm);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008097}
8098
ager@chromium.orge2902be2009-06-08 12:21:35 +00008099
ager@chromium.orgc4c92722009-11-18 14:12:51 +00008100void ApiGetterEntryStub::Generate(MacroAssembler* masm) {
8101 UNREACHABLE();
8102}
8103
8104
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008105void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) {
ager@chromium.orge2902be2009-06-08 12:21:35 +00008106 Label invoke, exit;
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +00008107#ifdef ENABLE_LOGGING_AND_PROFILING
8108 Label not_outermost_js, not_outermost_js_2;
8109#endif
ager@chromium.orge2902be2009-06-08 12:21:35 +00008110
8111 // Setup frame.
8112 __ push(rbp);
8113 __ movq(rbp, rsp);
8114
ager@chromium.org18ad94b2009-09-02 08:22:29 +00008115 // Push the stack frame type marker twice.
ager@chromium.orge2902be2009-06-08 12:21:35 +00008116 int marker = is_construct ? StackFrame::ENTRY_CONSTRUCT : StackFrame::ENTRY;
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00008117 __ Push(Smi::FromInt(marker)); // context slot
8118 __ Push(Smi::FromInt(marker)); // function slot
ager@chromium.org18ad94b2009-09-02 08:22:29 +00008119 // Save callee-saved registers (X64 calling conventions).
ager@chromium.orge2902be2009-06-08 12:21:35 +00008120 __ push(r12);
8121 __ push(r13);
8122 __ push(r14);
8123 __ push(r15);
8124 __ push(rdi);
8125 __ push(rsi);
8126 __ push(rbx);
8127 // TODO(X64): Push XMM6-XMM15 (low 64 bits) as well, or make them
8128 // callee-save in JS code as well.
8129
8130 // Save copies of the top frame descriptor on the stack.
8131 ExternalReference c_entry_fp(Top::k_c_entry_fp_address);
8132 __ load_rax(c_entry_fp);
8133 __ push(rax);
8134
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +00008135#ifdef ENABLE_LOGGING_AND_PROFILING
8136 // If this is the outermost JS call, set js_entry_sp value.
8137 ExternalReference js_entry_sp(Top::k_js_entry_sp_address);
8138 __ load_rax(js_entry_sp);
8139 __ testq(rax, rax);
8140 __ j(not_zero, &not_outermost_js);
8141 __ movq(rax, rbp);
8142 __ store_rax(js_entry_sp);
8143 __ bind(&not_outermost_js);
8144#endif
8145
ager@chromium.orge2902be2009-06-08 12:21:35 +00008146 // Call a faked try-block that does the invoke.
8147 __ call(&invoke);
8148
8149 // Caught exception: Store result (exception) in the pending
8150 // exception field in the JSEnv and return a failure sentinel.
8151 ExternalReference pending_exception(Top::k_pending_exception_address);
8152 __ store_rax(pending_exception);
8153 __ movq(rax, Failure::Exception(), RelocInfo::NONE);
8154 __ jmp(&exit);
8155
8156 // Invoke: Link this frame into the handler chain.
8157 __ bind(&invoke);
8158 __ PushTryHandler(IN_JS_ENTRY, JS_ENTRY_HANDLER);
ager@chromium.orge2902be2009-06-08 12:21:35 +00008159
8160 // Clear any pending exceptions.
8161 __ load_rax(ExternalReference::the_hole_value_location());
8162 __ store_rax(pending_exception);
8163
8164 // Fake a receiver (NULL).
8165 __ push(Immediate(0)); // receiver
8166
8167 // Invoke the function by calling through JS entry trampoline
8168 // builtin and pop the faked function when we return. We load the address
8169 // from an external reference instead of inlining the call target address
8170 // directly in the code, because the builtin stubs may not have been
8171 // generated yet at the time this code is generated.
8172 if (is_construct) {
8173 ExternalReference construct_entry(Builtins::JSConstructEntryTrampoline);
8174 __ load_rax(construct_entry);
8175 } else {
8176 ExternalReference entry(Builtins::JSEntryTrampoline);
8177 __ load_rax(entry);
8178 }
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008179 __ lea(kScratchRegister, FieldOperand(rax, Code::kHeaderSize));
8180 __ call(kScratchRegister);
ager@chromium.orge2902be2009-06-08 12:21:35 +00008181
8182 // Unlink this frame from the handler chain.
8183 __ movq(kScratchRegister, ExternalReference(Top::k_handler_address));
8184 __ pop(Operand(kScratchRegister, 0));
8185 // Pop next_sp.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008186 __ addq(rsp, Immediate(StackHandlerConstants::kSize - kPointerSize));
ager@chromium.orge2902be2009-06-08 12:21:35 +00008187
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +00008188#ifdef ENABLE_LOGGING_AND_PROFILING
8189 // If current EBP value is the same as js_entry_sp value, it means that
8190 // the current function is the outermost.
8191 __ movq(kScratchRegister, js_entry_sp);
8192 __ cmpq(rbp, Operand(kScratchRegister, 0));
8193 __ j(not_equal, &not_outermost_js_2);
8194 __ movq(Operand(kScratchRegister, 0), Immediate(0));
8195 __ bind(&not_outermost_js_2);
8196#endif
8197
ager@chromium.orge2902be2009-06-08 12:21:35 +00008198 // Restore the top frame descriptor from the stack.
8199 __ bind(&exit);
8200 __ movq(kScratchRegister, ExternalReference(Top::k_c_entry_fp_address));
8201 __ pop(Operand(kScratchRegister, 0));
8202
8203 // Restore callee-saved registers (X64 conventions).
8204 __ pop(rbx);
8205 __ pop(rsi);
8206 __ pop(rdi);
8207 __ pop(r15);
8208 __ pop(r14);
8209 __ pop(r13);
8210 __ pop(r12);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008211 __ addq(rsp, Immediate(2 * kPointerSize)); // remove markers
ager@chromium.orge2902be2009-06-08 12:21:35 +00008212
8213 // Restore frame pointer and return.
8214 __ pop(rbp);
8215 __ ret(0);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008216}
8217
ager@chromium.org3e875802009-06-29 08:26:34 +00008218
8219// -----------------------------------------------------------------------------
8220// Implementation of stubs.
8221
8222// Stub classes have public member named masm, not masm_.
8223
8224void StackCheckStub::Generate(MacroAssembler* masm) {
8225 // Because builtins always remove the receiver from the stack, we
8226 // have to fake one to avoid underflowing the stack. The receiver
8227 // must be inserted below the return address on the stack so we
8228 // temporarily store that in a register.
8229 __ pop(rax);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00008230 __ Push(Smi::FromInt(0));
ager@chromium.org3e875802009-06-29 08:26:34 +00008231 __ push(rax);
8232
8233 // Do tail-call to runtime routine.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00008234 __ TailCallRuntime(Runtime::kStackGuard, 1, 1);
ager@chromium.org3e875802009-06-29 08:26:34 +00008235}
8236
8237
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00008238void FloatingPointHelper::LoadFloatOperand(MacroAssembler* masm,
8239 Register number) {
8240 Label load_smi, done;
8241
ager@chromium.org4af710e2009-09-15 12:20:11 +00008242 __ JumpIfSmi(number, &load_smi);
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00008243 __ fld_d(FieldOperand(number, HeapNumber::kValueOffset));
8244 __ jmp(&done);
8245
8246 __ bind(&load_smi);
ager@chromium.org4af710e2009-09-15 12:20:11 +00008247 __ SmiToInteger32(number, number);
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00008248 __ push(number);
8249 __ fild_s(Operand(rsp, 0));
8250 __ pop(number);
8251
8252 __ bind(&done);
8253}
8254
ager@chromium.org3e875802009-06-29 08:26:34 +00008255
8256void FloatingPointHelper::LoadFloatOperand(MacroAssembler* masm,
8257 Register src,
8258 XMMRegister dst) {
8259 Label load_smi, done;
8260
ager@chromium.org4af710e2009-09-15 12:20:11 +00008261 __ JumpIfSmi(src, &load_smi);
ager@chromium.org3e875802009-06-29 08:26:34 +00008262 __ movsd(dst, FieldOperand(src, HeapNumber::kValueOffset));
8263 __ jmp(&done);
8264
8265 __ bind(&load_smi);
ager@chromium.org4af710e2009-09-15 12:20:11 +00008266 __ SmiToInteger32(src, src);
ager@chromium.org3e875802009-06-29 08:26:34 +00008267 __ cvtlsi2sd(dst, src);
8268
8269 __ bind(&done);
8270}
8271
8272
8273void FloatingPointHelper::LoadFloatOperands(MacroAssembler* masm,
8274 XMMRegister dst1,
8275 XMMRegister dst2) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008276 __ movq(kScratchRegister, rdx);
ager@chromium.org3e875802009-06-29 08:26:34 +00008277 LoadFloatOperand(masm, kScratchRegister, dst1);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008278 __ movq(kScratchRegister, rax);
ager@chromium.org3e875802009-06-29 08:26:34 +00008279 LoadFloatOperand(masm, kScratchRegister, dst2);
8280}
8281
8282
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008283void FloatingPointHelper::LoadFloatOperandsFromSmis(MacroAssembler* masm,
8284 XMMRegister dst1,
8285 XMMRegister dst2) {
8286 __ SmiToInteger32(kScratchRegister, rdx);
8287 __ cvtlsi2sd(dst1, kScratchRegister);
8288 __ SmiToInteger32(kScratchRegister, rax);
8289 __ cvtlsi2sd(dst2, kScratchRegister);
ager@chromium.org3e875802009-06-29 08:26:34 +00008290}
8291
8292
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008293// Input: rdx, rax are the left and right objects of a bit op.
8294// Output: rax, rcx are left and right integers for a bit op.
8295void FloatingPointHelper::LoadAsIntegers(MacroAssembler* masm,
8296 bool use_sse3,
8297 Label* conversion_failure) {
8298 // Check float operands.
8299 Label arg1_is_object, check_undefined_arg1;
8300 Label arg2_is_object, check_undefined_arg2;
8301 Label load_arg2, done;
ager@chromium.org3e875802009-06-29 08:26:34 +00008302
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008303 __ JumpIfNotSmi(rdx, &arg1_is_object);
8304 __ SmiToInteger32(rdx, rdx);
8305 __ jmp(&load_arg2);
8306
8307 // If the argument is undefined it converts to zero (ECMA-262, section 9.5).
8308 __ bind(&check_undefined_arg1);
8309 __ CompareRoot(rdx, Heap::kUndefinedValueRootIndex);
8310 __ j(not_equal, conversion_failure);
8311 __ movl(rdx, Immediate(0));
8312 __ jmp(&load_arg2);
8313
8314 __ bind(&arg1_is_object);
8315 __ movq(rbx, FieldOperand(rdx, HeapObject::kMapOffset));
8316 __ CompareRoot(rbx, Heap::kHeapNumberMapRootIndex);
8317 __ j(not_equal, &check_undefined_arg1);
8318 // Get the untagged integer version of the edx heap number in rcx.
8319 IntegerConvert(masm, rdx, use_sse3, conversion_failure);
8320 __ movl(rdx, rcx);
8321
8322 // Here edx has the untagged integer, eax has a Smi or a heap number.
8323 __ bind(&load_arg2);
8324 // Test if arg2 is a Smi.
8325 __ JumpIfNotSmi(rax, &arg2_is_object);
8326 __ SmiToInteger32(rax, rax);
8327 __ movl(rcx, rax);
ager@chromium.org3e875802009-06-29 08:26:34 +00008328 __ jmp(&done);
8329
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008330 // If the argument is undefined it converts to zero (ECMA-262, section 9.5).
8331 __ bind(&check_undefined_arg2);
8332 __ CompareRoot(rax, Heap::kUndefinedValueRootIndex);
8333 __ j(not_equal, conversion_failure);
8334 __ movl(rcx, Immediate(0));
8335 __ jmp(&done);
ager@chromium.org3e875802009-06-29 08:26:34 +00008336
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008337 __ bind(&arg2_is_object);
8338 __ movq(rbx, FieldOperand(rax, HeapObject::kMapOffset));
8339 __ CompareRoot(rbx, Heap::kHeapNumberMapRootIndex);
8340 __ j(not_equal, &check_undefined_arg2);
8341 // Get the untagged integer version of the eax heap number in ecx.
8342 IntegerConvert(masm, rax, use_sse3, conversion_failure);
ager@chromium.org3e875802009-06-29 08:26:34 +00008343 __ bind(&done);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008344 __ movl(rax, rdx);
ager@chromium.org3e875802009-06-29 08:26:34 +00008345}
8346
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +00008347
ager@chromium.org3e875802009-06-29 08:26:34 +00008348void FloatingPointHelper::LoadFloatOperands(MacroAssembler* masm,
8349 Register lhs,
8350 Register rhs) {
8351 Label load_smi_lhs, load_smi_rhs, done_load_lhs, done;
ager@chromium.org4af710e2009-09-15 12:20:11 +00008352 __ JumpIfSmi(lhs, &load_smi_lhs);
ager@chromium.org3e875802009-06-29 08:26:34 +00008353 __ fld_d(FieldOperand(lhs, HeapNumber::kValueOffset));
8354 __ bind(&done_load_lhs);
8355
ager@chromium.org4af710e2009-09-15 12:20:11 +00008356 __ JumpIfSmi(rhs, &load_smi_rhs);
ager@chromium.org3e875802009-06-29 08:26:34 +00008357 __ fld_d(FieldOperand(rhs, HeapNumber::kValueOffset));
8358 __ jmp(&done);
8359
8360 __ bind(&load_smi_lhs);
ager@chromium.org4af710e2009-09-15 12:20:11 +00008361 __ SmiToInteger64(kScratchRegister, lhs);
ager@chromium.org3e875802009-06-29 08:26:34 +00008362 __ push(kScratchRegister);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008363 __ fild_d(Operand(rsp, 0));
ager@chromium.org3e875802009-06-29 08:26:34 +00008364 __ pop(kScratchRegister);
8365 __ jmp(&done_load_lhs);
8366
8367 __ bind(&load_smi_rhs);
ager@chromium.org4af710e2009-09-15 12:20:11 +00008368 __ SmiToInteger64(kScratchRegister, rhs);
ager@chromium.org3e875802009-06-29 08:26:34 +00008369 __ push(kScratchRegister);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008370 __ fild_d(Operand(rsp, 0));
ager@chromium.org3e875802009-06-29 08:26:34 +00008371 __ pop(kScratchRegister);
8372
8373 __ bind(&done);
8374}
8375
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +00008376
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00008377void FloatingPointHelper::CheckNumberOperands(MacroAssembler* masm,
8378 Label* non_float) {
ager@chromium.org3e875802009-06-29 08:26:34 +00008379 Label test_other, done;
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008380 // Test if both operands are numbers (heap_numbers or smis).
8381 // If not, jump to label non_float.
ager@chromium.org4af710e2009-09-15 12:20:11 +00008382 __ JumpIfSmi(rdx, &test_other); // argument in rdx is OK
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008383 __ Cmp(FieldOperand(rdx, HeapObject::kMapOffset), Factory::heap_number_map());
8384 __ j(not_equal, non_float); // The argument in rdx is not a number.
ager@chromium.org3e875802009-06-29 08:26:34 +00008385
8386 __ bind(&test_other);
ager@chromium.org4af710e2009-09-15 12:20:11 +00008387 __ JumpIfSmi(rax, &done); // argument in rax is OK
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008388 __ Cmp(FieldOperand(rax, HeapObject::kMapOffset), Factory::heap_number_map());
8389 __ j(not_equal, non_float); // The argument in rax is not a number.
ager@chromium.org3e875802009-06-29 08:26:34 +00008390
8391 // Fall-through: Both operands are numbers.
8392 __ bind(&done);
8393}
8394
8395
8396const char* GenericBinaryOpStub::GetName() {
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00008397 if (name_ != NULL) return name_;
8398 const int len = 100;
8399 name_ = Bootstrapper::AllocateAutoDeletedArray(len);
8400 if (name_ == NULL) return "OOM";
8401 const char* op_name = Token::Name(op_);
8402 const char* overwrite_name;
8403 switch (mode_) {
8404 case NO_OVERWRITE: overwrite_name = "Alloc"; break;
8405 case OVERWRITE_RIGHT: overwrite_name = "OverwriteRight"; break;
8406 case OVERWRITE_LEFT: overwrite_name = "OverwriteLeft"; break;
8407 default: overwrite_name = "UnknownOverwrite"; break;
ager@chromium.org3e875802009-06-29 08:26:34 +00008408 }
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00008409
8410 OS::SNPrintF(Vector<char>(name_, len),
ager@chromium.org5c838252010-02-19 08:53:10 +00008411 "GenericBinaryOpStub_%s_%s%s_%s%s_%s%s",
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00008412 op_name,
8413 overwrite_name,
8414 (flags_ & NO_SMI_CODE_IN_STUB) ? "_NoSmiInStub" : "",
8415 args_in_registers_ ? "RegArgs" : "StackArgs",
8416 args_reversed_ ? "_R" : "",
ager@chromium.org5c838252010-02-19 08:53:10 +00008417 use_sse3_ ? "SSE3" : "SSE2",
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00008418 operands_type_.ToString());
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00008419 return name_;
ager@chromium.org3e875802009-06-29 08:26:34 +00008420}
8421
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +00008422
ager@chromium.org3811b432009-10-28 14:53:37 +00008423void GenericBinaryOpStub::GenerateCall(
8424 MacroAssembler* masm,
8425 Register left,
8426 Register right) {
8427 if (!ArgsInRegistersSupported()) {
8428 // Pass arguments on the stack.
8429 __ push(left);
8430 __ push(right);
8431 } else {
8432 // The calling convention with registers is left in rdx and right in rax.
8433 Register left_arg = rdx;
8434 Register right_arg = rax;
8435 if (!(left.is(left_arg) && right.is(right_arg))) {
8436 if (left.is(right_arg) && right.is(left_arg)) {
8437 if (IsOperationCommutative()) {
8438 SetArgsReversed();
8439 } else {
8440 __ xchg(left, right);
8441 }
8442 } else if (left.is(left_arg)) {
8443 __ movq(right_arg, right);
ager@chromium.org951093b2010-02-11 12:59:04 +00008444 } else if (right.is(right_arg)) {
8445 __ movq(left_arg, left);
ager@chromium.org3811b432009-10-28 14:53:37 +00008446 } else if (left.is(right_arg)) {
8447 if (IsOperationCommutative()) {
8448 __ movq(left_arg, right);
8449 SetArgsReversed();
8450 } else {
8451 // Order of moves important to avoid destroying left argument.
8452 __ movq(left_arg, left);
8453 __ movq(right_arg, right);
8454 }
8455 } else if (right.is(left_arg)) {
8456 if (IsOperationCommutative()) {
8457 __ movq(right_arg, left);
8458 SetArgsReversed();
8459 } else {
8460 // Order of moves important to avoid destroying right argument.
8461 __ movq(right_arg, right);
8462 __ movq(left_arg, left);
8463 }
ager@chromium.org3811b432009-10-28 14:53:37 +00008464 } else {
8465 // Order of moves is not important.
8466 __ movq(left_arg, left);
8467 __ movq(right_arg, right);
8468 }
8469 }
8470
8471 // Update flags to indicate that arguments are in registers.
8472 SetArgsInRegisters();
8473 __ IncrementCounter(&Counters::generic_binary_stub_calls_regs, 1);
8474 }
8475
8476 // Call the stub.
8477 __ CallStub(this);
8478}
8479
8480
8481void GenericBinaryOpStub::GenerateCall(
8482 MacroAssembler* masm,
8483 Register left,
8484 Smi* right) {
8485 if (!ArgsInRegistersSupported()) {
8486 // Pass arguments on the stack.
8487 __ push(left);
8488 __ Push(right);
8489 } else {
8490 // The calling convention with registers is left in rdx and right in rax.
8491 Register left_arg = rdx;
8492 Register right_arg = rax;
8493 if (left.is(left_arg)) {
8494 __ Move(right_arg, right);
8495 } else if (left.is(right_arg) && IsOperationCommutative()) {
8496 __ Move(left_arg, right);
8497 SetArgsReversed();
8498 } else {
ager@chromium.org951093b2010-02-11 12:59:04 +00008499 // For non-commutative operations, left and right_arg might be
8500 // the same register. Therefore, the order of the moves is
8501 // important here in order to not overwrite left before moving
8502 // it to left_arg.
ager@chromium.org3811b432009-10-28 14:53:37 +00008503 __ movq(left_arg, left);
8504 __ Move(right_arg, right);
8505 }
8506
8507 // Update flags to indicate that arguments are in registers.
8508 SetArgsInRegisters();
8509 __ IncrementCounter(&Counters::generic_binary_stub_calls_regs, 1);
8510 }
8511
8512 // Call the stub.
8513 __ CallStub(this);
8514}
8515
8516
8517void GenericBinaryOpStub::GenerateCall(
8518 MacroAssembler* masm,
8519 Smi* left,
8520 Register right) {
8521 if (!ArgsInRegistersSupported()) {
8522 // Pass arguments on the stack.
8523 __ Push(left);
8524 __ push(right);
8525 } else {
8526 // The calling convention with registers is left in rdx and right in rax.
8527 Register left_arg = rdx;
8528 Register right_arg = rax;
8529 if (right.is(right_arg)) {
8530 __ Move(left_arg, left);
8531 } else if (right.is(left_arg) && IsOperationCommutative()) {
8532 __ Move(right_arg, left);
8533 SetArgsReversed();
8534 } else {
ager@chromium.org951093b2010-02-11 12:59:04 +00008535 // For non-commutative operations, right and left_arg might be
8536 // the same register. Therefore, the order of the moves is
8537 // important here in order to not overwrite right before moving
8538 // it to right_arg.
ager@chromium.org3811b432009-10-28 14:53:37 +00008539 __ movq(right_arg, right);
ager@chromium.org951093b2010-02-11 12:59:04 +00008540 __ Move(left_arg, left);
ager@chromium.org3811b432009-10-28 14:53:37 +00008541 }
8542 // Update flags to indicate that arguments are in registers.
8543 SetArgsInRegisters();
8544 __ IncrementCounter(&Counters::generic_binary_stub_calls_regs, 1);
8545 }
8546
8547 // Call the stub.
8548 __ CallStub(this);
8549}
8550
8551
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008552Result GenericBinaryOpStub::GenerateCall(MacroAssembler* masm,
8553 VirtualFrame* frame,
8554 Result* left,
8555 Result* right) {
8556 if (ArgsInRegistersSupported()) {
8557 SetArgsInRegisters();
8558 return frame->CallStub(this, left, right);
8559 } else {
8560 frame->Push(left);
8561 frame->Push(right);
8562 return frame->CallStub(this, 2);
8563 }
8564}
8565
8566
ager@chromium.org3e875802009-06-29 08:26:34 +00008567void GenericBinaryOpStub::GenerateSmiCode(MacroAssembler* masm, Label* slow) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008568 // 1. Move arguments into edx, eax except for DIV and MOD, which need the
8569 // dividend in eax and edx free for the division. Use eax, ebx for those.
8570 Comment load_comment(masm, "-- Load arguments");
8571 Register left = rdx;
8572 Register right = rax;
8573 if (op_ == Token::DIV || op_ == Token::MOD) {
8574 left = rax;
8575 right = rbx;
8576 if (HasArgsInRegisters()) {
8577 __ movq(rbx, rax);
8578 __ movq(rax, rdx);
8579 }
8580 }
8581 if (!HasArgsInRegisters()) {
8582 __ movq(right, Operand(rsp, 1 * kPointerSize));
8583 __ movq(left, Operand(rsp, 2 * kPointerSize));
8584 }
ager@chromium.org3e875802009-06-29 08:26:34 +00008585
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008586 // 2. Smi check both operands. Skip the check for OR as it is better combined
8587 // with the actual operation.
8588 Label not_smis;
8589 if (op_ != Token::BIT_OR) {
8590 Comment smi_check_comment(masm, "-- Smi check arguments");
8591 __ JumpIfNotBothSmi(left, right, &not_smis);
8592 }
ager@chromium.org3e875802009-06-29 08:26:34 +00008593
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008594 // 3. Operands are both smis (except for OR), perform the operation leaving
8595 // the result in rax and check the result if necessary.
8596 Comment perform_smi(masm, "-- Perform smi operation");
8597 Label use_fp_on_smis;
ager@chromium.org3e875802009-06-29 08:26:34 +00008598 switch (op_) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008599 case Token::ADD: {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008600 ASSERT(right.is(rax));
8601 __ SmiAdd(right, right, left, &use_fp_on_smis); // ADD is commutative.
ager@chromium.org3e875802009-06-29 08:26:34 +00008602 break;
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008603 }
8604
8605 case Token::SUB: {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008606 __ SmiSub(left, left, right, &use_fp_on_smis);
8607 __ movq(rax, left);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008608 break;
8609 }
ager@chromium.org3e875802009-06-29 08:26:34 +00008610
8611 case Token::MUL:
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008612 ASSERT(right.is(rax));
8613 __ SmiMul(right, right, left, &use_fp_on_smis); // MUL is commutative.
ager@chromium.org3e875802009-06-29 08:26:34 +00008614 break;
8615
8616 case Token::DIV:
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008617 ASSERT(left.is(rax));
8618 __ SmiDiv(left, left, right, &use_fp_on_smis);
ager@chromium.org3e875802009-06-29 08:26:34 +00008619 break;
8620
8621 case Token::MOD:
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008622 ASSERT(left.is(rax));
8623 __ SmiMod(left, left, right, slow);
ager@chromium.org3e875802009-06-29 08:26:34 +00008624 break;
8625
8626 case Token::BIT_OR:
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008627 ASSERT(right.is(rax));
8628 __ movq(rcx, right); // Save the right operand.
8629 __ SmiOr(right, right, left); // BIT_OR is commutative.
8630 __ testb(right, Immediate(kSmiTagMask));
8631 __ j(not_zero, &not_smis);
ager@chromium.org3e875802009-06-29 08:26:34 +00008632 break;
8633
8634 case Token::BIT_AND:
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008635 ASSERT(right.is(rax));
8636 __ SmiAnd(right, right, left); // BIT_AND is commutative.
ager@chromium.org3e875802009-06-29 08:26:34 +00008637 break;
8638
8639 case Token::BIT_XOR:
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008640 ASSERT(right.is(rax));
8641 __ SmiXor(right, right, left); // BIT_XOR is commutative.
ager@chromium.org3e875802009-06-29 08:26:34 +00008642 break;
8643
8644 case Token::SHL:
8645 case Token::SHR:
8646 case Token::SAR:
ager@chromium.org3e875802009-06-29 08:26:34 +00008647 switch (op_) {
8648 case Token::SAR:
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008649 __ SmiShiftArithmeticRight(left, left, right);
ager@chromium.org3e875802009-06-29 08:26:34 +00008650 break;
8651 case Token::SHR:
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008652 __ SmiShiftLogicalRight(left, left, right, slow);
ager@chromium.org3e875802009-06-29 08:26:34 +00008653 break;
8654 case Token::SHL:
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008655 __ SmiShiftLeft(left, left, right, slow);
ager@chromium.org3e875802009-06-29 08:26:34 +00008656 break;
8657 default:
8658 UNREACHABLE();
8659 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008660 __ movq(rax, left);
ager@chromium.org3e875802009-06-29 08:26:34 +00008661 break;
8662
8663 default:
8664 UNREACHABLE();
8665 break;
8666 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008667
8668 // 4. Emit return of result in eax.
8669 GenerateReturn(masm);
8670
8671 // 5. For some operations emit inline code to perform floating point
8672 // operations on known smis (e.g., if the result of the operation
8673 // overflowed the smi range).
8674 switch (op_) {
8675 case Token::ADD:
8676 case Token::SUB:
8677 case Token::MUL:
8678 case Token::DIV: {
8679 __ bind(&use_fp_on_smis);
8680 if (op_ == Token::DIV) {
8681 __ movq(rdx, rax);
8682 __ movq(rax, rbx);
8683 }
8684 // left is rdx, right is rax.
8685 __ AllocateHeapNumber(rbx, rcx, slow);
8686 FloatingPointHelper::LoadFloatOperandsFromSmis(masm, xmm4, xmm5);
8687 switch (op_) {
8688 case Token::ADD: __ addsd(xmm4, xmm5); break;
8689 case Token::SUB: __ subsd(xmm4, xmm5); break;
8690 case Token::MUL: __ mulsd(xmm4, xmm5); break;
8691 case Token::DIV: __ divsd(xmm4, xmm5); break;
8692 default: UNREACHABLE();
8693 }
8694 __ movsd(FieldOperand(rbx, HeapNumber::kValueOffset), xmm4);
8695 __ movq(rax, rbx);
8696 GenerateReturn(masm);
8697 }
8698 default:
8699 break;
8700 }
8701
8702 // 6. Non-smi operands, fall out to the non-smi code with the operands in
8703 // rdx and rax.
8704 Comment done_comment(masm, "-- Enter non-smi code");
8705 __ bind(&not_smis);
8706
8707 switch (op_) {
8708 case Token::DIV:
8709 case Token::MOD:
8710 // Operands are in rax, rbx at this point.
8711 __ movq(rdx, rax);
8712 __ movq(rax, rbx);
8713 break;
8714
8715 case Token::BIT_OR:
8716 // Right operand is saved in rcx and rax was destroyed by the smi
8717 // operation.
8718 __ movq(rax, rcx);
8719 break;
8720
8721 default:
8722 break;
8723 }
ager@chromium.org3e875802009-06-29 08:26:34 +00008724}
8725
8726
8727void GenericBinaryOpStub::Generate(MacroAssembler* masm) {
8728 Label call_runtime;
ager@chromium.org3811b432009-10-28 14:53:37 +00008729 if (HasSmiCodeInStub()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008730 GenerateSmiCode(masm, &call_runtime);
8731 } else if (op_ != Token::MOD) {
8732 GenerateLoadArguments(masm);
ager@chromium.org3e875802009-06-29 08:26:34 +00008733 }
ager@chromium.org3e875802009-06-29 08:26:34 +00008734 // Floating point case.
8735 switch (op_) {
8736 case Token::ADD:
8737 case Token::SUB:
8738 case Token::MUL:
8739 case Token::DIV: {
8740 // rax: y
8741 // rdx: x
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00008742 if (operands_type_.IsNumber()) {
ager@chromium.org5c838252010-02-19 08:53:10 +00008743 if (FLAG_debug_code) {
8744 // Assert at runtime that inputs are only numbers.
8745 __ AbortIfNotNumber(rdx, "GenericBinaryOpStub operand not a number.");
8746 __ AbortIfNotNumber(rax, "GenericBinaryOpStub operand not a number.");
8747 }
8748 } else {
8749 FloatingPointHelper::CheckNumberOperands(masm, &call_runtime);
8750 }
ager@chromium.org3e875802009-06-29 08:26:34 +00008751 // Fast-case: Both operands are numbers.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008752 // xmm4 and xmm5 are volatile XMM registers.
8753 FloatingPointHelper::LoadFloatOperands(masm, xmm4, xmm5);
8754
8755 switch (op_) {
8756 case Token::ADD: __ addsd(xmm4, xmm5); break;
8757 case Token::SUB: __ subsd(xmm4, xmm5); break;
8758 case Token::MUL: __ mulsd(xmm4, xmm5); break;
8759 case Token::DIV: __ divsd(xmm4, xmm5); break;
8760 default: UNREACHABLE();
8761 }
ager@chromium.org3e875802009-06-29 08:26:34 +00008762 // Allocate a heap number, if needed.
8763 Label skip_allocation;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008764 OverwriteMode mode = mode_;
8765 if (HasArgsReversed()) {
8766 if (mode == OVERWRITE_RIGHT) {
8767 mode = OVERWRITE_LEFT;
8768 } else if (mode == OVERWRITE_LEFT) {
8769 mode = OVERWRITE_RIGHT;
8770 }
8771 }
8772 switch (mode) {
ager@chromium.org3e875802009-06-29 08:26:34 +00008773 case OVERWRITE_LEFT:
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008774 __ JumpIfNotSmi(rdx, &skip_allocation);
8775 __ AllocateHeapNumber(rbx, rcx, &call_runtime);
8776 __ movq(rdx, rbx);
8777 __ bind(&skip_allocation);
ager@chromium.org3e875802009-06-29 08:26:34 +00008778 __ movq(rax, rdx);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008779 break;
ager@chromium.org3e875802009-06-29 08:26:34 +00008780 case OVERWRITE_RIGHT:
8781 // If the argument in rax is already an object, we skip the
8782 // allocation of a heap number.
ager@chromium.org4af710e2009-09-15 12:20:11 +00008783 __ JumpIfNotSmi(rax, &skip_allocation);
ager@chromium.org3e875802009-06-29 08:26:34 +00008784 // Fall through!
8785 case NO_OVERWRITE:
ager@chromium.org3811b432009-10-28 14:53:37 +00008786 // Allocate a heap number for the result. Keep rax and rdx intact
8787 // for the possible runtime call.
8788 __ AllocateHeapNumber(rbx, rcx, &call_runtime);
8789 __ movq(rax, rbx);
ager@chromium.org3e875802009-06-29 08:26:34 +00008790 __ bind(&skip_allocation);
8791 break;
8792 default: UNREACHABLE();
8793 }
ager@chromium.org3e875802009-06-29 08:26:34 +00008794 __ movsd(FieldOperand(rax, HeapNumber::kValueOffset), xmm4);
ager@chromium.org3811b432009-10-28 14:53:37 +00008795 GenerateReturn(masm);
ager@chromium.org3e875802009-06-29 08:26:34 +00008796 }
8797 case Token::MOD: {
8798 // For MOD we go directly to runtime in the non-smi case.
8799 break;
8800 }
8801 case Token::BIT_OR:
8802 case Token::BIT_AND:
8803 case Token::BIT_XOR:
8804 case Token::SAR:
8805 case Token::SHL:
8806 case Token::SHR: {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008807 Label skip_allocation, non_smi_result;
8808 FloatingPointHelper::LoadAsIntegers(masm, use_sse3_, &call_runtime);
ager@chromium.org3e875802009-06-29 08:26:34 +00008809 switch (op_) {
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00008810 case Token::BIT_OR: __ orl(rax, rcx); break;
8811 case Token::BIT_AND: __ andl(rax, rcx); break;
8812 case Token::BIT_XOR: __ xorl(rax, rcx); break;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00008813 case Token::SAR: __ sarl_cl(rax); break;
8814 case Token::SHL: __ shll_cl(rax); break;
8815 case Token::SHR: __ shrl_cl(rax); break;
ager@chromium.org3e875802009-06-29 08:26:34 +00008816 default: UNREACHABLE();
8817 }
8818 if (op_ == Token::SHR) {
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00008819 // Check if result is non-negative. This can only happen for a shift
8820 // by zero, which also doesn't update the sign flag.
8821 __ testl(rax, rax);
ager@chromium.org3e875802009-06-29 08:26:34 +00008822 __ j(negative, &non_smi_result);
8823 }
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00008824 __ JumpIfNotValidSmiValue(rax, &non_smi_result);
8825 // Tag smi result, if possible, and return.
ager@chromium.org4af710e2009-09-15 12:20:11 +00008826 __ Integer32ToSmi(rax, rax);
ager@chromium.org3811b432009-10-28 14:53:37 +00008827 GenerateReturn(masm);
ager@chromium.org3e875802009-06-29 08:26:34 +00008828
8829 // All ops except SHR return a signed int32 that we load in a HeapNumber.
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00008830 if (op_ != Token::SHR && non_smi_result.is_linked()) {
ager@chromium.org3e875802009-06-29 08:26:34 +00008831 __ bind(&non_smi_result);
8832 // Allocate a heap number if needed.
8833 __ movsxlq(rbx, rax); // rbx: sign extended 32-bit result
8834 switch (mode_) {
8835 case OVERWRITE_LEFT:
8836 case OVERWRITE_RIGHT:
8837 // If the operand was an object, we skip the
8838 // allocation of a heap number.
8839 __ movq(rax, Operand(rsp, mode_ == OVERWRITE_RIGHT ?
8840 1 * kPointerSize : 2 * kPointerSize));
ager@chromium.org4af710e2009-09-15 12:20:11 +00008841 __ JumpIfNotSmi(rax, &skip_allocation);
ager@chromium.org3e875802009-06-29 08:26:34 +00008842 // Fall through!
8843 case NO_OVERWRITE:
ager@chromium.org3811b432009-10-28 14:53:37 +00008844 __ AllocateHeapNumber(rax, rcx, &call_runtime);
ager@chromium.org3e875802009-06-29 08:26:34 +00008845 __ bind(&skip_allocation);
8846 break;
8847 default: UNREACHABLE();
8848 }
8849 // Store the result in the HeapNumber and return.
8850 __ movq(Operand(rsp, 1 * kPointerSize), rbx);
8851 __ fild_s(Operand(rsp, 1 * kPointerSize));
8852 __ fstp_d(FieldOperand(rax, HeapNumber::kValueOffset));
ager@chromium.org3811b432009-10-28 14:53:37 +00008853 GenerateReturn(masm);
ager@chromium.org3e875802009-06-29 08:26:34 +00008854 }
8855
ager@chromium.org3e875802009-06-29 08:26:34 +00008856 // SHR should return uint32 - go to runtime for non-smi/negative result.
8857 if (op_ == Token::SHR) {
8858 __ bind(&non_smi_result);
8859 }
ager@chromium.org3e875802009-06-29 08:26:34 +00008860 break;
8861 }
8862 default: UNREACHABLE(); break;
8863 }
8864
8865 // If all else fails, use the runtime system to get the correct
ager@chromium.org3811b432009-10-28 14:53:37 +00008866 // result. If arguments was passed in registers now place them on the
8867 // stack in the correct order below the return address.
ager@chromium.org3e875802009-06-29 08:26:34 +00008868 __ bind(&call_runtime);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00008869
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008870 if (HasArgsInRegisters()) {
ager@chromium.org3811b432009-10-28 14:53:37 +00008871 __ pop(rcx);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008872 if (HasArgsReversed()) {
ager@chromium.org3811b432009-10-28 14:53:37 +00008873 __ push(rax);
8874 __ push(rdx);
8875 } else {
8876 __ push(rdx);
8877 __ push(rax);
8878 }
8879 __ push(rcx);
8880 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00008881
ager@chromium.org3e875802009-06-29 08:26:34 +00008882 switch (op_) {
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00008883 case Token::ADD: {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00008884 // Registers containing left and right operands respectively.
8885 Register lhs, rhs;
8886
8887 if (HasArgsReversed()) {
8888 lhs = rax;
8889 rhs = rdx;
8890 } else {
8891 lhs = rdx;
8892 rhs = rax;
8893 }
8894
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00008895 // Test for string arguments before calling runtime.
fschneider@chromium.org086aac62010-03-17 13:18:24 +00008896 Label not_strings, both_strings, not_string1, string1, string1_smi2;
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00008897 Condition is_smi;
8898 Result answer;
fschneider@chromium.org086aac62010-03-17 13:18:24 +00008899 is_smi = masm->CheckSmi(lhs);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00008900 __ j(is_smi, &not_string1);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00008901 __ CmpObjectType(lhs, FIRST_NONSTRING_TYPE, r8);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00008902 __ j(above_equal, &not_string1);
8903
8904 // First argument is a a string, test second.
fschneider@chromium.org086aac62010-03-17 13:18:24 +00008905 is_smi = masm->CheckSmi(rhs);
8906 __ j(is_smi, &string1_smi2);
8907 __ CmpObjectType(rhs, FIRST_NONSTRING_TYPE, r9);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00008908 __ j(above_equal, &string1);
8909
8910 // First and second argument are strings.
fschneider@chromium.org086aac62010-03-17 13:18:24 +00008911 StringAddStub string_add_stub(NO_STRING_CHECK_IN_STUB);
8912 __ TailCallStub(&string_add_stub);
8913
8914 __ bind(&string1_smi2);
8915 // First argument is a string, second is a smi. Try to lookup the number
8916 // string for the smi in the number string cache.
8917 NumberToStringStub::GenerateLookupNumberStringCache(
8918 masm, rhs, rbx, rcx, r8, true, &string1);
8919
8920 // Replace second argument on stack and tailcall string add stub to make
8921 // the result.
8922 __ movq(Operand(rsp, 1 * kPointerSize), rbx);
8923 __ TailCallStub(&string_add_stub);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00008924
8925 // Only first argument is a string.
8926 __ bind(&string1);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00008927 __ InvokeBuiltin(Builtins::STRING_ADD_LEFT, JUMP_FUNCTION);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00008928
8929 // First argument was not a string, test second.
8930 __ bind(&not_string1);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00008931 is_smi = masm->CheckSmi(rhs);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00008932 __ j(is_smi, &not_strings);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00008933 __ CmpObjectType(rhs, FIRST_NONSTRING_TYPE, rhs);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00008934 __ j(above_equal, &not_strings);
8935
8936 // Only second argument is a string.
fschneider@chromium.org086aac62010-03-17 13:18:24 +00008937 __ InvokeBuiltin(Builtins::STRING_ADD_RIGHT, JUMP_FUNCTION);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00008938
8939 __ bind(&not_strings);
8940 // Neither argument is a string.
ager@chromium.org3e875802009-06-29 08:26:34 +00008941 __ InvokeBuiltin(Builtins::ADD, JUMP_FUNCTION);
8942 break;
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00008943 }
ager@chromium.org3e875802009-06-29 08:26:34 +00008944 case Token::SUB:
8945 __ InvokeBuiltin(Builtins::SUB, JUMP_FUNCTION);
8946 break;
8947 case Token::MUL:
8948 __ InvokeBuiltin(Builtins::MUL, JUMP_FUNCTION);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008949 break;
ager@chromium.org3e875802009-06-29 08:26:34 +00008950 case Token::DIV:
8951 __ InvokeBuiltin(Builtins::DIV, JUMP_FUNCTION);
8952 break;
8953 case Token::MOD:
8954 __ InvokeBuiltin(Builtins::MOD, JUMP_FUNCTION);
8955 break;
8956 case Token::BIT_OR:
8957 __ InvokeBuiltin(Builtins::BIT_OR, JUMP_FUNCTION);
8958 break;
8959 case Token::BIT_AND:
8960 __ InvokeBuiltin(Builtins::BIT_AND, JUMP_FUNCTION);
8961 break;
8962 case Token::BIT_XOR:
8963 __ InvokeBuiltin(Builtins::BIT_XOR, JUMP_FUNCTION);
8964 break;
8965 case Token::SAR:
8966 __ InvokeBuiltin(Builtins::SAR, JUMP_FUNCTION);
8967 break;
8968 case Token::SHL:
8969 __ InvokeBuiltin(Builtins::SHL, JUMP_FUNCTION);
8970 break;
8971 case Token::SHR:
8972 __ InvokeBuiltin(Builtins::SHR, JUMP_FUNCTION);
8973 break;
8974 default:
8975 UNREACHABLE();
8976 }
8977}
8978
8979
ager@chromium.org3811b432009-10-28 14:53:37 +00008980void GenericBinaryOpStub::GenerateLoadArguments(MacroAssembler* masm) {
8981 // If arguments are not passed in registers read them from the stack.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008982 if (!HasArgsInRegisters()) {
ager@chromium.org3811b432009-10-28 14:53:37 +00008983 __ movq(rax, Operand(rsp, 1 * kPointerSize));
8984 __ movq(rdx, Operand(rsp, 2 * kPointerSize));
8985 }
8986}
8987
8988
8989void GenericBinaryOpStub::GenerateReturn(MacroAssembler* masm) {
8990 // If arguments are not passed in registers remove them from the stack before
8991 // returning.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008992 if (!HasArgsInRegisters()) {
ager@chromium.org3811b432009-10-28 14:53:37 +00008993 __ ret(2 * kPointerSize); // Remove both operands
8994 } else {
8995 __ ret(0);
8996 }
8997}
8998
8999
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009000Handle<Code> GetBinaryOpStub(int key, BinaryOpIC::TypeInfo type_info) {
9001 return Handle<Code>::null();
9002}
9003
9004
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009005int CompareStub::MinorKey() {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00009006 // Encode the three parameters in a unique 16 bit value.
9007 ASSERT(static_cast<unsigned>(cc_) < (1 << 14));
9008 int nnn_value = (never_nan_nan_ ? 2 : 0);
9009 if (cc_ != equal) nnn_value = 0; // Avoid duplicate stubs.
9010 return (static_cast<unsigned>(cc_) << 2) | nnn_value | (strict_ ? 1 : 0);
9011}
9012
9013
9014const char* CompareStub::GetName() {
9015 switch (cc_) {
9016 case less: return "CompareStub_LT";
9017 case greater: return "CompareStub_GT";
9018 case less_equal: return "CompareStub_LE";
9019 case greater_equal: return "CompareStub_GE";
9020 case not_equal: {
9021 if (strict_) {
9022 if (never_nan_nan_) {
9023 return "CompareStub_NE_STRICT_NO_NAN";
9024 } else {
9025 return "CompareStub_NE_STRICT";
9026 }
9027 } else {
9028 if (never_nan_nan_) {
9029 return "CompareStub_NE_NO_NAN";
9030 } else {
9031 return "CompareStub_NE";
9032 }
9033 }
9034 }
9035 case equal: {
9036 if (strict_) {
9037 if (never_nan_nan_) {
9038 return "CompareStub_EQ_STRICT_NO_NAN";
9039 } else {
9040 return "CompareStub_EQ_STRICT";
9041 }
9042 } else {
9043 if (never_nan_nan_) {
9044 return "CompareStub_EQ_NO_NAN";
9045 } else {
9046 return "CompareStub_EQ";
9047 }
9048 }
9049 }
9050 default: return "CompareStub";
9051 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009052}
9053
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00009054
9055void StringAddStub::Generate(MacroAssembler* masm) {
9056 Label string_add_runtime;
9057
9058 // Load the two arguments.
9059 __ movq(rax, Operand(rsp, 2 * kPointerSize)); // First argument.
9060 __ movq(rdx, Operand(rsp, 1 * kPointerSize)); // Second argument.
9061
9062 // Make sure that both arguments are strings if not known in advance.
9063 if (string_check_) {
9064 Condition is_smi;
9065 is_smi = masm->CheckSmi(rax);
9066 __ j(is_smi, &string_add_runtime);
9067 __ CmpObjectType(rax, FIRST_NONSTRING_TYPE, r8);
9068 __ j(above_equal, &string_add_runtime);
9069
9070 // First argument is a a string, test second.
9071 is_smi = masm->CheckSmi(rdx);
9072 __ j(is_smi, &string_add_runtime);
9073 __ CmpObjectType(rdx, FIRST_NONSTRING_TYPE, r9);
9074 __ j(above_equal, &string_add_runtime);
9075 }
9076
9077 // Both arguments are strings.
9078 // rax: first string
9079 // rdx: second string
9080 // Check if either of the strings are empty. In that case return the other.
9081 Label second_not_zero_length, both_not_zero_length;
9082 __ movl(rcx, FieldOperand(rdx, String::kLengthOffset));
9083 __ testl(rcx, rcx);
9084 __ j(not_zero, &second_not_zero_length);
9085 // Second string is empty, result is first string which is already in rax.
9086 __ IncrementCounter(&Counters::string_add_native, 1);
9087 __ ret(2 * kPointerSize);
9088 __ bind(&second_not_zero_length);
9089 __ movl(rbx, FieldOperand(rax, String::kLengthOffset));
9090 __ testl(rbx, rbx);
9091 __ j(not_zero, &both_not_zero_length);
9092 // First string is empty, result is second string which is in rdx.
9093 __ movq(rax, rdx);
9094 __ IncrementCounter(&Counters::string_add_native, 1);
9095 __ ret(2 * kPointerSize);
9096
9097 // Both strings are non-empty.
9098 // rax: first string
9099 // rbx: length of first string
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009100 // rcx: length of second string
9101 // rdx: second string
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009102 // r8: map of first string if string check was performed above
9103 // r9: map of second string if string check was performed above
9104 Label string_add_flat_result, longer_than_two;
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00009105 __ bind(&both_not_zero_length);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009106
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00009107 // If arguments where known to be strings, maps are not loaded to r8 and r9
9108 // by the code above.
9109 if (!string_check_) {
9110 __ movq(r8, FieldOperand(rax, HeapObject::kMapOffset));
9111 __ movq(r9, FieldOperand(rdx, HeapObject::kMapOffset));
9112 }
9113 // Get the instance types of the two strings as they will be needed soon.
9114 __ movzxbl(r8, FieldOperand(r8, Map::kInstanceTypeOffset));
9115 __ movzxbl(r9, FieldOperand(r9, Map::kInstanceTypeOffset));
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009116
9117 // Look at the length of the result of adding the two strings.
9118 __ addl(rbx, rcx);
9119 // Use the runtime system when adding two one character strings, as it
9120 // contains optimizations for this specific case using the symbol table.
9121 __ cmpl(rbx, Immediate(2));
9122 __ j(not_equal, &longer_than_two);
9123
9124 // Check that both strings are non-external ascii strings.
9125 __ JumpIfBothInstanceTypesAreNotSequentialAscii(r8, r9, rbx, rcx,
9126 &string_add_runtime);
9127
9128 // Get the two characters forming the sub string.
9129 __ movzxbq(rbx, FieldOperand(rax, SeqAsciiString::kHeaderSize));
9130 __ movzxbq(rcx, FieldOperand(rdx, SeqAsciiString::kHeaderSize));
9131
9132 // Try to lookup two character string in symbol table. If it is not found
9133 // just allocate a new one.
9134 Label make_two_character_string, make_flat_ascii_string;
9135 GenerateTwoCharacterSymbolTableProbe(masm, rbx, rcx, r14, r12, rdi, r15,
9136 &make_two_character_string);
9137 __ IncrementCounter(&Counters::string_add_native, 1);
9138 __ ret(2 * kPointerSize);
9139
9140 __ bind(&make_two_character_string);
9141 __ Set(rbx, 2);
9142 __ jmp(&make_flat_ascii_string);
9143
9144 __ bind(&longer_than_two);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00009145 // Check if resulting string will be flat.
9146 __ cmpl(rbx, Immediate(String::kMinNonFlatLength));
9147 __ j(below, &string_add_flat_result);
9148 // Handle exceptionally long strings in the runtime system.
9149 ASSERT((String::kMaxLength & 0x80000000) == 0);
9150 __ cmpl(rbx, Immediate(String::kMaxLength));
9151 __ j(above, &string_add_runtime);
9152
9153 // If result is not supposed to be flat, allocate a cons string object. If
9154 // both strings are ascii the result is an ascii cons string.
9155 // rax: first string
9156 // ebx: length of resulting flat string
9157 // rdx: second string
9158 // r8: instance type of first string
9159 // r9: instance type of second string
9160 Label non_ascii, allocated;
9161 __ movl(rcx, r8);
9162 __ and_(rcx, r9);
9163 ASSERT(kStringEncodingMask == kAsciiStringTag);
9164 __ testl(rcx, Immediate(kAsciiStringTag));
9165 __ j(zero, &non_ascii);
9166 // Allocate an acsii cons string.
9167 __ AllocateAsciiConsString(rcx, rdi, no_reg, &string_add_runtime);
9168 __ bind(&allocated);
9169 // Fill the fields of the cons string.
9170 __ movl(FieldOperand(rcx, ConsString::kLengthOffset), rbx);
9171 __ movl(FieldOperand(rcx, ConsString::kHashFieldOffset),
9172 Immediate(String::kEmptyHashField));
9173 __ movq(FieldOperand(rcx, ConsString::kFirstOffset), rax);
9174 __ movq(FieldOperand(rcx, ConsString::kSecondOffset), rdx);
9175 __ movq(rax, rcx);
9176 __ IncrementCounter(&Counters::string_add_native, 1);
9177 __ ret(2 * kPointerSize);
9178 __ bind(&non_ascii);
9179 // Allocate a two byte cons string.
9180 __ AllocateConsString(rcx, rdi, no_reg, &string_add_runtime);
9181 __ jmp(&allocated);
9182
9183 // Handle creating a flat result. First check that both strings are not
9184 // external strings.
9185 // rax: first string
9186 // ebx: length of resulting flat string
9187 // rdx: second string
9188 // r8: instance type of first string
9189 // r9: instance type of first string
9190 __ bind(&string_add_flat_result);
9191 __ movl(rcx, r8);
9192 __ and_(rcx, Immediate(kStringRepresentationMask));
9193 __ cmpl(rcx, Immediate(kExternalStringTag));
9194 __ j(equal, &string_add_runtime);
9195 __ movl(rcx, r9);
9196 __ and_(rcx, Immediate(kStringRepresentationMask));
9197 __ cmpl(rcx, Immediate(kExternalStringTag));
9198 __ j(equal, &string_add_runtime);
9199 // Now check if both strings are ascii strings.
9200 // rax: first string
9201 // ebx: length of resulting flat string
9202 // rdx: second string
9203 // r8: instance type of first string
9204 // r9: instance type of second string
9205 Label non_ascii_string_add_flat_result;
9206 ASSERT(kStringEncodingMask == kAsciiStringTag);
9207 __ testl(r8, Immediate(kAsciiStringTag));
9208 __ j(zero, &non_ascii_string_add_flat_result);
9209 __ testl(r9, Immediate(kAsciiStringTag));
9210 __ j(zero, &string_add_runtime);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009211
9212 __ bind(&make_flat_ascii_string);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00009213 // Both strings are ascii strings. As they are short they are both flat.
9214 __ AllocateAsciiString(rcx, rbx, rdi, r14, r15, &string_add_runtime);
9215 // rcx: result string
9216 __ movq(rbx, rcx);
9217 // Locate first character of result.
9218 __ addq(rcx, Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag));
9219 // Locate first character of first argument
9220 __ movl(rdi, FieldOperand(rax, String::kLengthOffset));
9221 __ addq(rax, Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag));
9222 // rax: first char of first argument
9223 // rbx: result string
9224 // rcx: first character of result
9225 // rdx: second string
9226 // rdi: length of first argument
9227 GenerateCopyCharacters(masm, rcx, rax, rdi, true);
9228 // Locate first character of second argument.
9229 __ movl(rdi, FieldOperand(rdx, String::kLengthOffset));
9230 __ addq(rdx, Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag));
9231 // rbx: result string
9232 // rcx: next character of result
9233 // rdx: first char of second argument
9234 // rdi: length of second argument
9235 GenerateCopyCharacters(masm, rcx, rdx, rdi, true);
9236 __ movq(rax, rbx);
9237 __ IncrementCounter(&Counters::string_add_native, 1);
9238 __ ret(2 * kPointerSize);
9239
9240 // Handle creating a flat two byte result.
9241 // rax: first string - known to be two byte
9242 // rbx: length of resulting flat string
9243 // rdx: second string
9244 // r8: instance type of first string
9245 // r9: instance type of first string
9246 __ bind(&non_ascii_string_add_flat_result);
9247 __ and_(r9, Immediate(kAsciiStringTag));
9248 __ j(not_zero, &string_add_runtime);
9249 // Both strings are two byte strings. As they are short they are both
9250 // flat.
9251 __ AllocateTwoByteString(rcx, rbx, rdi, r14, r15, &string_add_runtime);
9252 // rcx: result string
9253 __ movq(rbx, rcx);
9254 // Locate first character of result.
9255 __ addq(rcx, Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
9256 // Locate first character of first argument.
9257 __ movl(rdi, FieldOperand(rax, String::kLengthOffset));
9258 __ addq(rax, Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
9259 // rax: first char of first argument
9260 // rbx: result string
9261 // rcx: first character of result
9262 // rdx: second argument
9263 // rdi: length of first argument
9264 GenerateCopyCharacters(masm, rcx, rax, rdi, false);
9265 // Locate first character of second argument.
9266 __ movl(rdi, FieldOperand(rdx, String::kLengthOffset));
9267 __ addq(rdx, Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
9268 // rbx: result string
9269 // rcx: next character of result
9270 // rdx: first char of second argument
9271 // rdi: length of second argument
9272 GenerateCopyCharacters(masm, rcx, rdx, rdi, false);
9273 __ movq(rax, rbx);
9274 __ IncrementCounter(&Counters::string_add_native, 1);
9275 __ ret(2 * kPointerSize);
9276
9277 // Just jump to runtime to add the two strings.
9278 __ bind(&string_add_runtime);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009279 __ TailCallRuntime(Runtime::kStringAdd, 2, 1);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00009280}
9281
9282
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009283void StringStubBase::GenerateCopyCharacters(MacroAssembler* masm,
9284 Register dest,
9285 Register src,
9286 Register count,
9287 bool ascii) {
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00009288 Label loop;
9289 __ bind(&loop);
9290 // This loop just copies one character at a time, as it is only used for very
9291 // short strings.
9292 if (ascii) {
9293 __ movb(kScratchRegister, Operand(src, 0));
9294 __ movb(Operand(dest, 0), kScratchRegister);
9295 __ addq(src, Immediate(1));
9296 __ addq(dest, Immediate(1));
9297 } else {
9298 __ movzxwl(kScratchRegister, Operand(src, 0));
9299 __ movw(Operand(dest, 0), kScratchRegister);
9300 __ addq(src, Immediate(2));
9301 __ addq(dest, Immediate(2));
9302 }
9303 __ subl(count, Immediate(1));
9304 __ j(not_zero, &loop);
9305}
9306
9307
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009308void StringStubBase::GenerateCopyCharactersREP(MacroAssembler* masm,
9309 Register dest,
9310 Register src,
9311 Register count,
9312 bool ascii) {
9313 // Copy characters using rep movs of doublewords. Align destination on 4 byte
9314 // boundary before starting rep movs. Copy remaining characters after running
9315 // rep movs.
9316 ASSERT(dest.is(rdi)); // rep movs destination
9317 ASSERT(src.is(rsi)); // rep movs source
9318 ASSERT(count.is(rcx)); // rep movs count
9319
9320 // Nothing to do for zero characters.
9321 Label done;
9322 __ testq(count, count);
9323 __ j(zero, &done);
9324
9325 // Make count the number of bytes to copy.
9326 if (!ascii) {
9327 ASSERT_EQ(2, sizeof(uc16)); // NOLINT
9328 __ addq(count, count);
9329 }
9330
9331 // Don't enter the rep movs if there are less than 4 bytes to copy.
9332 Label last_bytes;
9333 __ testq(count, Immediate(~7));
9334 __ j(zero, &last_bytes);
9335
9336 // Copy from edi to esi using rep movs instruction.
9337 __ movq(kScratchRegister, count);
9338 __ sar(count, Immediate(3)); // Number of doublewords to copy.
9339 __ repmovsq();
9340
9341 // Find number of bytes left.
9342 __ movq(count, kScratchRegister);
9343 __ and_(count, Immediate(7));
9344
9345 // Check if there are more bytes to copy.
9346 __ bind(&last_bytes);
9347 __ testq(count, count);
9348 __ j(zero, &done);
9349
9350 // Copy remaining characters.
9351 Label loop;
9352 __ bind(&loop);
9353 __ movb(kScratchRegister, Operand(src, 0));
9354 __ movb(Operand(dest, 0), kScratchRegister);
9355 __ addq(src, Immediate(1));
9356 __ addq(dest, Immediate(1));
9357 __ subq(count, Immediate(1));
9358 __ j(not_zero, &loop);
9359
9360 __ bind(&done);
9361}
9362
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009363void StringStubBase::GenerateTwoCharacterSymbolTableProbe(MacroAssembler* masm,
9364 Register c1,
9365 Register c2,
9366 Register scratch1,
9367 Register scratch2,
9368 Register scratch3,
9369 Register scratch4,
9370 Label* not_found) {
9371 // Register scratch3 is the general scratch register in this function.
9372 Register scratch = scratch3;
9373
9374 // Make sure that both characters are not digits as such strings has a
9375 // different hash algorithm. Don't try to look for these in the symbol table.
9376 Label not_array_index;
9377 __ movq(scratch, c1);
9378 __ subq(scratch, Immediate(static_cast<int>('0')));
9379 __ cmpq(scratch, Immediate(static_cast<int>('9' - '0')));
9380 __ j(above, &not_array_index);
9381 __ movq(scratch, c2);
9382 __ subq(scratch, Immediate(static_cast<int>('0')));
9383 __ cmpq(scratch, Immediate(static_cast<int>('9' - '0')));
9384 __ j(below_equal, not_found);
9385
9386 __ bind(&not_array_index);
9387 // Calculate the two character string hash.
9388 Register hash = scratch1;
9389 GenerateHashInit(masm, hash, c1, scratch);
9390 GenerateHashAddCharacter(masm, hash, c2, scratch);
9391 GenerateHashGetHash(masm, hash, scratch);
9392
9393 // Collect the two characters in a register.
9394 Register chars = c1;
9395 __ shl(c2, Immediate(kBitsPerByte));
9396 __ orl(chars, c2);
9397
9398 // chars: two character string, char 1 in byte 0 and char 2 in byte 1.
9399 // hash: hash of two character string.
9400
9401 // Load the symbol table.
9402 Register symbol_table = c2;
9403 __ LoadRoot(symbol_table, Heap::kSymbolTableRootIndex);
9404
9405 // Calculate capacity mask from the symbol table capacity.
9406 Register mask = scratch2;
9407 __ movq(mask, FieldOperand(symbol_table, SymbolTable::kCapacityOffset));
9408 __ SmiToInteger32(mask, mask);
9409 __ decl(mask);
9410
9411 Register undefined = scratch4;
9412 __ LoadRoot(undefined, Heap::kUndefinedValueRootIndex);
9413
9414 // Registers
9415 // chars: two character string, char 1 in byte 0 and char 2 in byte 1.
9416 // hash: hash of two character string (32-bit int)
9417 // symbol_table: symbol table
9418 // mask: capacity mask (32-bit int)
9419 // undefined: undefined value
9420 // scratch: -
9421
9422 // Perform a number of probes in the symbol table.
9423 static const int kProbes = 4;
9424 Label found_in_symbol_table;
9425 Label next_probe[kProbes];
9426 for (int i = 0; i < kProbes; i++) {
9427 // Calculate entry in symbol table.
9428 __ movl(scratch, hash);
9429 if (i > 0) {
9430 __ addl(scratch, Immediate(SymbolTable::GetProbeOffset(i)));
9431 }
9432 __ andl(scratch, mask);
9433
9434 // Load the entry from the symble table.
9435 Register candidate = scratch; // Scratch register contains candidate.
9436 ASSERT_EQ(1, SymbolTable::kEntrySize);
9437 __ movq(candidate,
9438 FieldOperand(symbol_table,
9439 scratch,
9440 times_pointer_size,
9441 SymbolTable::kElementsStartOffset));
9442
9443 // If entry is undefined no string with this hash can be found.
9444 __ cmpq(candidate, undefined);
9445 __ j(equal, not_found);
9446
9447 // If length is not 2 the string is not a candidate.
9448 __ cmpl(FieldOperand(candidate, String::kLengthOffset), Immediate(2));
9449 __ j(not_equal, &next_probe[i]);
9450
9451 // We use kScratchRegister as a temporary register in assumption that
9452 // JumpIfInstanceTypeIsNotSequentialAscii does not use it implicitly
9453 Register temp = kScratchRegister;
9454
9455 // Check that the candidate is a non-external ascii string.
9456 __ movq(temp, FieldOperand(candidate, HeapObject::kMapOffset));
9457 __ movzxbl(temp, FieldOperand(temp, Map::kInstanceTypeOffset));
9458 __ JumpIfInstanceTypeIsNotSequentialAscii(
9459 temp, temp, &next_probe[i]);
9460
9461 // Check if the two characters match.
9462 __ movl(temp, FieldOperand(candidate, SeqAsciiString::kHeaderSize));
9463 __ andl(temp, Immediate(0x0000ffff));
9464 __ cmpl(chars, temp);
9465 __ j(equal, &found_in_symbol_table);
9466 __ bind(&next_probe[i]);
9467 }
9468
9469 // No matching 2 character string found by probing.
9470 __ jmp(not_found);
9471
9472 // Scratch register contains result when we fall through to here.
9473 Register result = scratch;
9474 __ bind(&found_in_symbol_table);
9475 if (!result.is(rax)) {
9476 __ movq(rax, result);
9477 }
9478}
9479
9480
9481void StringStubBase::GenerateHashInit(MacroAssembler* masm,
9482 Register hash,
9483 Register character,
9484 Register scratch) {
9485 // hash = character + (character << 10);
9486 __ movl(hash, character);
9487 __ shll(hash, Immediate(10));
9488 __ addl(hash, character);
9489 // hash ^= hash >> 6;
9490 __ movl(scratch, hash);
9491 __ sarl(scratch, Immediate(6));
9492 __ xorl(hash, scratch);
9493}
9494
9495
9496void StringStubBase::GenerateHashAddCharacter(MacroAssembler* masm,
9497 Register hash,
9498 Register character,
9499 Register scratch) {
9500 // hash += character;
9501 __ addl(hash, character);
9502 // hash += hash << 10;
9503 __ movl(scratch, hash);
9504 __ shll(scratch, Immediate(10));
9505 __ addl(hash, scratch);
9506 // hash ^= hash >> 6;
9507 __ movl(scratch, hash);
9508 __ sarl(scratch, Immediate(6));
9509 __ xorl(hash, scratch);
9510}
9511
9512
9513void StringStubBase::GenerateHashGetHash(MacroAssembler* masm,
9514 Register hash,
9515 Register scratch) {
9516 // hash += hash << 3;
9517 __ movl(scratch, hash);
9518 __ shll(scratch, Immediate(3));
9519 __ addl(hash, scratch);
9520 // hash ^= hash >> 11;
9521 __ movl(scratch, hash);
9522 __ sarl(scratch, Immediate(11));
9523 __ xorl(hash, scratch);
9524 // hash += hash << 15;
9525 __ movl(scratch, hash);
9526 __ shll(scratch, Immediate(15));
9527 __ addl(hash, scratch);
9528
9529 // if (hash == 0) hash = 27;
9530 Label hash_not_zero;
9531 __ testl(hash, hash);
9532 __ j(not_zero, &hash_not_zero);
9533 __ movl(hash, Immediate(27));
9534 __ bind(&hash_not_zero);
9535}
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009536
9537void SubStringStub::Generate(MacroAssembler* masm) {
9538 Label runtime;
9539
9540 // Stack frame on entry.
9541 // rsp[0]: return address
9542 // rsp[8]: to
9543 // rsp[16]: from
9544 // rsp[24]: string
9545
9546 const int kToOffset = 1 * kPointerSize;
9547 const int kFromOffset = kToOffset + kPointerSize;
9548 const int kStringOffset = kFromOffset + kPointerSize;
9549 const int kArgumentsSize = (kStringOffset + kPointerSize) - kToOffset;
9550
9551 // Make sure first argument is a string.
9552 __ movq(rax, Operand(rsp, kStringOffset));
9553 ASSERT_EQ(0, kSmiTag);
9554 __ testl(rax, Immediate(kSmiTagMask));
9555 __ j(zero, &runtime);
9556 Condition is_string = masm->IsObjectStringType(rax, rbx, rbx);
9557 __ j(NegateCondition(is_string), &runtime);
9558
9559 // rax: string
9560 // rbx: instance type
9561 // Calculate length of sub string using the smi values.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009562 Label result_longer_than_two;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009563 __ movq(rcx, Operand(rsp, kToOffset));
9564 __ movq(rdx, Operand(rsp, kFromOffset));
9565 __ JumpIfNotBothPositiveSmi(rcx, rdx, &runtime);
9566
9567 __ SmiSub(rcx, rcx, rdx, NULL); // Overflow doesn't happen.
9568 __ j(negative, &runtime);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009569 // Special handling of sub-strings of length 1 and 2. One character strings
9570 // are handled in the runtime system (looked up in the single character
9571 // cache). Two character strings are looked for in the symbol cache.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009572 __ SmiToInteger32(rcx, rcx);
9573 __ cmpl(rcx, Immediate(2));
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009574 __ j(greater, &result_longer_than_two);
9575 __ j(less, &runtime);
9576
9577 // Sub string of length 2 requested.
9578 // rax: string
9579 // rbx: instance type
9580 // rcx: sub string length (value is 2)
9581 // rdx: from index (smi)
9582 __ JumpIfInstanceTypeIsNotSequentialAscii(rbx, rbx, &runtime);
9583
9584 // Get the two characters forming the sub string.
9585 __ SmiToInteger32(rdx, rdx); // From index is no longer smi.
9586 __ movzxbq(rbx, FieldOperand(rax, rdx, times_1, SeqAsciiString::kHeaderSize));
9587 __ movzxbq(rcx,
9588 FieldOperand(rax, rdx, times_1, SeqAsciiString::kHeaderSize + 1));
9589
9590 // Try to lookup two character string in symbol table.
9591 Label make_two_character_string;
9592 GenerateTwoCharacterSymbolTableProbe(masm, rbx, rcx, rax, rdx, rdi, r14,
9593 &make_two_character_string);
9594 __ ret(3 * kPointerSize);
9595
9596 __ bind(&make_two_character_string);
9597 // Setup registers for allocating the two character string.
9598 __ movq(rax, Operand(rsp, kStringOffset));
9599 __ movq(rbx, FieldOperand(rax, HeapObject::kMapOffset));
9600 __ movzxbl(rbx, FieldOperand(rbx, Map::kInstanceTypeOffset));
9601 __ Set(rcx, 2);
9602
9603 __ bind(&result_longer_than_two);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009604
9605 // rax: string
9606 // rbx: instance type
9607 // rcx: result string length
9608 // Check for flat ascii string
9609 Label non_ascii_flat;
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009610 __ JumpIfInstanceTypeIsNotSequentialAscii(rbx, rbx, &non_ascii_flat);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009611
9612 // Allocate the result.
9613 __ AllocateAsciiString(rax, rcx, rbx, rdx, rdi, &runtime);
9614
9615 // rax: result string
9616 // rcx: result string length
9617 __ movq(rdx, rsi); // esi used by following code.
9618 // Locate first character of result.
9619 __ lea(rdi, FieldOperand(rax, SeqAsciiString::kHeaderSize));
9620 // Load string argument and locate character of sub string start.
9621 __ movq(rsi, Operand(rsp, kStringOffset));
9622 __ movq(rbx, Operand(rsp, kFromOffset));
9623 {
9624 SmiIndex smi_as_index = masm->SmiToIndex(rbx, rbx, times_1);
9625 __ lea(rsi, Operand(rsi, smi_as_index.reg, smi_as_index.scale,
9626 SeqAsciiString::kHeaderSize - kHeapObjectTag));
9627 }
9628
9629 // rax: result string
9630 // rcx: result length
9631 // rdx: original value of rsi
9632 // rdi: first character of result
9633 // rsi: character of sub string start
9634 GenerateCopyCharactersREP(masm, rdi, rsi, rcx, true);
9635 __ movq(rsi, rdx); // Restore rsi.
9636 __ IncrementCounter(&Counters::sub_string_native, 1);
9637 __ ret(kArgumentsSize);
9638
9639 __ bind(&non_ascii_flat);
9640 // rax: string
9641 // rbx: instance type & kStringRepresentationMask | kStringEncodingMask
9642 // rcx: result string length
9643 // Check for sequential two byte string
9644 __ cmpb(rbx, Immediate(kSeqStringTag | kTwoByteStringTag));
9645 __ j(not_equal, &runtime);
9646
9647 // Allocate the result.
9648 __ AllocateTwoByteString(rax, rcx, rbx, rdx, rdi, &runtime);
9649
9650 // rax: result string
9651 // rcx: result string length
9652 __ movq(rdx, rsi); // esi used by following code.
9653 // Locate first character of result.
9654 __ lea(rdi, FieldOperand(rax, SeqTwoByteString::kHeaderSize));
9655 // Load string argument and locate character of sub string start.
9656 __ movq(rsi, Operand(rsp, kStringOffset));
9657 __ movq(rbx, Operand(rsp, kFromOffset));
9658 {
9659 SmiIndex smi_as_index = masm->SmiToIndex(rbx, rbx, times_2);
9660 __ lea(rsi, Operand(rsi, smi_as_index.reg, smi_as_index.scale,
9661 SeqAsciiString::kHeaderSize - kHeapObjectTag));
9662 }
9663
9664 // rax: result string
9665 // rcx: result length
9666 // rdx: original value of rsi
9667 // rdi: first character of result
9668 // rsi: character of sub string start
9669 GenerateCopyCharactersREP(masm, rdi, rsi, rcx, false);
9670 __ movq(rsi, rdx); // Restore esi.
9671 __ IncrementCounter(&Counters::sub_string_native, 1);
9672 __ ret(kArgumentsSize);
9673
9674 // Just jump to runtime to create the sub string.
9675 __ bind(&runtime);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009676 __ TailCallRuntime(Runtime::kSubString, 3, 1);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009677}
9678
9679
9680void StringCompareStub::GenerateCompareFlatAsciiStrings(MacroAssembler* masm,
9681 Register left,
9682 Register right,
9683 Register scratch1,
9684 Register scratch2,
9685 Register scratch3,
9686 Register scratch4) {
9687 // Ensure that you can always subtract a string length from a non-negative
9688 // number (e.g. another length).
9689 ASSERT(String::kMaxLength < 0x7fffffff);
9690
9691 // Find minimum length and length difference.
9692 __ movl(scratch1, FieldOperand(left, String::kLengthOffset));
9693 __ movl(scratch4, scratch1);
9694 __ subl(scratch4, FieldOperand(right, String::kLengthOffset));
9695 // Register scratch4 now holds left.length - right.length.
9696 const Register length_difference = scratch4;
9697 Label left_shorter;
9698 __ j(less, &left_shorter);
9699 // The right string isn't longer that the left one.
9700 // Get the right string's length by subtracting the (non-negative) difference
9701 // from the left string's length.
9702 __ subl(scratch1, length_difference);
9703 __ bind(&left_shorter);
9704 // Register scratch1 now holds Min(left.length, right.length).
9705 const Register min_length = scratch1;
9706
9707 Label compare_lengths;
9708 // If min-length is zero, go directly to comparing lengths.
9709 __ testl(min_length, min_length);
9710 __ j(zero, &compare_lengths);
9711
9712 // Registers scratch2 and scratch3 are free.
9713 Label result_not_equal;
9714 Label loop;
9715 {
9716 // Check characters 0 .. min_length - 1 in a loop.
9717 // Use scratch3 as loop index, min_length as limit and scratch2
9718 // for computation.
9719 const Register index = scratch3;
9720 __ movl(index, Immediate(0)); // Index into strings.
9721 __ bind(&loop);
9722 // Compare characters.
9723 // TODO(lrn): Could we load more than one character at a time?
9724 __ movb(scratch2, FieldOperand(left,
9725 index,
9726 times_1,
9727 SeqAsciiString::kHeaderSize));
9728 // Increment index and use -1 modifier on next load to give
9729 // the previous load extra time to complete.
9730 __ addl(index, Immediate(1));
9731 __ cmpb(scratch2, FieldOperand(right,
9732 index,
9733 times_1,
9734 SeqAsciiString::kHeaderSize - 1));
9735 __ j(not_equal, &result_not_equal);
9736 __ cmpl(index, min_length);
9737 __ j(not_equal, &loop);
9738 }
9739 // Completed loop without finding different characters.
9740 // Compare lengths (precomputed).
9741 __ bind(&compare_lengths);
9742 __ testl(length_difference, length_difference);
9743 __ j(not_zero, &result_not_equal);
9744
9745 // Result is EQUAL.
9746 __ Move(rax, Smi::FromInt(EQUAL));
9747 __ ret(2 * kPointerSize);
9748
9749 Label result_greater;
9750 __ bind(&result_not_equal);
9751 // Unequal comparison of left to right, either character or length.
9752 __ j(greater, &result_greater);
9753
9754 // Result is LESS.
9755 __ Move(rax, Smi::FromInt(LESS));
9756 __ ret(2 * kPointerSize);
9757
9758 // Result is GREATER.
9759 __ bind(&result_greater);
9760 __ Move(rax, Smi::FromInt(GREATER));
9761 __ ret(2 * kPointerSize);
9762}
9763
9764
9765void StringCompareStub::Generate(MacroAssembler* masm) {
9766 Label runtime;
9767
9768 // Stack frame on entry.
9769 // rsp[0]: return address
9770 // rsp[8]: right string
9771 // rsp[16]: left string
9772
9773 __ movq(rdx, Operand(rsp, 2 * kPointerSize)); // left
9774 __ movq(rax, Operand(rsp, 1 * kPointerSize)); // right
9775
9776 // Check for identity.
9777 Label not_same;
9778 __ cmpq(rdx, rax);
9779 __ j(not_equal, &not_same);
9780 __ Move(rax, Smi::FromInt(EQUAL));
9781 __ IncrementCounter(&Counters::string_compare_native, 1);
9782 __ ret(2 * kPointerSize);
9783
9784 __ bind(&not_same);
9785
9786 // Check that both are sequential ASCII strings.
9787 __ JumpIfNotBothSequentialAsciiStrings(rdx, rax, rcx, rbx, &runtime);
9788
9789 // Inline comparison of ascii strings.
9790 __ IncrementCounter(&Counters::string_compare_native, 1);
9791 GenerateCompareFlatAsciiStrings(masm, rdx, rax, rcx, rbx, rdi, r8);
9792
9793 // Call the runtime; it returns -1 (less), 0 (equal), or 1 (greater)
9794 // tagged as a small integer.
9795 __ bind(&runtime);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009796 __ TailCallRuntime(Runtime::kStringCompare, 2, 1);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009797}
9798
ager@chromium.org3811b432009-10-28 14:53:37 +00009799#undef __
9800
9801#define __ masm.
9802
9803#ifdef _WIN64
9804typedef double (*ModuloFunction)(double, double);
9805// Define custom fmod implementation.
9806ModuloFunction CreateModuloFunction() {
9807 size_t actual_size;
9808 byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
9809 &actual_size,
9810 true));
9811 CHECK(buffer);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00009812 Assembler masm(buffer, static_cast<int>(actual_size));
ager@chromium.org3811b432009-10-28 14:53:37 +00009813 // Generated code is put into a fixed, unmovable, buffer, and not into
9814 // the V8 heap. We can't, and don't, refer to any relocatable addresses
9815 // (e.g. the JavaScript nan-object).
9816
9817 // Windows 64 ABI passes double arguments in xmm0, xmm1 and
9818 // returns result in xmm0.
9819 // Argument backing space is allocated on the stack above
9820 // the return address.
9821
9822 // Compute x mod y.
9823 // Load y and x (use argument backing store as temporary storage).
9824 __ movsd(Operand(rsp, kPointerSize * 2), xmm1);
9825 __ movsd(Operand(rsp, kPointerSize), xmm0);
9826 __ fld_d(Operand(rsp, kPointerSize * 2));
9827 __ fld_d(Operand(rsp, kPointerSize));
9828
9829 // Clear exception flags before operation.
9830 {
9831 Label no_exceptions;
9832 __ fwait();
9833 __ fnstsw_ax();
9834 // Clear if Illegal Operand or Zero Division exceptions are set.
9835 __ testb(rax, Immediate(5));
9836 __ j(zero, &no_exceptions);
9837 __ fnclex();
9838 __ bind(&no_exceptions);
9839 }
9840
9841 // Compute st(0) % st(1)
9842 {
9843 Label partial_remainder_loop;
9844 __ bind(&partial_remainder_loop);
9845 __ fprem();
9846 __ fwait();
9847 __ fnstsw_ax();
9848 __ testl(rax, Immediate(0x400 /* C2 */));
9849 // If C2 is set, computation only has partial result. Loop to
9850 // continue computation.
9851 __ j(not_zero, &partial_remainder_loop);
9852 }
9853
9854 Label valid_result;
9855 Label return_result;
9856 // If Invalid Operand or Zero Division exceptions are set,
9857 // return NaN.
9858 __ testb(rax, Immediate(5));
9859 __ j(zero, &valid_result);
9860 __ fstp(0); // Drop result in st(0).
9861 int64_t kNaNValue = V8_INT64_C(0x7ff8000000000000);
9862 __ movq(rcx, kNaNValue, RelocInfo::NONE);
9863 __ movq(Operand(rsp, kPointerSize), rcx);
9864 __ movsd(xmm0, Operand(rsp, kPointerSize));
9865 __ jmp(&return_result);
9866
9867 // If result is valid, return that.
9868 __ bind(&valid_result);
9869 __ fstp_d(Operand(rsp, kPointerSize));
9870 __ movsd(xmm0, Operand(rsp, kPointerSize));
9871
9872 // Clean up FPU stack and exceptions and return xmm0
9873 __ bind(&return_result);
9874 __ fstp(0); // Unload y.
9875
9876 Label clear_exceptions;
9877 __ testb(rax, Immediate(0x3f /* Any Exception*/));
9878 __ j(not_zero, &clear_exceptions);
9879 __ ret(0);
9880 __ bind(&clear_exceptions);
9881 __ fnclex();
9882 __ ret(0);
9883
9884 CodeDesc desc;
9885 masm.GetCode(&desc);
9886 // Call the function from C++.
9887 return FUNCTION_CAST<ModuloFunction>(buffer);
9888}
9889
9890#endif
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009891
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009892
ager@chromium.orge2902be2009-06-08 12:21:35 +00009893#undef __
kasperl@chromium.org71affb52009-05-26 05:44:31 +00009894
9895} } // namespace v8::internal