blob: 6467734abe79a8a883b40d259f609b25829e6cd3 [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
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +000030#if defined(V8_TARGET_ARCH_X64)
31
ager@chromium.org5aa501c2009-06-23 07:57:28 +000032#include "bootstrapper.h"
33#include "codegen-inl.h"
ager@chromium.orgc4c92722009-11-18 14:12:51 +000034#include "compiler.h"
ager@chromium.org5aa501c2009-06-23 07:57:28 +000035#include "debug.h"
36#include "ic-inl.h"
37#include "parser.h"
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000038#include "regexp-macro-assembler.h"
kasperl@chromium.org71affb52009-05-26 05:44:31 +000039#include "register-allocator-inl.h"
ager@chromium.org5aa501c2009-06-23 07:57:28 +000040#include "scopes.h"
sgjesse@chromium.org833cdd72010-02-26 10:06:16 +000041#include "virtual-frame-inl.h"
ager@chromium.org5aa501c2009-06-23 07:57:28 +000042
kasperl@chromium.org71affb52009-05-26 05:44:31 +000043namespace v8 {
44namespace internal {
45
ricow@chromium.org30ce4112010-05-31 10:38:25 +000046#define __ ACCESS_MASM(masm)
ager@chromium.org5aa501c2009-06-23 07:57:28 +000047
ager@chromium.orge2902be2009-06-08 12:21:35 +000048// -------------------------------------------------------------------------
ricow@chromium.org30ce4112010-05-31 10:38:25 +000049// Platform-specific FrameRegisterState functions.
ager@chromium.orge2902be2009-06-08 12:21:35 +000050
ricow@chromium.org30ce4112010-05-31 10:38:25 +000051void FrameRegisterState::Save(MacroAssembler* masm) const {
ager@chromium.org5aa501c2009-06-23 07:57:28 +000052 for (int i = 0; i < RegisterAllocator::kNumRegisters; i++) {
53 int action = registers_[i];
54 if (action == kPush) {
55 __ push(RegisterAllocator::ToRegister(i));
56 } else if (action != kIgnore && (action & kSyncedFlag) == 0) {
57 __ movq(Operand(rbp, action), RegisterAllocator::ToRegister(i));
58 }
59 }
60}
ager@chromium.orge2902be2009-06-08 12:21:35 +000061
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +000062
ricow@chromium.org30ce4112010-05-31 10:38:25 +000063void FrameRegisterState::Restore(MacroAssembler* masm) const {
ager@chromium.org5aa501c2009-06-23 07:57:28 +000064 // Restore registers in reverse order due to the stack.
65 for (int i = RegisterAllocator::kNumRegisters - 1; i >= 0; i--) {
66 int action = registers_[i];
67 if (action == kPush) {
68 __ pop(RegisterAllocator::ToRegister(i));
69 } else if (action != kIgnore) {
70 action &= ~kSyncedFlag;
71 __ movq(RegisterAllocator::ToRegister(i), Operand(rbp, action));
72 }
73 }
74}
75
ager@chromium.orge2902be2009-06-08 12:21:35 +000076
ricow@chromium.org30ce4112010-05-31 10:38:25 +000077#undef __
78#define __ ACCESS_MASM(masm_)
79
80// -------------------------------------------------------------------------
81// Platform-specific DeferredCode functions.
82
83void DeferredCode::SaveRegisters() {
84 frame_state_.Save(masm_);
85}
86
87
88void DeferredCode::RestoreRegisters() {
89 frame_state_.Restore(masm_);
90}
91
92
93// -------------------------------------------------------------------------
94// Platform-specific RuntimeCallHelper functions.
95
96void VirtualFrameRuntimeCallHelper::BeforeCall(MacroAssembler* masm) const {
97 frame_state_->Save(masm);
98}
99
100
101void VirtualFrameRuntimeCallHelper::AfterCall(MacroAssembler* masm) const {
102 frame_state_->Restore(masm);
103}
104
105
106void ICRuntimeCallHelper::BeforeCall(MacroAssembler* masm) const {
107 masm->EnterInternalFrame();
108}
109
110
111void ICRuntimeCallHelper::AfterCall(MacroAssembler* masm) const {
112 masm->LeaveInternalFrame();
113}
114
115
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000116// -------------------------------------------------------------------------
117// CodeGenState implementation.
118
119CodeGenState::CodeGenState(CodeGenerator* owner)
120 : owner_(owner),
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000121 destination_(NULL),
122 previous_(NULL) {
123 owner_->set_state(this);
124}
125
126
127CodeGenState::CodeGenState(CodeGenerator* owner,
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000128 ControlDestination* destination)
129 : owner_(owner),
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000130 destination_(destination),
131 previous_(owner->state()) {
132 owner_->set_state(this);
133}
134
135
136CodeGenState::~CodeGenState() {
137 ASSERT(owner_->state() == this);
138 owner_->set_state(previous_);
139}
140
141
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +0000142// -------------------------------------------------------------------------
143// Deferred code objects
144//
145// These subclasses of DeferredCode add pieces of code to the end of generated
146// code. They are branched to from the generated code, and
147// keep some slower code out of the main body of the generated code.
148// Many of them call a code stub or a runtime function.
149
150class DeferredInlineSmiAdd: public DeferredCode {
151 public:
152 DeferredInlineSmiAdd(Register dst,
153 Smi* value,
154 OverwriteMode overwrite_mode)
155 : dst_(dst), value_(value), overwrite_mode_(overwrite_mode) {
156 set_comment("[ DeferredInlineSmiAdd");
157 }
158
159 virtual void Generate();
160
161 private:
162 Register dst_;
163 Smi* value_;
164 OverwriteMode overwrite_mode_;
165};
166
167
168// The result of value + src is in dst. It either overflowed or was not
169// smi tagged. Undo the speculative addition and call the appropriate
170// specialized stub for add. The result is left in dst.
171class DeferredInlineSmiAddReversed: public DeferredCode {
172 public:
173 DeferredInlineSmiAddReversed(Register dst,
174 Smi* value,
175 OverwriteMode overwrite_mode)
176 : dst_(dst), value_(value), overwrite_mode_(overwrite_mode) {
177 set_comment("[ DeferredInlineSmiAddReversed");
178 }
179
180 virtual void Generate();
181
182 private:
183 Register dst_;
184 Smi* value_;
185 OverwriteMode overwrite_mode_;
186};
187
188
189class DeferredInlineSmiSub: public DeferredCode {
190 public:
191 DeferredInlineSmiSub(Register dst,
192 Smi* value,
193 OverwriteMode overwrite_mode)
194 : dst_(dst), value_(value), overwrite_mode_(overwrite_mode) {
195 set_comment("[ DeferredInlineSmiSub");
196 }
197
198 virtual void Generate();
199
200 private:
201 Register dst_;
202 Smi* value_;
203 OverwriteMode overwrite_mode_;
204};
205
206
207// Call the appropriate binary operation stub to compute src op value
208// and leave the result in dst.
209class DeferredInlineSmiOperation: public DeferredCode {
210 public:
211 DeferredInlineSmiOperation(Token::Value op,
212 Register dst,
213 Register src,
214 Smi* value,
215 OverwriteMode overwrite_mode)
216 : op_(op),
217 dst_(dst),
218 src_(src),
219 value_(value),
220 overwrite_mode_(overwrite_mode) {
221 set_comment("[ DeferredInlineSmiOperation");
222 }
223
224 virtual void Generate();
225
226 private:
227 Token::Value op_;
228 Register dst_;
229 Register src_;
230 Smi* value_;
231 OverwriteMode overwrite_mode_;
232};
233
234
ager@chromium.orgac091b72010-05-05 07:34:42 +0000235// Call the appropriate binary operation stub to compute value op src
236// and leave the result in dst.
237class DeferredInlineSmiOperationReversed: public DeferredCode {
238 public:
239 DeferredInlineSmiOperationReversed(Token::Value op,
240 Register dst,
241 Smi* value,
242 Register src,
243 OverwriteMode overwrite_mode)
244 : op_(op),
245 dst_(dst),
246 value_(value),
247 src_(src),
248 overwrite_mode_(overwrite_mode) {
249 set_comment("[ DeferredInlineSmiOperationReversed");
250 }
251
252 virtual void Generate();
253
254 private:
255 Token::Value op_;
256 Register dst_;
257 Smi* value_;
258 Register src_;
259 OverwriteMode overwrite_mode_;
260};
261
262
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +0000263class FloatingPointHelper : public AllStatic {
264 public:
265 // Code pattern for loading a floating point value. Input value must
266 // be either a smi or a heap number object (fp value). Requirements:
267 // operand on TOS+1. Returns operand as floating point number on FPU
268 // stack.
269 static void LoadFloatOperand(MacroAssembler* masm, Register scratch);
270
271 // Code pattern for loading a floating point value. Input value must
272 // be either a smi or a heap number object (fp value). Requirements:
273 // operand in src register. Returns operand as floating point number
lrn@chromium.orgc34f5802010-04-28 12:53:43 +0000274 // in XMM register. May destroy src register.
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +0000275 static void LoadFloatOperand(MacroAssembler* masm,
276 Register src,
277 XMMRegister dst);
278
lrn@chromium.orgc34f5802010-04-28 12:53:43 +0000279 // Code pattern for loading a possible number into a XMM register.
280 // If the contents of src is not a number, control branches to
281 // the Label not_number. If contents of src is a smi or a heap number
282 // object (fp value), it is loaded into the XMM register as a double.
283 // The register src is not changed, and src may not be kScratchRegister.
284 static void LoadFloatOperand(MacroAssembler* masm,
285 Register src,
286 XMMRegister dst,
287 Label *not_number);
288
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +0000289 // Code pattern for loading floating point values. Input values must
290 // be either smi or heap number objects (fp values). Requirements:
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000291 // operand_1 in rdx, operand_2 in rax; Returns operands as
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +0000292 // floating point numbers in XMM registers.
293 static void LoadFloatOperands(MacroAssembler* masm,
294 XMMRegister dst1,
295 XMMRegister dst2);
296
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000297 // Similar to LoadFloatOperands, assumes that the operands are smis.
298 static void LoadFloatOperandsFromSmis(MacroAssembler* masm,
299 XMMRegister dst1,
300 XMMRegister dst2);
301
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +0000302 // Code pattern for loading floating point values onto the fp stack.
303 // Input values must be either smi or heap number objects (fp values).
304 // Requirements:
305 // Register version: operands in registers lhs and rhs.
306 // Stack version: operands on TOS+1 and TOS+2.
307 // Returns operands as floating point numbers on fp stack.
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +0000308 static void LoadFloatOperands(MacroAssembler* masm,
309 Register lhs,
310 Register rhs);
311
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +0000312 // Test if operands are smi or number objects (fp). Requirements:
313 // operand_1 in rax, operand_2 in rdx; falls through on float or smi
314 // operands, jumps to the non_float label otherwise.
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +0000315 static void CheckNumberOperands(MacroAssembler* masm,
316 Label* non_float);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000317
318 // Takes the operands in rdx and rax and loads them as integers in rax
319 // and rcx.
320 static void LoadAsIntegers(MacroAssembler* masm,
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000321 Label* operand_conversion_failure);
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +0000322};
323
324
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000325// -----------------------------------------------------------------------------
326// CodeGenerator implementation.
ager@chromium.orge2902be2009-06-08 12:21:35 +0000327
ager@chromium.org5c838252010-02-19 08:53:10 +0000328CodeGenerator::CodeGenerator(MacroAssembler* masm)
329 : deferred_(8),
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000330 masm_(masm),
ager@chromium.org5c838252010-02-19 08:53:10 +0000331 info_(NULL),
kasperl@chromium.org71affb52009-05-26 05:44:31 +0000332 frame_(NULL),
333 allocator_(NULL),
334 state_(NULL),
335 loop_nesting_(0),
336 function_return_is_shadowed_(false),
337 in_spilled_code_(false) {
338}
339
ager@chromium.orge2902be2009-06-08 12:21:35 +0000340
ager@chromium.org3e875802009-06-29 08:26:34 +0000341void CodeGenerator::DeclareGlobals(Handle<FixedArray> pairs) {
342 // Call the runtime to declare the globals. The inevitable call
343 // will sync frame elements to memory anyway, so we do it eagerly to
344 // allow us to push the arguments directly into place.
345 frame_->SyncRange(0, frame_->element_count() - 1);
346
347 __ movq(kScratchRegister, pairs, RelocInfo::EMBEDDED_OBJECT);
ager@chromium.org3811b432009-10-28 14:53:37 +0000348 frame_->EmitPush(rsi); // The context is the first argument.
ager@chromium.org3e875802009-06-29 08:26:34 +0000349 frame_->EmitPush(kScratchRegister);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +0000350 frame_->EmitPush(Smi::FromInt(is_eval() ? 1 : 0));
ager@chromium.org3e875802009-06-29 08:26:34 +0000351 Result ignored = frame_->CallRuntime(Runtime::kDeclareGlobals, 3);
352 // Return value is ignored.
kasperl@chromium.org71affb52009-05-26 05:44:31 +0000353}
354
ager@chromium.org3e875802009-06-29 08:26:34 +0000355
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000356void CodeGenerator::Generate(CompilationInfo* info) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000357 // Record the position for debugging purposes.
ager@chromium.org5c838252010-02-19 08:53:10 +0000358 CodeForFunctionPosition(info->function());
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000359 Comment cmnt(masm_, "[ function compiled by virtual frame code generator");
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000360
361 // Initialize state.
ager@chromium.org5c838252010-02-19 08:53:10 +0000362 info_ = info;
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000363 ASSERT(allocator_ == NULL);
364 RegisterAllocator register_allocator(this);
365 allocator_ = &register_allocator;
366 ASSERT(frame_ == NULL);
367 frame_ = new VirtualFrame();
368 set_in_spilled_code(false);
369
370 // Adjust for function-level loop nesting.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000371 ASSERT_EQ(0, loop_nesting_);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000372 loop_nesting_ += info->loop_nesting();
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000373
374 JumpTarget::set_compiling_deferred_code(false);
375
376#ifdef DEBUG
377 if (strlen(FLAG_stop_at) > 0 &&
ager@chromium.org5c838252010-02-19 08:53:10 +0000378 info->function()->name()->IsEqualTo(CStrVector(FLAG_stop_at))) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000379 frame_->SpillAll();
380 __ int3();
381 }
382#endif
383
384 // New scope to get automatic timing calculation.
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000385 { HistogramTimerScope codegen_timer(&Counters::code_generation);
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000386 CodeGenState state(this);
387
388 // Entry:
389 // Stack: receiver, arguments, return address.
ager@chromium.org3e875802009-06-29 08:26:34 +0000390 // rbp: caller's frame pointer
391 // rsp: stack pointer
392 // rdi: called JS function
393 // rsi: callee's context
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000394 allocator_->Initialize();
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000395
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000396 if (info->mode() == CompilationInfo::PRIMARY) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000397 frame_->Enter();
398
399 // Allocate space for locals and initialize them.
400 frame_->AllocateStackSlots();
401
402 // Allocate the local context if needed.
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +0000403 int heap_slots = scope()->num_heap_slots() - Context::MIN_CONTEXT_SLOTS;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000404 if (heap_slots > 0) {
405 Comment cmnt(masm_, "[ allocate local context");
406 // Allocate local context.
407 // Get outer context and create a new context based on it.
408 frame_->PushFunction();
409 Result context;
410 if (heap_slots <= FastNewContextStub::kMaximumSlots) {
411 FastNewContextStub stub(heap_slots);
412 context = frame_->CallStub(&stub, 1);
413 } else {
414 context = frame_->CallRuntime(Runtime::kNewContext, 1);
415 }
416
417 // Update context local.
418 frame_->SaveContextRegister();
419
420 // Verify that the runtime call result and rsi agree.
421 if (FLAG_debug_code) {
422 __ cmpq(context.reg(), rsi);
423 __ Assert(equal, "Runtime::NewContext should end up in rsi");
424 }
425 }
426
427 // TODO(1241774): Improve this code:
428 // 1) only needed if we have a context
429 // 2) no need to recompute context ptr every single time
430 // 3) don't copy parameter operand code from SlotOperand!
431 {
432 Comment cmnt2(masm_, "[ copy context parameters into .context");
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000433 // Note that iteration order is relevant here! If we have the same
434 // parameter twice (e.g., function (x, y, x)), and that parameter
435 // needs to be copied into the context, it must be the last argument
436 // passed to the parameter that needs to be copied. This is a rare
437 // case so we don't check for it, instead we rely on the copying
438 // order: such a parameter is copied repeatedly into the same
439 // context location and thus the last value is what is seen inside
440 // the function.
ager@chromium.org5c838252010-02-19 08:53:10 +0000441 for (int i = 0; i < scope()->num_parameters(); i++) {
442 Variable* par = scope()->parameter(i);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000443 Slot* slot = par->slot();
444 if (slot != NULL && slot->type() == Slot::CONTEXT) {
445 // The use of SlotOperand below is safe in unspilled code
446 // because the slot is guaranteed to be a context slot.
447 //
448 // There are no parameters in the global scope.
ager@chromium.org5c838252010-02-19 08:53:10 +0000449 ASSERT(!scope()->is_global_scope());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000450 frame_->PushParameterAt(i);
451 Result value = frame_->Pop();
452 value.ToRegister();
453
454 // SlotOperand loads context.reg() with the context object
455 // stored to, used below in RecordWrite.
456 Result context = allocator_->Allocate();
457 ASSERT(context.is_valid());
458 __ movq(SlotOperand(slot, context.reg()), value.reg());
459 int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize;
460 Result scratch = allocator_->Allocate();
461 ASSERT(scratch.is_valid());
462 frame_->Spill(context.reg());
463 frame_->Spill(value.reg());
464 __ RecordWrite(context.reg(), offset, value.reg(), scratch.reg());
465 }
466 }
467 }
468
469 // Store the arguments object. This must happen after context
470 // initialization because the arguments object may be stored in
471 // the context.
472 if (ArgumentsMode() != NO_ARGUMENTS_ALLOCATION) {
473 StoreArgumentsObject(true);
474 }
475
476 // Initialize ThisFunction reference if present.
ager@chromium.org5c838252010-02-19 08:53:10 +0000477 if (scope()->is_function_scope() && scope()->function() != NULL) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000478 frame_->Push(Factory::the_hole_value());
ager@chromium.org5c838252010-02-19 08:53:10 +0000479 StoreToSlot(scope()->function()->slot(), NOT_CONST_INIT);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000480 }
481 } else {
482 // When used as the secondary compiler for splitting, rbp, rsi,
483 // and rdi have been pushed on the stack. Adjust the virtual
484 // frame to match this state.
485 frame_->Adjust(3);
486 allocator_->Unuse(rdi);
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000487
488 // Bind all the bailout labels to the beginning of the function.
489 List<CompilationInfo::Bailout*>* bailouts = info->bailouts();
490 for (int i = 0; i < bailouts->length(); i++) {
491 __ bind(bailouts->at(i)->label());
492 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000493 }
494
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000495 // Initialize the function return target after the locals are set
496 // up, because it needs the expected frame height from the frame.
497 function_return_.set_direction(JumpTarget::BIDIRECTIONAL);
498 function_return_is_shadowed_ = false;
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000499
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000500 // Generate code to 'execute' declarations and initialize functions
501 // (source elements). In case of an illegal redeclaration we need to
502 // handle that instead of processing the declarations.
ager@chromium.org5c838252010-02-19 08:53:10 +0000503 if (scope()->HasIllegalRedeclaration()) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000504 Comment cmnt(masm_, "[ illegal redeclarations");
ager@chromium.org5c838252010-02-19 08:53:10 +0000505 scope()->VisitIllegalRedeclaration(this);
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000506 } else {
507 Comment cmnt(masm_, "[ declarations");
ager@chromium.org5c838252010-02-19 08:53:10 +0000508 ProcessDeclarations(scope()->declarations());
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000509 // Bail out if a stack-overflow exception occurred when processing
510 // declarations.
511 if (HasStackOverflow()) return;
512 }
513
514 if (FLAG_trace) {
515 frame_->CallRuntime(Runtime::kTraceEnter, 0);
516 // Ignore the return value.
517 }
ager@chromium.org3e875802009-06-29 08:26:34 +0000518 CheckStack();
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000519
520 // Compile the body of the function in a vanilla state. Don't
521 // bother compiling all the code if the scope has an illegal
522 // redeclaration.
ager@chromium.org5c838252010-02-19 08:53:10 +0000523 if (!scope()->HasIllegalRedeclaration()) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000524 Comment cmnt(masm_, "[ function body");
525#ifdef DEBUG
526 bool is_builtin = Bootstrapper::IsActive();
527 bool should_trace =
528 is_builtin ? FLAG_trace_builtin_calls : FLAG_trace_calls;
529 if (should_trace) {
530 frame_->CallRuntime(Runtime::kDebugTrace, 0);
531 // Ignore the return value.
532 }
533#endif
ager@chromium.org5c838252010-02-19 08:53:10 +0000534 VisitStatements(info->function()->body());
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000535
ager@chromium.org3e875802009-06-29 08:26:34 +0000536 // Handle the return from the function.
537 if (has_valid_frame()) {
538 // If there is a valid frame, control flow can fall off the end of
539 // the body. In that case there is an implicit return statement.
540 ASSERT(!function_return_is_shadowed_);
ager@chromium.org5c838252010-02-19 08:53:10 +0000541 CodeForReturnPosition(info->function());
ager@chromium.org3e875802009-06-29 08:26:34 +0000542 frame_->PrepareForReturn();
543 Result undefined(Factory::undefined_value());
544 if (function_return_.is_bound()) {
545 function_return_.Jump(&undefined);
546 } else {
547 function_return_.Bind(&undefined);
548 GenerateReturnSequence(&undefined);
549 }
550 } else if (function_return_.is_linked()) {
551 // If the return target has dangling jumps to it, then we have not
552 // yet generated the return sequence. This can happen when (a)
553 // control does not flow off the end of the body so we did not
554 // compile an artificial return statement just above, and (b) there
555 // are return statements in the body but (c) they are all shadowed.
556 Result return_value;
557 function_return_.Bind(&return_value);
558 GenerateReturnSequence(&return_value);
559 }
560 }
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000561 }
ager@chromium.org3e875802009-06-29 08:26:34 +0000562
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000563 // Adjust for function-level loop nesting.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000564 ASSERT_EQ(loop_nesting_, info->loop_nesting());
565 loop_nesting_ = 0;
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000566
567 // Code generation state must be reset.
568 ASSERT(state_ == NULL);
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000569 ASSERT(!function_return_is_shadowed_);
570 function_return_.Unuse();
571 DeleteFrame();
572
573 // Process any deferred code using the register allocator.
574 if (!HasStackOverflow()) {
575 HistogramTimerScope deferred_timer(&Counters::deferred_code_generation);
576 JumpTarget::set_compiling_deferred_code(true);
577 ProcessDeferred();
578 JumpTarget::set_compiling_deferred_code(false);
579 }
580
581 // There is no need to delete the register allocator, it is a
582 // stack-allocated local.
583 allocator_ = NULL;
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000584}
585
586void CodeGenerator::GenerateReturnSequence(Result* return_value) {
587 // The return value is a live (but not currently reference counted)
588 // reference to rax. This is safe because the current frame does not
589 // contain a reference to rax (it is prepared for the return by spilling
590 // all registers).
591 if (FLAG_trace) {
592 frame_->Push(return_value);
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000593 *return_value = frame_->CallRuntime(Runtime::kTraceExit, 1);
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000594 }
595 return_value->ToRegister(rax);
596
597 // Add a label for checking the size of the code used for returning.
sgjesse@chromium.org911335c2009-08-19 12:59:44 +0000598#ifdef DEBUG
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000599 Label check_exit_codesize;
600 masm_->bind(&check_exit_codesize);
sgjesse@chromium.org911335c2009-08-19 12:59:44 +0000601#endif
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000602
603 // Leave the frame and return popping the arguments and the
604 // receiver.
605 frame_->Exit();
ager@chromium.org5c838252010-02-19 08:53:10 +0000606 masm_->ret((scope()->num_parameters() + 1) * kPointerSize);
ager@chromium.org4af710e2009-09-15 12:20:11 +0000607#ifdef ENABLE_DEBUGGER_SUPPORT
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +0000608 // Add padding that will be overwritten by a debugger breakpoint.
sgjesse@chromium.org911335c2009-08-19 12:59:44 +0000609 // frame_->Exit() generates "movq rsp, rbp; pop rbp; ret k"
610 // with length 7 (3 + 1 + 3).
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000611 const int kPadding = Assembler::kJSReturnSequenceLength - 7;
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +0000612 for (int i = 0; i < kPadding; ++i) {
613 masm_->int3();
614 }
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000615 // Check that the size of the code used for returning matches what is
616 // expected by the debugger.
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000617 ASSERT_EQ(Assembler::kJSReturnSequenceLength,
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +0000618 masm_->SizeOfCodeGeneratedSince(&check_exit_codesize));
ager@chromium.org4af710e2009-09-15 12:20:11 +0000619#endif
620 DeleteFrame();
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000621}
622
623
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000624#ifdef DEBUG
625bool CodeGenerator::HasValidEntryRegisters() {
626 return (allocator()->count(rax) == (frame()->is_used(rax) ? 1 : 0))
627 && (allocator()->count(rbx) == (frame()->is_used(rbx) ? 1 : 0))
628 && (allocator()->count(rcx) == (frame()->is_used(rcx) ? 1 : 0))
629 && (allocator()->count(rdx) == (frame()->is_used(rdx) ? 1 : 0))
630 && (allocator()->count(rdi) == (frame()->is_used(rdi) ? 1 : 0))
631 && (allocator()->count(r8) == (frame()->is_used(r8) ? 1 : 0))
632 && (allocator()->count(r9) == (frame()->is_used(r9) ? 1 : 0))
633 && (allocator()->count(r11) == (frame()->is_used(r11) ? 1 : 0))
634 && (allocator()->count(r14) == (frame()->is_used(r14) ? 1 : 0))
635 && (allocator()->count(r15) == (frame()->is_used(r15) ? 1 : 0))
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000636 && (allocator()->count(r12) == (frame()->is_used(r12) ? 1 : 0));
637}
638#endif
639
640
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000641class DeferredReferenceGetKeyedValue: public DeferredCode {
642 public:
643 explicit DeferredReferenceGetKeyedValue(Register dst,
644 Register receiver,
kmillikin@chromium.org9155e252010-05-26 13:27:57 +0000645 Register key)
646 : dst_(dst), receiver_(receiver), key_(key) {
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000647 set_comment("[ DeferredReferenceGetKeyedValue");
648 }
649
650 virtual void Generate();
651
652 Label* patch_site() { return &patch_site_; }
653
654 private:
655 Label patch_site_;
656 Register dst_;
657 Register receiver_;
658 Register key_;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000659};
660
661
662void DeferredReferenceGetKeyedValue::Generate() {
663 __ push(receiver_); // First IC argument.
664 __ push(key_); // Second IC argument.
665
666 // Calculate the delta from the IC call instruction to the map check
667 // movq instruction in the inlined version. This delta is stored in
668 // a test(rax, delta) instruction after the call so that we can find
669 // it in the IC initialization code and patch the movq instruction.
670 // This means that we cannot allow test instructions after calls to
671 // KeyedLoadIC stubs in other places.
672 Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
kmillikin@chromium.org9155e252010-05-26 13:27:57 +0000673 __ Call(ic, RelocInfo::CODE_TARGET);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000674 // The delta from the start of the map-compare instruction to the
675 // test instruction. We use masm_-> directly here instead of the __
676 // macro because the macro sometimes uses macro expansion to turn
677 // into something that can't return a value. This is encountered
678 // when doing generated code coverage tests.
679 int delta_to_patch_site = masm_->SizeOfCodeGeneratedSince(patch_site());
680 // Here we use masm_-> instead of the __ macro because this is the
681 // instruction that gets patched and coverage code gets in the way.
682 // TODO(X64): Consider whether it's worth switching the test to a
683 // 7-byte NOP with non-zero immediate (0f 1f 80 xxxxxxxx) which won't
684 // be generated normally.
685 masm_->testl(rax, Immediate(-delta_to_patch_site));
686 __ IncrementCounter(&Counters::keyed_load_inline_miss, 1);
687
688 if (!dst_.is(rax)) __ movq(dst_, rax);
689 __ pop(key_);
690 __ pop(receiver_);
691}
692
693
694class DeferredReferenceSetKeyedValue: public DeferredCode {
695 public:
696 DeferredReferenceSetKeyedValue(Register value,
697 Register key,
698 Register receiver)
699 : value_(value), key_(key), receiver_(receiver) {
700 set_comment("[ DeferredReferenceSetKeyedValue");
701 }
702
703 virtual void Generate();
704
705 Label* patch_site() { return &patch_site_; }
706
707 private:
708 Register value_;
709 Register key_;
710 Register receiver_;
711 Label patch_site_;
712};
713
714
715void DeferredReferenceSetKeyedValue::Generate() {
716 __ IncrementCounter(&Counters::keyed_store_inline_miss, 1);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000717 // Move value, receiver, and key to registers rax, rdx, and rcx, as
718 // the IC stub expects.
719 // Move value to rax, using xchg if the receiver or key is in rax.
720 if (!value_.is(rax)) {
721 if (!receiver_.is(rax) && !key_.is(rax)) {
722 __ movq(rax, value_);
723 } else {
724 __ xchg(rax, value_);
725 // Update receiver_ and key_ if they are affected by the swap.
726 if (receiver_.is(rax)) {
727 receiver_ = value_;
728 } else if (receiver_.is(value_)) {
729 receiver_ = rax;
730 }
731 if (key_.is(rax)) {
732 key_ = value_;
733 } else if (key_.is(value_)) {
734 key_ = rax;
735 }
736 }
737 }
738 // Value is now in rax. Its original location is remembered in value_,
739 // and the value is restored to value_ before returning.
740 // The variables receiver_ and key_ are not preserved.
741 // Move receiver and key to rdx and rcx, swapping if necessary.
742 if (receiver_.is(rdx)) {
743 if (!key_.is(rcx)) {
744 __ movq(rcx, key_);
745 } // Else everything is already in the right place.
746 } else if (receiver_.is(rcx)) {
747 if (key_.is(rdx)) {
748 __ xchg(rcx, rdx);
749 } else if (key_.is(rcx)) {
750 __ movq(rdx, receiver_);
751 } else {
752 __ movq(rdx, receiver_);
753 __ movq(rcx, key_);
754 }
755 } else if (key_.is(rcx)) {
756 __ movq(rdx, receiver_);
757 } else {
758 __ movq(rcx, key_);
759 __ movq(rdx, receiver_);
760 }
761
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000762 // Call the IC stub.
763 Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize));
764 __ Call(ic, RelocInfo::CODE_TARGET);
765 // The delta from the start of the map-compare instructions (initial movq)
766 // to the test instruction. We use masm_-> directly here instead of the
767 // __ macro because the macro sometimes uses macro expansion to turn
768 // into something that can't return a value. This is encountered
769 // when doing generated code coverage tests.
770 int delta_to_patch_site = masm_->SizeOfCodeGeneratedSince(patch_site());
771 // Here we use masm_-> instead of the __ macro because this is the
772 // instruction that gets patched and coverage code gets in the way.
773 masm_->testl(rax, Immediate(-delta_to_patch_site));
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000774 // Restore value (returned from store IC).
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000775 if (!value_.is(rax)) __ movq(value_, rax);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000776}
777
778
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000779void CodeGenerator::CallApplyLazy(Expression* applicand,
ager@chromium.org18ad94b2009-09-02 08:22:29 +0000780 Expression* receiver,
781 VariableProxy* arguments,
782 int position) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000783 // An optimized implementation of expressions of the form
784 // x.apply(y, arguments).
785 // If the arguments object of the scope has not been allocated,
786 // and x.apply is Function.prototype.apply, this optimization
787 // just copies y and the arguments of the current function on the
788 // stack, as receiver and arguments, and calls x.
789 // In the implementation comments, we call x the applicand
790 // and y the receiver.
ager@chromium.org18ad94b2009-09-02 08:22:29 +0000791 ASSERT(ArgumentsMode() == LAZY_ARGUMENTS_ALLOCATION);
792 ASSERT(arguments->IsArguments());
793
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000794 // Load applicand.apply onto the stack. This will usually
ager@chromium.org18ad94b2009-09-02 08:22:29 +0000795 // give us a megamorphic load site. Not super, but it works.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000796 Load(applicand);
sgjesse@chromium.org82dbbab2010-06-02 08:57:44 +0000797 frame()->Dup();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000798 Handle<String> name = Factory::LookupAsciiSymbol("apply");
799 frame()->Push(name);
800 Result answer = frame()->CallLoadIC(RelocInfo::CODE_TARGET);
801 __ nop();
802 frame()->Push(&answer);
ager@chromium.org18ad94b2009-09-02 08:22:29 +0000803
804 // Load the receiver and the existing arguments object onto the
805 // expression stack. Avoid allocating the arguments object here.
806 Load(receiver);
ager@chromium.org5c838252010-02-19 08:53:10 +0000807 LoadFromSlot(scope()->arguments()->var()->slot(), NOT_INSIDE_TYPEOF);
ager@chromium.org18ad94b2009-09-02 08:22:29 +0000808
809 // Emit the source position information after having loaded the
810 // receiver and the arguments.
811 CodeForSourcePosition(position);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000812 // Contents of frame at this point:
813 // Frame[0]: arguments object of the current function or the hole.
814 // Frame[1]: receiver
815 // Frame[2]: applicand.apply
816 // Frame[3]: applicand.
ager@chromium.org18ad94b2009-09-02 08:22:29 +0000817
818 // Check if the arguments object has been lazily allocated
819 // already. If so, just use that instead of copying the arguments
820 // from the stack. This also deals with cases where a local variable
821 // named 'arguments' has been introduced.
822 frame_->Dup();
823 Result probe = frame_->Pop();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000824 { VirtualFrame::SpilledScope spilled_scope;
825 Label slow, done;
826 bool try_lazy = true;
827 if (probe.is_constant()) {
828 try_lazy = probe.handle()->IsTheHole();
829 } else {
830 __ CompareRoot(probe.reg(), Heap::kTheHoleValueRootIndex);
831 probe.Unuse();
832 __ j(not_equal, &slow);
833 }
ager@chromium.org18ad94b2009-09-02 08:22:29 +0000834
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000835 if (try_lazy) {
836 Label build_args;
837 // Get rid of the arguments object probe.
838 frame_->Drop(); // Can be called on a spilled frame.
839 // Stack now has 3 elements on it.
840 // Contents of stack at this point:
841 // rsp[0]: receiver
842 // rsp[1]: applicand.apply
843 // rsp[2]: applicand.
ager@chromium.org18ad94b2009-09-02 08:22:29 +0000844
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000845 // Check that the receiver really is a JavaScript object.
846 __ movq(rax, Operand(rsp, 0));
847 Condition is_smi = masm_->CheckSmi(rax);
848 __ j(is_smi, &build_args);
ager@chromium.org18ad94b2009-09-02 08:22:29 +0000849 // We allow all JSObjects including JSFunctions. As long as
850 // JS_FUNCTION_TYPE is the last instance type and it is right
851 // after LAST_JS_OBJECT_TYPE, we do not have to check the upper
852 // bound.
853 ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
854 ASSERT(JS_FUNCTION_TYPE == LAST_JS_OBJECT_TYPE + 1);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000855 __ CmpObjectType(rax, FIRST_JS_OBJECT_TYPE, rcx);
856 __ j(below, &build_args);
ager@chromium.org18ad94b2009-09-02 08:22:29 +0000857
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000858 // Check that applicand.apply is Function.prototype.apply.
859 __ movq(rax, Operand(rsp, kPointerSize));
860 is_smi = masm_->CheckSmi(rax);
861 __ j(is_smi, &build_args);
862 __ CmpObjectType(rax, JS_FUNCTION_TYPE, rcx);
863 __ j(not_equal, &build_args);
864 __ movq(rax, FieldOperand(rax, JSFunction::kSharedFunctionInfoOffset));
ager@chromium.org18ad94b2009-09-02 08:22:29 +0000865 Handle<Code> apply_code(Builtins::builtin(Builtins::FunctionApply));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000866 __ Cmp(FieldOperand(rax, SharedFunctionInfo::kCodeOffset), apply_code);
867 __ j(not_equal, &build_args);
868
869 // Check that applicand is a function.
870 __ movq(rdi, Operand(rsp, 2 * kPointerSize));
871 is_smi = masm_->CheckSmi(rdi);
872 __ j(is_smi, &build_args);
873 __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rcx);
874 __ j(not_equal, &build_args);
875
876 // Copy the arguments to this function possibly from the
877 // adaptor frame below it.
878 Label invoke, adapted;
879 __ movq(rdx, Operand(rbp, StandardFrameConstants::kCallerFPOffset));
880 __ SmiCompare(Operand(rdx, StandardFrameConstants::kContextOffset),
881 Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR));
882 __ j(equal, &adapted);
883
884 // No arguments adaptor frame. Copy fixed number of arguments.
ager@chromium.org5c838252010-02-19 08:53:10 +0000885 __ movq(rax, Immediate(scope()->num_parameters()));
886 for (int i = 0; i < scope()->num_parameters(); i++) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000887 __ push(frame_->ParameterAt(i));
888 }
889 __ jmp(&invoke);
890
891 // Arguments adaptor frame present. Copy arguments from there, but
892 // avoid copying too many arguments to avoid stack overflows.
893 __ bind(&adapted);
894 static const uint32_t kArgumentsLimit = 1 * KB;
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000895 __ SmiToInteger32(rax,
896 Operand(rdx,
897 ArgumentsAdaptorFrameConstants::kLengthOffset));
898 __ movl(rcx, rax);
899 __ cmpl(rax, Immediate(kArgumentsLimit));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000900 __ j(above, &build_args);
901
902 // Loop through the arguments pushing them onto the execution
903 // stack. We don't inform the virtual frame of the push, so we don't
904 // have to worry about getting rid of the elements from the virtual
905 // frame.
906 Label loop;
907 // rcx is a small non-negative integer, due to the test above.
908 __ testl(rcx, rcx);
909 __ j(zero, &invoke);
910 __ bind(&loop);
911 __ push(Operand(rdx, rcx, times_pointer_size, 1 * kPointerSize));
912 __ decl(rcx);
913 __ j(not_zero, &loop);
914
915 // Invoke the function.
916 __ bind(&invoke);
917 ParameterCount actual(rax);
918 __ InvokeFunction(rdi, actual, CALL_FUNCTION);
919 // Drop applicand.apply and applicand from the stack, and push
920 // the result of the function call, but leave the spilled frame
921 // unchanged, with 3 elements, so it is correct when we compile the
922 // slow-case code.
923 __ addq(rsp, Immediate(2 * kPointerSize));
924 __ push(rax);
925 // Stack now has 1 element:
926 // rsp[0]: result
927 __ jmp(&done);
928
929 // Slow-case: Allocate the arguments object since we know it isn't
930 // there, and fall-through to the slow-case where we call
931 // applicand.apply.
932 __ bind(&build_args);
933 // Stack now has 3 elements, because we have jumped from where:
934 // rsp[0]: receiver
935 // rsp[1]: applicand.apply
936 // rsp[2]: applicand.
937
938 // StoreArgumentsObject requires a correct frame, and may modify it.
939 Result arguments_object = StoreArgumentsObject(false);
940 frame_->SpillAll();
941 arguments_object.ToRegister();
942 frame_->EmitPush(arguments_object.reg());
943 arguments_object.Unuse();
944 // Stack and frame now have 4 elements.
945 __ bind(&slow);
ager@chromium.org18ad94b2009-09-02 08:22:29 +0000946 }
947
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000948 // Generic computation of x.apply(y, args) with no special optimization.
949 // Flip applicand.apply and applicand on the stack, so
950 // applicand looks like the receiver of the applicand.apply call.
951 // Then process it as a normal function call.
952 __ movq(rax, Operand(rsp, 3 * kPointerSize));
953 __ movq(rbx, Operand(rsp, 2 * kPointerSize));
954 __ movq(Operand(rsp, 2 * kPointerSize), rax);
955 __ movq(Operand(rsp, 3 * kPointerSize), rbx);
ager@chromium.org18ad94b2009-09-02 08:22:29 +0000956
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000957 CallFunctionStub call_function(2, NOT_IN_LOOP, NO_CALL_FUNCTION_FLAGS);
958 Result res = frame_->CallStub(&call_function, 3);
959 // The function and its two arguments have been dropped.
960 frame_->Drop(1); // Drop the receiver as well.
961 res.ToRegister();
962 frame_->EmitPush(res.reg());
963 // Stack now has 1 element:
964 // rsp[0]: result
965 if (try_lazy) __ bind(&done);
966 } // End of spilled scope.
967 // Restore the context register after a call.
ager@chromium.org18ad94b2009-09-02 08:22:29 +0000968 frame_->RestoreContextRegister();
969}
970
971
972class DeferredStackCheck: public DeferredCode {
973 public:
974 DeferredStackCheck() {
975 set_comment("[ DeferredStackCheck");
976 }
977
978 virtual void Generate();
979};
980
981
982void DeferredStackCheck::Generate() {
983 StackCheckStub stub;
984 __ CallStub(&stub);
985}
986
987
988void CodeGenerator::CheckStack() {
ager@chromium.org3811b432009-10-28 14:53:37 +0000989 DeferredStackCheck* deferred = new DeferredStackCheck;
990 __ CompareRoot(rsp, Heap::kStackLimitRootIndex);
991 deferred->Branch(below);
992 deferred->BindExit();
ager@chromium.org18ad94b2009-09-02 08:22:29 +0000993}
994
995
ager@chromium.org3e875802009-06-29 08:26:34 +0000996void CodeGenerator::VisitAndSpill(Statement* statement) {
997 // TODO(X64): No architecture specific code. Move to shared location.
998 ASSERT(in_spilled_code());
999 set_in_spilled_code(false);
1000 Visit(statement);
1001 if (frame_ != NULL) {
1002 frame_->SpillAll();
1003 }
1004 set_in_spilled_code(true);
1005}
1006
1007
1008void CodeGenerator::VisitStatementsAndSpill(ZoneList<Statement*>* statements) {
1009 ASSERT(in_spilled_code());
1010 set_in_spilled_code(false);
1011 VisitStatements(statements);
1012 if (frame_ != NULL) {
1013 frame_->SpillAll();
1014 }
1015 set_in_spilled_code(true);
1016}
1017
1018
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001019void CodeGenerator::VisitStatements(ZoneList<Statement*>* statements) {
1020 ASSERT(!in_spilled_code());
1021 for (int i = 0; has_valid_frame() && i < statements->length(); i++) {
1022 Visit(statements->at(i));
1023 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001024}
1025
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001026
1027void CodeGenerator::VisitBlock(Block* node) {
1028 ASSERT(!in_spilled_code());
1029 Comment cmnt(masm_, "[ Block");
1030 CodeForStatementPosition(node);
1031 node->break_target()->set_direction(JumpTarget::FORWARD_ONLY);
1032 VisitStatements(node->statements());
1033 if (node->break_target()->is_linked()) {
1034 node->break_target()->Bind();
1035 }
1036 node->break_target()->Unuse();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001037}
1038
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001039
1040void CodeGenerator::VisitDeclaration(Declaration* node) {
1041 Comment cmnt(masm_, "[ Declaration");
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001042 Variable* var = node->proxy()->var();
1043 ASSERT(var != NULL); // must have been resolved
1044 Slot* slot = var->slot();
1045
1046 // If it was not possible to allocate the variable at compile time,
1047 // we need to "declare" it at runtime to make sure it actually
1048 // exists in the local context.
1049 if (slot != NULL && slot->type() == Slot::LOOKUP) {
1050 // Variables with a "LOOKUP" slot were introduced as non-locals
1051 // during variable resolution and must have mode DYNAMIC.
1052 ASSERT(var->is_dynamic());
1053 // For now, just do a runtime call. Sync the virtual frame eagerly
1054 // so we can simply push the arguments into place.
1055 frame_->SyncRange(0, frame_->element_count() - 1);
1056 frame_->EmitPush(rsi);
1057 __ movq(kScratchRegister, var->name(), RelocInfo::EMBEDDED_OBJECT);
1058 frame_->EmitPush(kScratchRegister);
1059 // Declaration nodes are always introduced in one of two modes.
1060 ASSERT(node->mode() == Variable::VAR || node->mode() == Variable::CONST);
1061 PropertyAttributes attr = node->mode() == Variable::VAR ? NONE : READ_ONLY;
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001062 frame_->EmitPush(Smi::FromInt(attr));
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001063 // Push initial value, if any.
1064 // Note: For variables we must not push an initial value (such as
1065 // 'undefined') because we may have a (legal) redeclaration and we
1066 // must not destroy the current value.
1067 if (node->mode() == Variable::CONST) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00001068 frame_->EmitPush(Heap::kTheHoleValueRootIndex);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001069 } else if (node->fun() != NULL) {
1070 Load(node->fun());
1071 } else {
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001072 frame_->EmitPush(Smi::FromInt(0)); // no initial value!
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001073 }
1074 Result ignored = frame_->CallRuntime(Runtime::kDeclareContextSlot, 4);
1075 // Ignore the return value (declarations are statements).
1076 return;
1077 }
1078
1079 ASSERT(!var->is_global());
1080
1081 // If we have a function or a constant, we need to initialize the variable.
1082 Expression* val = NULL;
1083 if (node->mode() == Variable::CONST) {
1084 val = new Literal(Factory::the_hole_value());
1085 } else {
1086 val = node->fun(); // NULL if we don't have a function
1087 }
1088
1089 if (val != NULL) {
1090 {
1091 // Set the initial value.
1092 Reference target(this, node->proxy());
1093 Load(val);
1094 target.SetValue(NOT_CONST_INIT);
1095 // The reference is removed from the stack (preserving TOS) when
1096 // it goes out of scope.
1097 }
1098 // Get rid of the assigned value (declarations are statements).
1099 frame_->Drop();
1100 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001101}
1102
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001103
1104void CodeGenerator::VisitExpressionStatement(ExpressionStatement* node) {
1105 ASSERT(!in_spilled_code());
1106 Comment cmnt(masm_, "[ ExpressionStatement");
1107 CodeForStatementPosition(node);
1108 Expression* expression = node->expression();
1109 expression->MarkAsStatement();
1110 Load(expression);
1111 // Remove the lingering expression result from the top of stack.
1112 frame_->Drop();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001113}
1114
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001115
1116void CodeGenerator::VisitEmptyStatement(EmptyStatement* node) {
1117 ASSERT(!in_spilled_code());
1118 Comment cmnt(masm_, "// EmptyStatement");
1119 CodeForStatementPosition(node);
1120 // nothing to do
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001121}
1122
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001123
1124void CodeGenerator::VisitIfStatement(IfStatement* node) {
1125 ASSERT(!in_spilled_code());
1126 Comment cmnt(masm_, "[ IfStatement");
1127 // Generate different code depending on which parts of the if statement
1128 // are present or not.
1129 bool has_then_stm = node->HasThenStatement();
1130 bool has_else_stm = node->HasElseStatement();
1131
1132 CodeForStatementPosition(node);
1133 JumpTarget exit;
1134 if (has_then_stm && has_else_stm) {
1135 JumpTarget then;
1136 JumpTarget else_;
1137 ControlDestination dest(&then, &else_, true);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001138 LoadCondition(node->condition(), &dest, true);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001139
1140 if (dest.false_was_fall_through()) {
1141 // The else target was bound, so we compile the else part first.
1142 Visit(node->else_statement());
1143
1144 // We may have dangling jumps to the then part.
1145 if (then.is_linked()) {
1146 if (has_valid_frame()) exit.Jump();
1147 then.Bind();
1148 Visit(node->then_statement());
1149 }
1150 } else {
1151 // The then target was bound, so we compile the then part first.
1152 Visit(node->then_statement());
1153
1154 if (else_.is_linked()) {
1155 if (has_valid_frame()) exit.Jump();
1156 else_.Bind();
1157 Visit(node->else_statement());
1158 }
1159 }
1160
1161 } else if (has_then_stm) {
1162 ASSERT(!has_else_stm);
1163 JumpTarget then;
1164 ControlDestination dest(&then, &exit, true);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001165 LoadCondition(node->condition(), &dest, true);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001166
1167 if (dest.false_was_fall_through()) {
1168 // The exit label was bound. We may have dangling jumps to the
1169 // then part.
1170 if (then.is_linked()) {
1171 exit.Unuse();
1172 exit.Jump();
1173 then.Bind();
1174 Visit(node->then_statement());
1175 }
1176 } else {
1177 // The then label was bound.
1178 Visit(node->then_statement());
1179 }
1180
1181 } else if (has_else_stm) {
1182 ASSERT(!has_then_stm);
1183 JumpTarget else_;
1184 ControlDestination dest(&exit, &else_, false);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001185 LoadCondition(node->condition(), &dest, true);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001186
1187 if (dest.true_was_fall_through()) {
1188 // The exit label was bound. We may have dangling jumps to the
1189 // else part.
1190 if (else_.is_linked()) {
1191 exit.Unuse();
1192 exit.Jump();
1193 else_.Bind();
1194 Visit(node->else_statement());
1195 }
1196 } else {
1197 // The else label was bound.
1198 Visit(node->else_statement());
1199 }
1200
1201 } else {
1202 ASSERT(!has_then_stm && !has_else_stm);
1203 // We only care about the condition's side effects (not its value
1204 // or control flow effect). LoadCondition is called without
1205 // forcing control flow.
1206 ControlDestination dest(&exit, &exit, true);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001207 LoadCondition(node->condition(), &dest, false);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001208 if (!dest.is_used()) {
1209 // We got a value on the frame rather than (or in addition to)
1210 // control flow.
1211 frame_->Drop();
1212 }
1213 }
1214
1215 if (exit.is_linked()) {
1216 exit.Bind();
1217 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001218}
1219
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001220
ager@chromium.org3e875802009-06-29 08:26:34 +00001221void CodeGenerator::VisitContinueStatement(ContinueStatement* node) {
1222 ASSERT(!in_spilled_code());
1223 Comment cmnt(masm_, "[ ContinueStatement");
1224 CodeForStatementPosition(node);
1225 node->target()->continue_target()->Jump();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001226}
1227
ager@chromium.org3e875802009-06-29 08:26:34 +00001228
1229void CodeGenerator::VisitBreakStatement(BreakStatement* node) {
1230 ASSERT(!in_spilled_code());
1231 Comment cmnt(masm_, "[ BreakStatement");
1232 CodeForStatementPosition(node);
1233 node->target()->break_target()->Jump();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001234}
1235
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001236
1237void CodeGenerator::VisitReturnStatement(ReturnStatement* node) {
1238 ASSERT(!in_spilled_code());
1239 Comment cmnt(masm_, "[ ReturnStatement");
1240
1241 CodeForStatementPosition(node);
1242 Load(node->expression());
1243 Result return_value = frame_->Pop();
1244 if (function_return_is_shadowed_) {
1245 function_return_.Jump(&return_value);
1246 } else {
1247 frame_->PrepareForReturn();
1248 if (function_return_.is_bound()) {
1249 // If the function return label is already bound we reuse the
1250 // code by jumping to the return site.
1251 function_return_.Jump(&return_value);
1252 } else {
1253 function_return_.Bind(&return_value);
1254 GenerateReturnSequence(&return_value);
1255 }
1256 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001257}
1258
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001259
ager@chromium.org3e875802009-06-29 08:26:34 +00001260void CodeGenerator::VisitWithEnterStatement(WithEnterStatement* node) {
1261 ASSERT(!in_spilled_code());
1262 Comment cmnt(masm_, "[ WithEnterStatement");
1263 CodeForStatementPosition(node);
1264 Load(node->expression());
1265 Result context;
1266 if (node->is_catch_block()) {
1267 context = frame_->CallRuntime(Runtime::kPushCatchContext, 1);
1268 } else {
1269 context = frame_->CallRuntime(Runtime::kPushContext, 1);
1270 }
1271
1272 // Update context local.
1273 frame_->SaveContextRegister();
1274
1275 // Verify that the runtime call result and rsi agree.
1276 if (FLAG_debug_code) {
1277 __ cmpq(context.reg(), rsi);
1278 __ Assert(equal, "Runtime::NewContext should end up in rsi");
1279 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001280}
1281
ager@chromium.org3e875802009-06-29 08:26:34 +00001282
1283void CodeGenerator::VisitWithExitStatement(WithExitStatement* node) {
1284 ASSERT(!in_spilled_code());
1285 Comment cmnt(masm_, "[ WithExitStatement");
1286 CodeForStatementPosition(node);
1287 // Pop context.
1288 __ movq(rsi, ContextOperand(rsi, Context::PREVIOUS_INDEX));
1289 // Update context local.
1290 frame_->SaveContextRegister();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001291}
1292
ager@chromium.org3e875802009-06-29 08:26:34 +00001293
1294void CodeGenerator::VisitSwitchStatement(SwitchStatement* node) {
1295 // TODO(X64): This code is completely generic and should be moved somewhere
1296 // where it can be shared between architectures.
1297 ASSERT(!in_spilled_code());
1298 Comment cmnt(masm_, "[ SwitchStatement");
1299 CodeForStatementPosition(node);
1300 node->break_target()->set_direction(JumpTarget::FORWARD_ONLY);
1301
1302 // Compile the switch value.
1303 Load(node->tag());
1304
1305 ZoneList<CaseClause*>* cases = node->cases();
1306 int length = cases->length();
1307 CaseClause* default_clause = NULL;
1308
1309 JumpTarget next_test;
1310 // Compile the case label expressions and comparisons. Exit early
1311 // if a comparison is unconditionally true. The target next_test is
1312 // bound before the loop in order to indicate control flow to the
1313 // first comparison.
1314 next_test.Bind();
1315 for (int i = 0; i < length && !next_test.is_unused(); i++) {
1316 CaseClause* clause = cases->at(i);
1317 // The default is not a test, but remember it for later.
1318 if (clause->is_default()) {
1319 default_clause = clause;
1320 continue;
1321 }
1322
1323 Comment cmnt(masm_, "[ Case comparison");
1324 // We recycle the same target next_test for each test. Bind it if
1325 // the previous test has not done so and then unuse it for the
1326 // loop.
1327 if (next_test.is_linked()) {
1328 next_test.Bind();
1329 }
1330 next_test.Unuse();
1331
1332 // Duplicate the switch value.
1333 frame_->Dup();
1334
1335 // Compile the label expression.
1336 Load(clause->label());
1337
1338 // Compare and branch to the body if true or the next test if
1339 // false. Prefer the next test as a fall through.
1340 ControlDestination dest(clause->body_target(), &next_test, false);
ager@chromium.org5c838252010-02-19 08:53:10 +00001341 Comparison(node, equal, true, &dest);
ager@chromium.org3e875802009-06-29 08:26:34 +00001342
1343 // If the comparison fell through to the true target, jump to the
1344 // actual body.
1345 if (dest.true_was_fall_through()) {
1346 clause->body_target()->Unuse();
1347 clause->body_target()->Jump();
1348 }
1349 }
1350
1351 // If there was control flow to a next test from the last one
1352 // compiled, compile a jump to the default or break target.
1353 if (!next_test.is_unused()) {
1354 if (next_test.is_linked()) {
1355 next_test.Bind();
1356 }
1357 // Drop the switch value.
1358 frame_->Drop();
1359 if (default_clause != NULL) {
1360 default_clause->body_target()->Jump();
1361 } else {
1362 node->break_target()->Jump();
1363 }
1364 }
1365
1366 // The last instruction emitted was a jump, either to the default
1367 // clause or the break target, or else to a case body from the loop
1368 // that compiles the tests.
1369 ASSERT(!has_valid_frame());
1370 // Compile case bodies as needed.
1371 for (int i = 0; i < length; i++) {
1372 CaseClause* clause = cases->at(i);
1373
1374 // There are two ways to reach the body: from the corresponding
1375 // test or as the fall through of the previous body.
1376 if (clause->body_target()->is_linked() || has_valid_frame()) {
1377 if (clause->body_target()->is_linked()) {
1378 if (has_valid_frame()) {
1379 // If we have both a jump to the test and a fall through, put
1380 // a jump on the fall through path to avoid the dropping of
1381 // the switch value on the test path. The exception is the
1382 // default which has already had the switch value dropped.
1383 if (clause->is_default()) {
1384 clause->body_target()->Bind();
1385 } else {
1386 JumpTarget body;
1387 body.Jump();
1388 clause->body_target()->Bind();
1389 frame_->Drop();
1390 body.Bind();
1391 }
1392 } else {
1393 // No fall through to worry about.
1394 clause->body_target()->Bind();
1395 if (!clause->is_default()) {
1396 frame_->Drop();
1397 }
1398 }
1399 } else {
1400 // Otherwise, we have only fall through.
1401 ASSERT(has_valid_frame());
1402 }
1403
1404 // We are now prepared to compile the body.
1405 Comment cmnt(masm_, "[ Case body");
1406 VisitStatements(clause->statements());
1407 }
1408 clause->body_target()->Unuse();
1409 }
1410
1411 // We may not have a valid frame here so bind the break target only
1412 // if needed.
1413 if (node->break_target()->is_linked()) {
1414 node->break_target()->Bind();
1415 }
1416 node->break_target()->Unuse();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001417}
1418
ager@chromium.org3e875802009-06-29 08:26:34 +00001419
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001420void CodeGenerator::VisitDoWhileStatement(DoWhileStatement* node) {
ager@chromium.org3e875802009-06-29 08:26:34 +00001421 ASSERT(!in_spilled_code());
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001422 Comment cmnt(masm_, "[ DoWhileStatement");
ager@chromium.org3e875802009-06-29 08:26:34 +00001423 CodeForStatementPosition(node);
1424 node->break_target()->set_direction(JumpTarget::FORWARD_ONLY);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001425 JumpTarget body(JumpTarget::BIDIRECTIONAL);
1426 IncrementLoopNesting();
ager@chromium.org3e875802009-06-29 08:26:34 +00001427
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001428 ConditionAnalysis info = AnalyzeCondition(node->cond());
1429 // Label the top of the loop for the backward jump if necessary.
1430 switch (info) {
1431 case ALWAYS_TRUE:
1432 // Use the continue target.
1433 node->continue_target()->set_direction(JumpTarget::BIDIRECTIONAL);
1434 node->continue_target()->Bind();
1435 break;
1436 case ALWAYS_FALSE:
1437 // No need to label it.
1438 node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY);
1439 break;
1440 case DONT_KNOW:
1441 // Continue is the test, so use the backward body target.
1442 node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY);
1443 body.Bind();
1444 break;
ager@chromium.org3e875802009-06-29 08:26:34 +00001445 }
1446
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001447 CheckStack(); // TODO(1222600): ignore if body contains calls.
1448 Visit(node->body());
ager@chromium.org3e875802009-06-29 08:26:34 +00001449
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001450 // Compile the test.
1451 switch (info) {
1452 case ALWAYS_TRUE:
1453 // If control flow can fall off the end of the body, jump back
1454 // to the top and bind the break target at the exit.
1455 if (has_valid_frame()) {
1456 node->continue_target()->Jump();
ager@chromium.org3e875802009-06-29 08:26:34 +00001457 }
ager@chromium.org3e875802009-06-29 08:26:34 +00001458 if (node->break_target()->is_linked()) {
1459 node->break_target()->Bind();
1460 }
1461 break;
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001462 case ALWAYS_FALSE:
1463 // We may have had continues or breaks in the body.
1464 if (node->continue_target()->is_linked()) {
1465 node->continue_target()->Bind();
ager@chromium.org3e875802009-06-29 08:26:34 +00001466 }
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001467 if (node->break_target()->is_linked()) {
1468 node->break_target()->Bind();
ager@chromium.org3e875802009-06-29 08:26:34 +00001469 }
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001470 break;
1471 case DONT_KNOW:
1472 // We have to compile the test expression if it can be reached by
1473 // control flow falling out of the body or via continue.
1474 if (node->continue_target()->is_linked()) {
1475 node->continue_target()->Bind();
1476 }
1477 if (has_valid_frame()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001478 Comment cmnt(masm_, "[ DoWhileCondition");
1479 CodeForDoWhileConditionPosition(node);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001480 ControlDestination dest(&body, node->break_target(), false);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001481 LoadCondition(node->cond(), &dest, true);
ager@chromium.org3e875802009-06-29 08:26:34 +00001482 }
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001483 if (node->break_target()->is_linked()) {
1484 node->break_target()->Bind();
1485 }
1486 break;
1487 }
ager@chromium.org3e875802009-06-29 08:26:34 +00001488
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001489 DecrementLoopNesting();
1490 node->continue_target()->Unuse();
1491 node->break_target()->Unuse();
1492}
ager@chromium.org3e875802009-06-29 08:26:34 +00001493
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001494
1495void CodeGenerator::VisitWhileStatement(WhileStatement* node) {
1496 ASSERT(!in_spilled_code());
1497 Comment cmnt(masm_, "[ WhileStatement");
1498 CodeForStatementPosition(node);
1499
1500 // If the condition is always false and has no side effects, we do not
1501 // need to compile anything.
1502 ConditionAnalysis info = AnalyzeCondition(node->cond());
1503 if (info == ALWAYS_FALSE) return;
1504
1505 // Do not duplicate conditions that may have function literal
1506 // subexpressions. This can cause us to compile the function literal
1507 // twice.
1508 bool test_at_bottom = !node->may_have_function_literal();
1509 node->break_target()->set_direction(JumpTarget::FORWARD_ONLY);
1510 IncrementLoopNesting();
1511 JumpTarget body;
1512 if (test_at_bottom) {
1513 body.set_direction(JumpTarget::BIDIRECTIONAL);
1514 }
1515
1516 // Based on the condition analysis, compile the test as necessary.
1517 switch (info) {
1518 case ALWAYS_TRUE:
1519 // We will not compile the test expression. Label the top of the
1520 // loop with the continue target.
1521 node->continue_target()->set_direction(JumpTarget::BIDIRECTIONAL);
1522 node->continue_target()->Bind();
1523 break;
1524 case DONT_KNOW: {
1525 if (test_at_bottom) {
1526 // Continue is the test at the bottom, no need to label the test
1527 // at the top. The body is a backward target.
1528 node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY);
1529 } else {
1530 // Label the test at the top as the continue target. The body
1531 // is a forward-only target.
1532 node->continue_target()->set_direction(JumpTarget::BIDIRECTIONAL);
1533 node->continue_target()->Bind();
1534 }
1535 // Compile the test with the body as the true target and preferred
1536 // fall-through and with the break target as the false target.
1537 ControlDestination dest(&body, node->break_target(), true);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001538 LoadCondition(node->cond(), &dest, true);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001539
1540 if (dest.false_was_fall_through()) {
1541 // If we got the break target as fall-through, the test may have
1542 // been unconditionally false (if there are no jumps to the
1543 // body).
1544 if (!body.is_linked()) {
1545 DecrementLoopNesting();
1546 return;
1547 }
1548
1549 // Otherwise, jump around the body on the fall through and then
1550 // bind the body target.
1551 node->break_target()->Unuse();
1552 node->break_target()->Jump();
1553 body.Bind();
1554 }
1555 break;
1556 }
1557 case ALWAYS_FALSE:
1558 UNREACHABLE();
1559 break;
1560 }
1561
1562 CheckStack(); // TODO(1222600): ignore if body contains calls.
1563 Visit(node->body());
1564
1565 // Based on the condition analysis, compile the backward jump as
1566 // necessary.
1567 switch (info) {
1568 case ALWAYS_TRUE:
1569 // The loop body has been labeled with the continue target.
1570 if (has_valid_frame()) {
1571 node->continue_target()->Jump();
1572 }
1573 break;
1574 case DONT_KNOW:
1575 if (test_at_bottom) {
1576 // If we have chosen to recompile the test at the bottom,
1577 // then it is the continue target.
ager@chromium.org3e875802009-06-29 08:26:34 +00001578 if (node->continue_target()->is_linked()) {
1579 node->continue_target()->Bind();
1580 }
ager@chromium.org3e875802009-06-29 08:26:34 +00001581 if (has_valid_frame()) {
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001582 // The break target is the fall-through (body is a backward
1583 // jump from here and thus an invalid fall-through).
1584 ControlDestination dest(&body, node->break_target(), false);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001585 LoadCondition(node->cond(), &dest, true);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001586 }
1587 } else {
1588 // If we have chosen not to recompile the test at the
1589 // bottom, jump back to the one at the top.
1590 if (has_valid_frame()) {
1591 node->continue_target()->Jump();
ager@chromium.org3e875802009-06-29 08:26:34 +00001592 }
1593 }
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001594 break;
1595 case ALWAYS_FALSE:
1596 UNREACHABLE();
1597 break;
1598 }
ager@chromium.org3e875802009-06-29 08:26:34 +00001599
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001600 // The break target may be already bound (by the condition), or there
1601 // may not be a valid frame. Bind it only if needed.
1602 if (node->break_target()->is_linked()) {
1603 node->break_target()->Bind();
1604 }
1605 DecrementLoopNesting();
1606}
1607
1608
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00001609void CodeGenerator::SetTypeForStackSlot(Slot* slot, TypeInfo info) {
1610 ASSERT(slot->type() == Slot::LOCAL || slot->type() == Slot::PARAMETER);
1611 if (slot->type() == Slot::LOCAL) {
1612 frame_->SetTypeForLocalAt(slot->index(), info);
1613 } else {
1614 frame_->SetTypeForParamAt(slot->index(), info);
1615 }
1616 if (FLAG_debug_code && info.IsSmi()) {
1617 if (slot->type() == Slot::LOCAL) {
1618 frame_->PushLocalAt(slot->index());
1619 } else {
1620 frame_->PushParameterAt(slot->index());
1621 }
1622 Result var = frame_->Pop();
1623 var.ToRegister();
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001624 __ AbortIfNotSmi(var.reg());
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00001625 }
1626}
1627
1628
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001629void CodeGenerator::VisitForStatement(ForStatement* node) {
1630 ASSERT(!in_spilled_code());
1631 Comment cmnt(masm_, "[ ForStatement");
1632 CodeForStatementPosition(node);
1633
1634 // Compile the init expression if present.
1635 if (node->init() != NULL) {
1636 Visit(node->init());
1637 }
1638
1639 // If the condition is always false and has no side effects, we do not
1640 // need to compile anything else.
1641 ConditionAnalysis info = AnalyzeCondition(node->cond());
1642 if (info == ALWAYS_FALSE) return;
1643
1644 // Do not duplicate conditions that may have function literal
1645 // subexpressions. This can cause us to compile the function literal
1646 // twice.
1647 bool test_at_bottom = !node->may_have_function_literal();
1648 node->break_target()->set_direction(JumpTarget::FORWARD_ONLY);
1649 IncrementLoopNesting();
1650
1651 // Target for backward edge if no test at the bottom, otherwise
1652 // unused.
1653 JumpTarget loop(JumpTarget::BIDIRECTIONAL);
1654
1655 // Target for backward edge if there is a test at the bottom,
1656 // otherwise used as target for test at the top.
1657 JumpTarget body;
1658 if (test_at_bottom) {
1659 body.set_direction(JumpTarget::BIDIRECTIONAL);
1660 }
1661
1662 // Based on the condition analysis, compile the test as necessary.
1663 switch (info) {
1664 case ALWAYS_TRUE:
1665 // We will not compile the test expression. Label the top of the
1666 // loop.
1667 if (node->next() == NULL) {
1668 // Use the continue target if there is no update expression.
1669 node->continue_target()->set_direction(JumpTarget::BIDIRECTIONAL);
1670 node->continue_target()->Bind();
1671 } else {
1672 // Otherwise use the backward loop target.
1673 node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY);
1674 loop.Bind();
1675 }
1676 break;
1677 case DONT_KNOW: {
1678 if (test_at_bottom) {
1679 // Continue is either the update expression or the test at the
1680 // bottom, no need to label the test at the top.
1681 node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY);
1682 } else if (node->next() == NULL) {
1683 // We are not recompiling the test at the bottom and there is no
1684 // update expression.
1685 node->continue_target()->set_direction(JumpTarget::BIDIRECTIONAL);
1686 node->continue_target()->Bind();
1687 } else {
1688 // We are not recompiling the test at the bottom and there is an
1689 // update expression.
1690 node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY);
1691 loop.Bind();
1692 }
1693
1694 // Compile the test with the body as the true target and preferred
1695 // fall-through and with the break target as the false target.
1696 ControlDestination dest(&body, node->break_target(), true);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001697 LoadCondition(node->cond(), &dest, true);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001698
1699 if (dest.false_was_fall_through()) {
1700 // If we got the break target as fall-through, the test may have
1701 // been unconditionally false (if there are no jumps to the
1702 // body).
1703 if (!body.is_linked()) {
1704 DecrementLoopNesting();
1705 return;
1706 }
1707
1708 // Otherwise, jump around the body on the fall through and then
1709 // bind the body target.
1710 node->break_target()->Unuse();
1711 node->break_target()->Jump();
1712 body.Bind();
1713 }
1714 break;
1715 }
1716 case ALWAYS_FALSE:
1717 UNREACHABLE();
1718 break;
1719 }
1720
1721 CheckStack(); // TODO(1222600): ignore if body contains calls.
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00001722
1723 // We know that the loop index is a smi if it is not modified in the
1724 // loop body and it is checked against a constant limit in the loop
1725 // condition. In this case, we reset the static type information of the
1726 // loop index to smi before compiling the body, the update expression, and
1727 // the bottom check of the loop condition.
1728 if (node->is_fast_smi_loop()) {
1729 // Set number type of the loop variable to smi.
1730 SetTypeForStackSlot(node->loop_variable()->slot(), TypeInfo::Smi());
1731 }
1732
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001733 Visit(node->body());
1734
1735 // If there is an update expression, compile it if necessary.
1736 if (node->next() != NULL) {
1737 if (node->continue_target()->is_linked()) {
1738 node->continue_target()->Bind();
1739 }
1740
1741 // Control can reach the update by falling out of the body or by a
1742 // continue.
1743 if (has_valid_frame()) {
1744 // Record the source position of the statement as this code which
1745 // is after the code for the body actually belongs to the loop
1746 // statement and not the body.
1747 CodeForStatementPosition(node);
1748 Visit(node->next());
1749 }
1750 }
1751
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00001752 // Set the type of the loop variable to smi before compiling the test
1753 // expression if we are in a fast smi loop condition.
1754 if (node->is_fast_smi_loop() && has_valid_frame()) {
1755 // Set number type of the loop variable to smi.
1756 SetTypeForStackSlot(node->loop_variable()->slot(), TypeInfo::Smi());
1757 }
1758
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001759 // Based on the condition analysis, compile the backward jump as
1760 // necessary.
1761 switch (info) {
1762 case ALWAYS_TRUE:
1763 if (has_valid_frame()) {
1764 if (node->next() == NULL) {
1765 node->continue_target()->Jump();
1766 } else {
1767 loop.Jump();
1768 }
1769 }
1770 break;
1771 case DONT_KNOW:
1772 if (test_at_bottom) {
1773 if (node->continue_target()->is_linked()) {
1774 // We can have dangling jumps to the continue target if there
1775 // was no update expression.
1776 node->continue_target()->Bind();
1777 }
1778 // Control can reach the test at the bottom by falling out of
1779 // the body, by a continue in the body, or from the update
1780 // expression.
1781 if (has_valid_frame()) {
1782 // The break target is the fall-through (body is a backward
1783 // jump from here).
1784 ControlDestination dest(&body, node->break_target(), false);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001785 LoadCondition(node->cond(), &dest, true);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001786 }
1787 } else {
1788 // Otherwise, jump back to the test at the top.
ager@chromium.org3e875802009-06-29 08:26:34 +00001789 if (has_valid_frame()) {
1790 if (node->next() == NULL) {
1791 node->continue_target()->Jump();
1792 } else {
1793 loop.Jump();
1794 }
1795 }
ager@chromium.org3e875802009-06-29 08:26:34 +00001796 }
1797 break;
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001798 case ALWAYS_FALSE:
1799 UNREACHABLE();
1800 break;
ager@chromium.org3e875802009-06-29 08:26:34 +00001801 }
1802
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001803 // The break target may be already bound (by the condition), or there
1804 // may not be a valid frame. Bind it only if needed.
1805 if (node->break_target()->is_linked()) {
1806 node->break_target()->Bind();
1807 }
ager@chromium.org3e875802009-06-29 08:26:34 +00001808 DecrementLoopNesting();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001809}
1810
ager@chromium.org3e875802009-06-29 08:26:34 +00001811
1812void CodeGenerator::VisitForInStatement(ForInStatement* node) {
1813 ASSERT(!in_spilled_code());
1814 VirtualFrame::SpilledScope spilled_scope;
1815 Comment cmnt(masm_, "[ ForInStatement");
1816 CodeForStatementPosition(node);
1817
1818 JumpTarget primitive;
1819 JumpTarget jsobject;
1820 JumpTarget fixed_array;
1821 JumpTarget entry(JumpTarget::BIDIRECTIONAL);
1822 JumpTarget end_del_check;
1823 JumpTarget exit;
1824
1825 // Get the object to enumerate over (converted to JSObject).
1826 LoadAndSpill(node->enumerable());
1827
1828 // Both SpiderMonkey and kjs ignore null and undefined in contrast
1829 // to the specification. 12.6.4 mandates a call to ToObject.
1830 frame_->EmitPop(rax);
1831
1832 // rax: value to be iterated over
ager@chromium.org18ad94b2009-09-02 08:22:29 +00001833 __ CompareRoot(rax, Heap::kUndefinedValueRootIndex);
ager@chromium.org3e875802009-06-29 08:26:34 +00001834 exit.Branch(equal);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00001835 __ CompareRoot(rax, Heap::kNullValueRootIndex);
ager@chromium.org3e875802009-06-29 08:26:34 +00001836 exit.Branch(equal);
1837
1838 // Stack layout in body:
1839 // [iteration counter (smi)] <- slot 0
1840 // [length of array] <- slot 1
1841 // [FixedArray] <- slot 2
1842 // [Map or 0] <- slot 3
1843 // [Object] <- slot 4
1844
1845 // Check if enumerable is already a JSObject
1846 // rax: value to be iterated over
ager@chromium.org4af710e2009-09-15 12:20:11 +00001847 Condition is_smi = masm_->CheckSmi(rax);
1848 primitive.Branch(is_smi);
ager@chromium.org3e875802009-06-29 08:26:34 +00001849 __ CmpObjectType(rax, FIRST_JS_OBJECT_TYPE, rcx);
1850 jsobject.Branch(above_equal);
1851
1852 primitive.Bind();
1853 frame_->EmitPush(rax);
1854 frame_->InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION, 1);
1855 // function call returns the value in rax, which is where we want it below
1856
1857 jsobject.Bind();
1858 // Get the set of properties (as a FixedArray or Map).
1859 // rax: value to be iterated over
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001860 frame_->EmitPush(rax); // Push the object being iterated over.
ager@chromium.org3e875802009-06-29 08:26:34 +00001861
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001862
1863 // Check cache validity in generated code. This is a fast case for
1864 // the JSObject::IsSimpleEnum cache validity checks. If we cannot
1865 // guarantee cache validity, call the runtime system to check cache
1866 // validity or get the property names in a fixed array.
1867 JumpTarget call_runtime;
1868 JumpTarget loop(JumpTarget::BIDIRECTIONAL);
1869 JumpTarget check_prototype;
1870 JumpTarget use_cache;
1871 __ movq(rcx, rax);
1872 loop.Bind();
1873 // Check that there are no elements.
1874 __ movq(rdx, FieldOperand(rcx, JSObject::kElementsOffset));
1875 __ CompareRoot(rdx, Heap::kEmptyFixedArrayRootIndex);
1876 call_runtime.Branch(not_equal);
1877 // Check that instance descriptors are not empty so that we can
1878 // check for an enum cache. Leave the map in ebx for the subsequent
1879 // prototype load.
1880 __ movq(rbx, FieldOperand(rcx, HeapObject::kMapOffset));
1881 __ movq(rdx, FieldOperand(rbx, Map::kInstanceDescriptorsOffset));
1882 __ CompareRoot(rdx, Heap::kEmptyDescriptorArrayRootIndex);
1883 call_runtime.Branch(equal);
1884 // Check that there in an enum cache in the non-empty instance
1885 // descriptors. This is the case if the next enumeration index
1886 // field does not contain a smi.
1887 __ movq(rdx, FieldOperand(rdx, DescriptorArray::kEnumerationIndexOffset));
1888 is_smi = masm_->CheckSmi(rdx);
1889 call_runtime.Branch(is_smi);
1890 // For all objects but the receiver, check that the cache is empty.
1891 __ cmpq(rcx, rax);
1892 check_prototype.Branch(equal);
1893 __ movq(rdx, FieldOperand(rdx, DescriptorArray::kEnumCacheBridgeCacheOffset));
1894 __ CompareRoot(rdx, Heap::kEmptyFixedArrayRootIndex);
1895 call_runtime.Branch(not_equal);
1896 check_prototype.Bind();
1897 // Load the prototype from the map and loop if non-null.
1898 __ movq(rcx, FieldOperand(rbx, Map::kPrototypeOffset));
1899 __ CompareRoot(rcx, Heap::kNullValueRootIndex);
1900 loop.Branch(not_equal);
1901 // The enum cache is valid. Load the map of the object being
1902 // iterated over and use the cache for the iteration.
1903 __ movq(rax, FieldOperand(rax, HeapObject::kMapOffset));
1904 use_cache.Jump();
1905
1906 call_runtime.Bind();
1907 // Call the runtime to get the property names for the object.
ager@chromium.org3e875802009-06-29 08:26:34 +00001908 frame_->EmitPush(rax); // push the Object (slot 4) for the runtime call
1909 frame_->CallRuntime(Runtime::kGetPropertyNamesFast, 1);
1910
1911 // If we got a Map, we can do a fast modification check.
1912 // Otherwise, we got a FixedArray, and we have to do a slow check.
1913 // rax: map or fixed array (result from call to
1914 // Runtime::kGetPropertyNamesFast)
1915 __ movq(rdx, rax);
1916 __ movq(rcx, FieldOperand(rdx, HeapObject::kMapOffset));
ager@chromium.org18ad94b2009-09-02 08:22:29 +00001917 __ CompareRoot(rcx, Heap::kMetaMapRootIndex);
ager@chromium.org3e875802009-06-29 08:26:34 +00001918 fixed_array.Branch(not_equal);
1919
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001920 use_cache.Bind();
ager@chromium.org3e875802009-06-29 08:26:34 +00001921 // Get enum cache
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001922 // rax: map (either the result from a call to
1923 // Runtime::kGetPropertyNamesFast or has been fetched directly from
1924 // the object)
ager@chromium.org3e875802009-06-29 08:26:34 +00001925 __ movq(rcx, rax);
1926 __ movq(rcx, FieldOperand(rcx, Map::kInstanceDescriptorsOffset));
1927 // Get the bridge array held in the enumeration index field.
1928 __ movq(rcx, FieldOperand(rcx, DescriptorArray::kEnumerationIndexOffset));
1929 // Get the cache from the bridge array.
1930 __ movq(rdx, FieldOperand(rcx, DescriptorArray::kEnumCacheBridgeCacheOffset));
1931
1932 frame_->EmitPush(rax); // <- slot 3
1933 frame_->EmitPush(rdx); // <- slot 2
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001934 __ movq(rax, FieldOperand(rdx, FixedArray::kLengthOffset));
ager@chromium.org3e875802009-06-29 08:26:34 +00001935 frame_->EmitPush(rax); // <- slot 1
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001936 frame_->EmitPush(Smi::FromInt(0)); // <- slot 0
ager@chromium.org3e875802009-06-29 08:26:34 +00001937 entry.Jump();
1938
1939 fixed_array.Bind();
1940 // rax: fixed array (result from call to Runtime::kGetPropertyNamesFast)
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001941 frame_->EmitPush(Smi::FromInt(0)); // <- slot 3
ager@chromium.org3e875802009-06-29 08:26:34 +00001942 frame_->EmitPush(rax); // <- slot 2
1943
1944 // Push the length of the array and the initial index onto the stack.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001945 __ movq(rax, FieldOperand(rax, FixedArray::kLengthOffset));
ager@chromium.org3e875802009-06-29 08:26:34 +00001946 frame_->EmitPush(rax); // <- slot 1
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001947 frame_->EmitPush(Smi::FromInt(0)); // <- slot 0
ager@chromium.org3e875802009-06-29 08:26:34 +00001948
1949 // Condition.
1950 entry.Bind();
1951 // Grab the current frame's height for the break and continue
1952 // targets only after all the state is pushed on the frame.
1953 node->break_target()->set_direction(JumpTarget::FORWARD_ONLY);
1954 node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY);
1955
1956 __ movq(rax, frame_->ElementAt(0)); // load the current count
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001957 __ SmiCompare(frame_->ElementAt(1), rax); // compare to the array length
1958 node->break_target()->Branch(below_equal);
ager@chromium.org3e875802009-06-29 08:26:34 +00001959
1960 // Get the i'th entry of the array.
1961 __ movq(rdx, frame_->ElementAt(2));
ager@chromium.org4af710e2009-09-15 12:20:11 +00001962 SmiIndex index = masm_->SmiToIndex(rbx, rax, kPointerSizeLog2);
1963 __ movq(rbx,
1964 FieldOperand(rdx, index.reg, index.scale, FixedArray::kHeaderSize));
ager@chromium.org3e875802009-06-29 08:26:34 +00001965
1966 // Get the expected map from the stack or a zero map in the
1967 // permanent slow case rax: current iteration count rbx: i'th entry
1968 // of the enum cache
1969 __ movq(rdx, frame_->ElementAt(3));
1970 // Check if the expected map still matches that of the enumerable.
1971 // If not, we have to filter the key.
1972 // rax: current iteration count
1973 // rbx: i'th entry of the enum cache
1974 // rdx: expected map value
1975 __ movq(rcx, frame_->ElementAt(4));
1976 __ movq(rcx, FieldOperand(rcx, HeapObject::kMapOffset));
1977 __ cmpq(rcx, rdx);
1978 end_del_check.Branch(equal);
1979
1980 // Convert the entry to a string (or null if it isn't a property anymore).
1981 frame_->EmitPush(frame_->ElementAt(4)); // push enumerable
1982 frame_->EmitPush(rbx); // push entry
1983 frame_->InvokeBuiltin(Builtins::FILTER_KEY, CALL_FUNCTION, 2);
1984 __ movq(rbx, rax);
1985
1986 // If the property has been removed while iterating, we just skip it.
ager@chromium.org18ad94b2009-09-02 08:22:29 +00001987 __ CompareRoot(rbx, Heap::kNullValueRootIndex);
ager@chromium.org3e875802009-06-29 08:26:34 +00001988 node->continue_target()->Branch(equal);
1989
1990 end_del_check.Bind();
1991 // Store the entry in the 'each' expression and take another spin in the
1992 // loop. rdx: i'th entry of the enum cache (or string there of)
1993 frame_->EmitPush(rbx);
1994 { Reference each(this, node->each());
1995 // Loading a reference may leave the frame in an unspilled state.
1996 frame_->SpillAll();
1997 if (!each.is_illegal()) {
1998 if (each.size() > 0) {
1999 frame_->EmitPush(frame_->ElementAt(each.size()));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002000 each.SetValue(NOT_CONST_INIT);
2001 frame_->Drop(2); // Drop the original and the copy of the element.
2002 } else {
2003 // If the reference has size zero then we can use the value below
2004 // the reference as if it were above the reference, instead of pushing
2005 // a new copy of it above the reference.
2006 each.SetValue(NOT_CONST_INIT);
2007 frame_->Drop(); // Drop the original of the element.
ager@chromium.org3e875802009-06-29 08:26:34 +00002008 }
2009 }
2010 }
2011 // Unloading a reference may leave the frame in an unspilled state.
2012 frame_->SpillAll();
2013
ager@chromium.org3e875802009-06-29 08:26:34 +00002014 // Body.
2015 CheckStack(); // TODO(1222600): ignore if body contains calls.
2016 VisitAndSpill(node->body());
2017
2018 // Next. Reestablish a spilled frame in case we are coming here via
2019 // a continue in the body.
2020 node->continue_target()->Bind();
2021 frame_->SpillAll();
2022 frame_->EmitPop(rax);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002023 __ SmiAddConstant(rax, rax, Smi::FromInt(1));
ager@chromium.org3e875802009-06-29 08:26:34 +00002024 frame_->EmitPush(rax);
2025 entry.Jump();
2026
2027 // Cleanup. No need to spill because VirtualFrame::Drop is safe for
2028 // any frame.
2029 node->break_target()->Bind();
2030 frame_->Drop(5);
2031
2032 // Exit.
2033 exit.Bind();
2034
2035 node->continue_target()->Unuse();
2036 node->break_target()->Unuse();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002037}
2038
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002039void CodeGenerator::VisitTryCatchStatement(TryCatchStatement* node) {
ager@chromium.org3e875802009-06-29 08:26:34 +00002040 ASSERT(!in_spilled_code());
2041 VirtualFrame::SpilledScope spilled_scope;
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002042 Comment cmnt(masm_, "[ TryCatchStatement");
ager@chromium.org3e875802009-06-29 08:26:34 +00002043 CodeForStatementPosition(node);
2044
2045 JumpTarget try_block;
2046 JumpTarget exit;
2047
2048 try_block.Call();
2049 // --- Catch block ---
2050 frame_->EmitPush(rax);
2051
2052 // Store the caught exception in the catch variable.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002053 Variable* catch_var = node->catch_var()->var();
2054 ASSERT(catch_var != NULL && catch_var->slot() != NULL);
2055 StoreToSlot(catch_var->slot(), NOT_CONST_INIT);
ager@chromium.org3e875802009-06-29 08:26:34 +00002056
2057 // Remove the exception from the stack.
2058 frame_->Drop();
2059
2060 VisitStatementsAndSpill(node->catch_block()->statements());
2061 if (has_valid_frame()) {
2062 exit.Jump();
2063 }
2064
2065
2066 // --- Try block ---
2067 try_block.Bind();
2068
2069 frame_->PushTryHandler(TRY_CATCH_HANDLER);
2070 int handler_height = frame_->height();
2071
2072 // Shadow the jump targets for all escapes from the try block, including
2073 // returns. During shadowing, the original target is hidden as the
2074 // ShadowTarget and operations on the original actually affect the
2075 // shadowing target.
2076 //
2077 // We should probably try to unify the escaping targets and the return
2078 // target.
2079 int nof_escapes = node->escaping_targets()->length();
2080 List<ShadowTarget*> shadows(1 + nof_escapes);
2081
2082 // Add the shadow target for the function return.
2083 static const int kReturnShadowIndex = 0;
2084 shadows.Add(new ShadowTarget(&function_return_));
2085 bool function_return_was_shadowed = function_return_is_shadowed_;
2086 function_return_is_shadowed_ = true;
2087 ASSERT(shadows[kReturnShadowIndex]->other_target() == &function_return_);
2088
2089 // Add the remaining shadow targets.
2090 for (int i = 0; i < nof_escapes; i++) {
2091 shadows.Add(new ShadowTarget(node->escaping_targets()->at(i)));
2092 }
2093
2094 // Generate code for the statements in the try block.
2095 VisitStatementsAndSpill(node->try_block()->statements());
2096
2097 // Stop the introduced shadowing and count the number of required unlinks.
2098 // After shadowing stops, the original targets are unshadowed and the
2099 // ShadowTargets represent the formerly shadowing targets.
2100 bool has_unlinks = false;
2101 for (int i = 0; i < shadows.length(); i++) {
2102 shadows[i]->StopShadowing();
2103 has_unlinks = has_unlinks || shadows[i]->is_linked();
2104 }
2105 function_return_is_shadowed_ = function_return_was_shadowed;
2106
2107 // Get an external reference to the handler address.
2108 ExternalReference handler_address(Top::k_handler_address);
2109
2110 // Make sure that there's nothing left on the stack above the
2111 // handler structure.
2112 if (FLAG_debug_code) {
2113 __ movq(kScratchRegister, handler_address);
2114 __ cmpq(rsp, Operand(kScratchRegister, 0));
2115 __ Assert(equal, "stack pointer should point to top handler");
2116 }
2117
2118 // If we can fall off the end of the try block, unlink from try chain.
2119 if (has_valid_frame()) {
2120 // The next handler address is on top of the frame. Unlink from
2121 // the handler list and drop the rest of this handler from the
2122 // frame.
2123 ASSERT(StackHandlerConstants::kNextOffset == 0);
2124 __ movq(kScratchRegister, handler_address);
2125 frame_->EmitPop(Operand(kScratchRegister, 0));
2126 frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1);
2127 if (has_unlinks) {
2128 exit.Jump();
2129 }
2130 }
2131
2132 // Generate unlink code for the (formerly) shadowing targets that
2133 // have been jumped to. Deallocate each shadow target.
2134 Result return_value;
2135 for (int i = 0; i < shadows.length(); i++) {
2136 if (shadows[i]->is_linked()) {
2137 // Unlink from try chain; be careful not to destroy the TOS if
2138 // there is one.
2139 if (i == kReturnShadowIndex) {
2140 shadows[i]->Bind(&return_value);
2141 return_value.ToRegister(rax);
2142 } else {
2143 shadows[i]->Bind();
2144 }
2145 // Because we can be jumping here (to spilled code) from
2146 // unspilled code, we need to reestablish a spilled frame at
2147 // this block.
2148 frame_->SpillAll();
2149
2150 // Reload sp from the top handler, because some statements that we
2151 // break from (eg, for...in) may have left stuff on the stack.
2152 __ movq(kScratchRegister, handler_address);
2153 __ movq(rsp, Operand(kScratchRegister, 0));
2154 frame_->Forget(frame_->height() - handler_height);
2155
2156 ASSERT(StackHandlerConstants::kNextOffset == 0);
2157 __ movq(kScratchRegister, handler_address);
2158 frame_->EmitPop(Operand(kScratchRegister, 0));
2159 frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1);
2160
2161 if (i == kReturnShadowIndex) {
2162 if (!function_return_is_shadowed_) frame_->PrepareForReturn();
2163 shadows[i]->other_target()->Jump(&return_value);
2164 } else {
2165 shadows[i]->other_target()->Jump();
2166 }
2167 }
2168 }
2169
2170 exit.Bind();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002171}
2172
ager@chromium.org3e875802009-06-29 08:26:34 +00002173
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002174void CodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* node) {
ager@chromium.org3e875802009-06-29 08:26:34 +00002175 ASSERT(!in_spilled_code());
2176 VirtualFrame::SpilledScope spilled_scope;
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002177 Comment cmnt(masm_, "[ TryFinallyStatement");
ager@chromium.org3e875802009-06-29 08:26:34 +00002178 CodeForStatementPosition(node);
2179
2180 // State: Used to keep track of reason for entering the finally
2181 // block. Should probably be extended to hold information for
2182 // break/continue from within the try block.
2183 enum { FALLING, THROWING, JUMPING };
2184
2185 JumpTarget try_block;
2186 JumpTarget finally_block;
2187
2188 try_block.Call();
2189
2190 frame_->EmitPush(rax);
2191 // In case of thrown exceptions, this is where we continue.
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002192 __ Move(rcx, Smi::FromInt(THROWING));
ager@chromium.org3e875802009-06-29 08:26:34 +00002193 finally_block.Jump();
2194
2195 // --- Try block ---
2196 try_block.Bind();
2197
2198 frame_->PushTryHandler(TRY_FINALLY_HANDLER);
2199 int handler_height = frame_->height();
2200
2201 // Shadow the jump targets for all escapes from the try block, including
2202 // returns. During shadowing, the original target is hidden as the
2203 // ShadowTarget and operations on the original actually affect the
2204 // shadowing target.
2205 //
2206 // We should probably try to unify the escaping targets and the return
2207 // target.
2208 int nof_escapes = node->escaping_targets()->length();
2209 List<ShadowTarget*> shadows(1 + nof_escapes);
2210
2211 // Add the shadow target for the function return.
2212 static const int kReturnShadowIndex = 0;
2213 shadows.Add(new ShadowTarget(&function_return_));
2214 bool function_return_was_shadowed = function_return_is_shadowed_;
2215 function_return_is_shadowed_ = true;
2216 ASSERT(shadows[kReturnShadowIndex]->other_target() == &function_return_);
2217
2218 // Add the remaining shadow targets.
2219 for (int i = 0; i < nof_escapes; i++) {
2220 shadows.Add(new ShadowTarget(node->escaping_targets()->at(i)));
2221 }
2222
2223 // Generate code for the statements in the try block.
2224 VisitStatementsAndSpill(node->try_block()->statements());
2225
2226 // Stop the introduced shadowing and count the number of required unlinks.
2227 // After shadowing stops, the original targets are unshadowed and the
2228 // ShadowTargets represent the formerly shadowing targets.
2229 int nof_unlinks = 0;
2230 for (int i = 0; i < shadows.length(); i++) {
2231 shadows[i]->StopShadowing();
2232 if (shadows[i]->is_linked()) nof_unlinks++;
2233 }
2234 function_return_is_shadowed_ = function_return_was_shadowed;
2235
2236 // Get an external reference to the handler address.
2237 ExternalReference handler_address(Top::k_handler_address);
2238
2239 // If we can fall off the end of the try block, unlink from the try
2240 // chain and set the state on the frame to FALLING.
2241 if (has_valid_frame()) {
2242 // The next handler address is on top of the frame.
2243 ASSERT(StackHandlerConstants::kNextOffset == 0);
2244 __ movq(kScratchRegister, handler_address);
2245 frame_->EmitPop(Operand(kScratchRegister, 0));
2246 frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1);
2247
2248 // Fake a top of stack value (unneeded when FALLING) and set the
2249 // state in ecx, then jump around the unlink blocks if any.
ager@chromium.org18ad94b2009-09-02 08:22:29 +00002250 frame_->EmitPush(Heap::kUndefinedValueRootIndex);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002251 __ Move(rcx, Smi::FromInt(FALLING));
ager@chromium.org3e875802009-06-29 08:26:34 +00002252 if (nof_unlinks > 0) {
2253 finally_block.Jump();
2254 }
2255 }
2256
2257 // Generate code to unlink and set the state for the (formerly)
2258 // shadowing targets that have been jumped to.
2259 for (int i = 0; i < shadows.length(); i++) {
2260 if (shadows[i]->is_linked()) {
2261 // If we have come from the shadowed return, the return value is
2262 // on the virtual frame. We must preserve it until it is
2263 // pushed.
2264 if (i == kReturnShadowIndex) {
2265 Result return_value;
2266 shadows[i]->Bind(&return_value);
2267 return_value.ToRegister(rax);
2268 } else {
2269 shadows[i]->Bind();
2270 }
2271 // Because we can be jumping here (to spilled code) from
2272 // unspilled code, we need to reestablish a spilled frame at
2273 // this block.
2274 frame_->SpillAll();
2275
2276 // Reload sp from the top handler, because some statements that
2277 // we break from (eg, for...in) may have left stuff on the
2278 // stack.
2279 __ movq(kScratchRegister, handler_address);
2280 __ movq(rsp, Operand(kScratchRegister, 0));
2281 frame_->Forget(frame_->height() - handler_height);
2282
2283 // Unlink this handler and drop it from the frame.
2284 ASSERT(StackHandlerConstants::kNextOffset == 0);
2285 __ movq(kScratchRegister, handler_address);
2286 frame_->EmitPop(Operand(kScratchRegister, 0));
2287 frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1);
2288
2289 if (i == kReturnShadowIndex) {
2290 // If this target shadowed the function return, materialize
2291 // the return value on the stack.
2292 frame_->EmitPush(rax);
2293 } else {
2294 // Fake TOS for targets that shadowed breaks and continues.
ager@chromium.org18ad94b2009-09-02 08:22:29 +00002295 frame_->EmitPush(Heap::kUndefinedValueRootIndex);
ager@chromium.org3e875802009-06-29 08:26:34 +00002296 }
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002297 __ Move(rcx, Smi::FromInt(JUMPING + i));
ager@chromium.org3e875802009-06-29 08:26:34 +00002298 if (--nof_unlinks > 0) {
2299 // If this is not the last unlink block, jump around the next.
2300 finally_block.Jump();
2301 }
2302 }
2303 }
2304
2305 // --- Finally block ---
2306 finally_block.Bind();
2307
2308 // Push the state on the stack.
2309 frame_->EmitPush(rcx);
2310
2311 // We keep two elements on the stack - the (possibly faked) result
2312 // and the state - while evaluating the finally block.
2313 //
2314 // Generate code for the statements in the finally block.
2315 VisitStatementsAndSpill(node->finally_block()->statements());
2316
2317 if (has_valid_frame()) {
2318 // Restore state and return value or faked TOS.
2319 frame_->EmitPop(rcx);
2320 frame_->EmitPop(rax);
2321 }
2322
2323 // Generate code to jump to the right destination for all used
2324 // formerly shadowing targets. Deallocate each shadow target.
2325 for (int i = 0; i < shadows.length(); i++) {
2326 if (has_valid_frame() && shadows[i]->is_bound()) {
2327 BreakTarget* original = shadows[i]->other_target();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002328 __ SmiCompare(rcx, Smi::FromInt(JUMPING + i));
ager@chromium.org3e875802009-06-29 08:26:34 +00002329 if (i == kReturnShadowIndex) {
2330 // The return value is (already) in rax.
2331 Result return_value = allocator_->Allocate(rax);
2332 ASSERT(return_value.is_valid());
2333 if (function_return_is_shadowed_) {
2334 original->Branch(equal, &return_value);
2335 } else {
2336 // Branch around the preparation for return which may emit
2337 // code.
2338 JumpTarget skip;
2339 skip.Branch(not_equal);
2340 frame_->PrepareForReturn();
2341 original->Jump(&return_value);
2342 skip.Bind();
2343 }
2344 } else {
2345 original->Branch(equal);
2346 }
2347 }
2348 }
2349
2350 if (has_valid_frame()) {
2351 // Check if we need to rethrow the exception.
2352 JumpTarget exit;
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002353 __ SmiCompare(rcx, Smi::FromInt(THROWING));
ager@chromium.org3e875802009-06-29 08:26:34 +00002354 exit.Branch(not_equal);
2355
2356 // Rethrow exception.
2357 frame_->EmitPush(rax); // undo pop from above
2358 frame_->CallRuntime(Runtime::kReThrow, 1);
2359
2360 // Done.
2361 exit.Bind();
2362 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002363}
2364
ager@chromium.org3e875802009-06-29 08:26:34 +00002365
2366void CodeGenerator::VisitDebuggerStatement(DebuggerStatement* node) {
2367 ASSERT(!in_spilled_code());
2368 Comment cmnt(masm_, "[ DebuggerStatement");
2369 CodeForStatementPosition(node);
2370#ifdef ENABLE_DEBUGGER_SUPPORT
2371 // Spill everything, even constants, to the frame.
2372 frame_->SpillAll();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002373
ager@chromium.org5c838252010-02-19 08:53:10 +00002374 frame_->DebugBreak();
ager@chromium.org3e875802009-06-29 08:26:34 +00002375 // Ignore the return value.
2376#endif
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002377}
2378
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002379
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00002380void CodeGenerator::InstantiateFunction(
2381 Handle<SharedFunctionInfo> function_info) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002382 // The inevitable call will sync frame elements to memory anyway, so
2383 // we do it eagerly to allow us to push the arguments directly into
2384 // place.
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002385 frame_->SyncRange(0, frame_->element_count() - 1);
2386
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002387 // Use the fast case closure allocation code that allocates in new
2388 // space for nested functions that don't need literals cloning.
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00002389 if (scope()->is_function_scope() && function_info->num_literals() == 0) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002390 FastNewClosureStub stub;
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00002391 frame_->Push(function_info);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002392 Result answer = frame_->CallStub(&stub, 1);
2393 frame_->Push(&answer);
2394 } else {
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00002395 // Call the runtime to instantiate the function based on the
2396 // shared function info.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002397 frame_->EmitPush(rsi);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00002398 frame_->EmitPush(function_info);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002399 Result result = frame_->CallRuntime(Runtime::kNewClosure, 2);
2400 frame_->Push(&result);
2401 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002402}
2403
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002404
2405void CodeGenerator::VisitFunctionLiteral(FunctionLiteral* node) {
2406 Comment cmnt(masm_, "[ FunctionLiteral");
2407
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00002408 // Build the function info and instantiate it.
2409 Handle<SharedFunctionInfo> function_info =
2410 Compiler::BuildFunctionInfo(node, script(), this);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002411 // Check for stack-overflow exception.
2412 if (HasStackOverflow()) return;
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00002413 InstantiateFunction(function_info);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002414}
2415
2416
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00002417void CodeGenerator::VisitSharedFunctionInfoLiteral(
2418 SharedFunctionInfoLiteral* node) {
2419 Comment cmnt(masm_, "[ SharedFunctionInfoLiteral");
2420 InstantiateFunction(node->shared_function_info());
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002421}
2422
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002423
2424void CodeGenerator::VisitConditional(Conditional* node) {
2425 Comment cmnt(masm_, "[ Conditional");
2426 JumpTarget then;
2427 JumpTarget else_;
2428 JumpTarget exit;
2429 ControlDestination dest(&then, &else_, true);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002430 LoadCondition(node->condition(), &dest, true);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002431
2432 if (dest.false_was_fall_through()) {
2433 // The else target was bound, so we compile the else part first.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002434 Load(node->else_expression());
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002435
2436 if (then.is_linked()) {
2437 exit.Jump();
2438 then.Bind();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002439 Load(node->then_expression());
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002440 }
2441 } else {
2442 // The then target was bound, so we compile the then part first.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002443 Load(node->then_expression());
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002444
2445 if (else_.is_linked()) {
2446 exit.Jump();
2447 else_.Bind();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002448 Load(node->else_expression());
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002449 }
2450 }
2451
2452 exit.Bind();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002453}
2454
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002455
2456void CodeGenerator::VisitSlot(Slot* node) {
2457 Comment cmnt(masm_, "[ Slot");
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002458 LoadFromSlotCheckForArguments(node, NOT_INSIDE_TYPEOF);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002459}
2460
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002461
2462void CodeGenerator::VisitVariableProxy(VariableProxy* node) {
2463 Comment cmnt(masm_, "[ VariableProxy");
2464 Variable* var = node->var();
2465 Expression* expr = var->rewrite();
2466 if (expr != NULL) {
2467 Visit(expr);
2468 } else {
2469 ASSERT(var->is_global());
2470 Reference ref(this, node);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002471 ref.GetValue();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002472 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002473}
2474
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002475
2476void CodeGenerator::VisitLiteral(Literal* node) {
2477 Comment cmnt(masm_, "[ Literal");
2478 frame_->Push(node->handle());
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002479}
2480
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002481
2482// Materialize the regexp literal 'node' in the literals array
2483// 'literals' of the function. Leave the regexp boilerplate in
2484// 'boilerplate'.
2485class DeferredRegExpLiteral: public DeferredCode {
2486 public:
2487 DeferredRegExpLiteral(Register boilerplate,
2488 Register literals,
2489 RegExpLiteral* node)
2490 : boilerplate_(boilerplate), literals_(literals), node_(node) {
2491 set_comment("[ DeferredRegExpLiteral");
2492 }
2493
2494 void Generate();
2495
2496 private:
2497 Register boilerplate_;
2498 Register literals_;
2499 RegExpLiteral* node_;
2500};
2501
2502
2503void DeferredRegExpLiteral::Generate() {
2504 // Since the entry is undefined we call the runtime system to
2505 // compute the literal.
2506 // Literal array (0).
2507 __ push(literals_);
2508 // Literal index (1).
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002509 __ Push(Smi::FromInt(node_->literal_index()));
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002510 // RegExp pattern (2).
2511 __ Push(node_->pattern());
2512 // RegExp flags (3).
2513 __ Push(node_->flags());
2514 __ CallRuntime(Runtime::kMaterializeRegExpLiteral, 4);
2515 if (!boilerplate_.is(rax)) __ movq(boilerplate_, rax);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002516}
2517
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002518
2519void CodeGenerator::VisitRegExpLiteral(RegExpLiteral* node) {
2520 Comment cmnt(masm_, "[ RegExp Literal");
2521
2522 // Retrieve the literals array and check the allocated entry. Begin
2523 // with a writable copy of the function of this activation in a
2524 // register.
2525 frame_->PushFunction();
2526 Result literals = frame_->Pop();
2527 literals.ToRegister();
2528 frame_->Spill(literals.reg());
2529
2530 // Load the literals array of the function.
2531 __ movq(literals.reg(),
2532 FieldOperand(literals.reg(), JSFunction::kLiteralsOffset));
2533
2534 // Load the literal at the ast saved index.
2535 Result boilerplate = allocator_->Allocate();
2536 ASSERT(boilerplate.is_valid());
2537 int literal_offset =
2538 FixedArray::kHeaderSize + node->literal_index() * kPointerSize;
2539 __ movq(boilerplate.reg(), FieldOperand(literals.reg(), literal_offset));
2540
2541 // Check whether we need to materialize the RegExp object. If so,
2542 // jump to the deferred code passing the literals array.
2543 DeferredRegExpLiteral* deferred =
2544 new DeferredRegExpLiteral(boilerplate.reg(), literals.reg(), node);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00002545 __ CompareRoot(boilerplate.reg(), Heap::kUndefinedValueRootIndex);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002546 deferred->Branch(equal);
2547 deferred->BindExit();
2548 literals.Unuse();
2549
2550 // Push the boilerplate object.
2551 frame_->Push(&boilerplate);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002552}
2553
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002554
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002555void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) {
2556 Comment cmnt(masm_, "[ ObjectLiteral");
2557
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002558 // Load a writable copy of the function of this activation in a
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002559 // register.
2560 frame_->PushFunction();
2561 Result literals = frame_->Pop();
2562 literals.ToRegister();
2563 frame_->Spill(literals.reg());
2564
2565 // Load the literals array of the function.
2566 __ movq(literals.reg(),
2567 FieldOperand(literals.reg(), JSFunction::kLiteralsOffset));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002568 // Literal array.
2569 frame_->Push(&literals);
2570 // Literal index.
2571 frame_->Push(Smi::FromInt(node->literal_index()));
2572 // Constant properties.
2573 frame_->Push(node->constant_properties());
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00002574 // Should the object literal have fast elements?
2575 frame_->Push(Smi::FromInt(node->fast_elements() ? 1 : 0));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002576 Result clone;
2577 if (node->depth() > 1) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00002578 clone = frame_->CallRuntime(Runtime::kCreateObjectLiteral, 4);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002579 } else {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00002580 clone = frame_->CallRuntime(Runtime::kCreateObjectLiteralShallow, 4);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002581 }
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002582 frame_->Push(&clone);
2583
2584 for (int i = 0; i < node->properties()->length(); i++) {
2585 ObjectLiteral::Property* property = node->properties()->at(i);
2586 switch (property->kind()) {
2587 case ObjectLiteral::Property::CONSTANT:
2588 break;
2589 case ObjectLiteral::Property::MATERIALIZED_LITERAL:
2590 if (CompileTimeValue::IsCompileTimeValue(property->value())) break;
2591 // else fall through.
2592 case ObjectLiteral::Property::COMPUTED: {
2593 Handle<Object> key(property->key()->handle());
2594 if (key->IsSymbol()) {
2595 // Duplicate the object as the IC receiver.
2596 frame_->Dup();
2597 Load(property->value());
2598 frame_->Push(key);
2599 Result ignored = frame_->CallStoreIC();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002600 break;
2601 }
2602 // Fall through
2603 }
2604 case ObjectLiteral::Property::PROTOTYPE: {
2605 // Duplicate the object as an argument to the runtime call.
2606 frame_->Dup();
2607 Load(property->key());
2608 Load(property->value());
2609 Result ignored = frame_->CallRuntime(Runtime::kSetProperty, 3);
2610 // Ignore the result.
2611 break;
2612 }
2613 case ObjectLiteral::Property::SETTER: {
2614 // Duplicate the object as an argument to the runtime call.
2615 frame_->Dup();
2616 Load(property->key());
2617 frame_->Push(Smi::FromInt(1));
2618 Load(property->value());
2619 Result ignored = frame_->CallRuntime(Runtime::kDefineAccessor, 4);
2620 // Ignore the result.
2621 break;
2622 }
2623 case ObjectLiteral::Property::GETTER: {
2624 // Duplicate the object as an argument to the runtime call.
2625 frame_->Dup();
2626 Load(property->key());
2627 frame_->Push(Smi::FromInt(0));
2628 Load(property->value());
2629 Result ignored = frame_->CallRuntime(Runtime::kDefineAccessor, 4);
2630 // Ignore the result.
2631 break;
2632 }
2633 default: UNREACHABLE();
2634 }
2635 }
2636}
2637
2638
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002639void CodeGenerator::VisitArrayLiteral(ArrayLiteral* node) {
2640 Comment cmnt(masm_, "[ ArrayLiteral");
2641
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002642 // Load a writable copy of the function of this activation in a
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002643 // register.
2644 frame_->PushFunction();
2645 Result literals = frame_->Pop();
2646 literals.ToRegister();
2647 frame_->Spill(literals.reg());
2648
2649 // Load the literals array of the function.
2650 __ movq(literals.reg(),
2651 FieldOperand(literals.reg(), JSFunction::kLiteralsOffset));
ager@chromium.org5c838252010-02-19 08:53:10 +00002652
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002653 frame_->Push(&literals);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002654 frame_->Push(Smi::FromInt(node->literal_index()));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002655 frame_->Push(node->constant_elements());
ager@chromium.org5c838252010-02-19 08:53:10 +00002656 int length = node->values()->length();
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002657 Result clone;
2658 if (node->depth() > 1) {
2659 clone = frame_->CallRuntime(Runtime::kCreateArrayLiteral, 3);
ager@chromium.org5c838252010-02-19 08:53:10 +00002660 } else if (length > FastCloneShallowArrayStub::kMaximumLength) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002661 clone = frame_->CallRuntime(Runtime::kCreateArrayLiteralShallow, 3);
ager@chromium.org5c838252010-02-19 08:53:10 +00002662 } else {
2663 FastCloneShallowArrayStub stub(length);
2664 clone = frame_->CallStub(&stub, 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002665 }
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002666 frame_->Push(&clone);
2667
2668 // Generate code to set the elements in the array that are not
2669 // literals.
2670 for (int i = 0; i < node->values()->length(); i++) {
2671 Expression* value = node->values()->at(i);
2672
2673 // If value is a literal the property value is already set in the
2674 // boilerplate object.
2675 if (value->AsLiteral() != NULL) continue;
2676 // If value is a materialized literal the property value is already set
2677 // in the boilerplate object if it is simple.
2678 if (CompileTimeValue::IsCompileTimeValue(value)) continue;
2679
2680 // The property must be set by generated code.
2681 Load(value);
2682
2683 // Get the property value off the stack.
2684 Result prop_value = frame_->Pop();
2685 prop_value.ToRegister();
2686
2687 // Fetch the array literal while leaving a copy on the stack and
2688 // use it to get the elements array.
2689 frame_->Dup();
2690 Result elements = frame_->Pop();
2691 elements.ToRegister();
2692 frame_->Spill(elements.reg());
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00002693 // Get the elements FixedArray.
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002694 __ movq(elements.reg(),
2695 FieldOperand(elements.reg(), JSObject::kElementsOffset));
2696
2697 // Write to the indexed properties array.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00002698 int offset = i * kPointerSize + FixedArray::kHeaderSize;
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002699 __ movq(FieldOperand(elements.reg(), offset), prop_value.reg());
2700
2701 // Update the write barrier for the array address.
2702 frame_->Spill(prop_value.reg()); // Overwritten by the write barrier.
2703 Result scratch = allocator_->Allocate();
2704 ASSERT(scratch.is_valid());
2705 __ RecordWrite(elements.reg(), offset, prop_value.reg(), scratch.reg());
2706 }
2707}
2708
2709
ager@chromium.org3e875802009-06-29 08:26:34 +00002710void CodeGenerator::VisitCatchExtensionObject(CatchExtensionObject* node) {
2711 ASSERT(!in_spilled_code());
2712 // Call runtime routine to allocate the catch extension object and
2713 // assign the exception value to the catch variable.
2714 Comment cmnt(masm_, "[ CatchExtensionObject");
2715 Load(node->key());
2716 Load(node->value());
2717 Result result =
2718 frame_->CallRuntime(Runtime::kCreateCatchExtensionObject, 2);
2719 frame_->Push(&result);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002720}
2721
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002722
2723void CodeGenerator::VisitAssignment(Assignment* node) {
2724 Comment cmnt(masm_, "[ Assignment");
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002725
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002726 { Reference target(this, node->target(), node->is_compound());
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002727 if (target.is_illegal()) {
2728 // Fool the virtual frame into thinking that we left the assignment's
2729 // value on the frame.
2730 frame_->Push(Smi::FromInt(0));
2731 return;
2732 }
2733 Variable* var = node->target()->AsVariableProxy()->AsVariable();
2734
2735 if (node->starts_initialization_block()) {
2736 ASSERT(target.type() == Reference::NAMED ||
2737 target.type() == Reference::KEYED);
2738 // Change to slow case in the beginning of an initialization
2739 // block to avoid the quadratic behavior of repeatedly adding
2740 // fast properties.
2741
2742 // The receiver is the argument to the runtime call. It is the
2743 // first value pushed when the reference was loaded to the
2744 // frame.
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002745 frame_->PushElementAt(target.size() - 1);
2746 Result ignored = frame_->CallRuntime(Runtime::kToSlowProperties, 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002747 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002748 if (node->ends_initialization_block()) {
2749 // Add an extra copy of the receiver to the frame, so that it can be
2750 // converted back to fast case after the assignment.
2751 ASSERT(target.type() == Reference::NAMED ||
2752 target.type() == Reference::KEYED);
2753 if (target.type() == Reference::NAMED) {
2754 frame_->Dup();
2755 // Dup target receiver on stack.
2756 } else {
2757 ASSERT(target.type() == Reference::KEYED);
2758 Result temp = frame_->Pop();
2759 frame_->Dup();
2760 frame_->Push(&temp);
2761 }
2762 }
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002763 if (node->op() == Token::ASSIGN ||
2764 node->op() == Token::INIT_VAR ||
2765 node->op() == Token::INIT_CONST) {
2766 Load(node->value());
2767
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002768 } else { // Assignment is a compound assignment.
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002769 Literal* literal = node->value()->AsLiteral();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002770 bool overwrite_value =
2771 (node->value()->AsBinaryOperation() != NULL &&
2772 node->value()->AsBinaryOperation()->ResultOverwriteAllowed());
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002773 Variable* right_var = node->value()->AsVariableProxy()->AsVariable();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002774 // There are two cases where the target is not read in the right hand
2775 // side, that are easy to test for: the right hand side is a literal,
2776 // or the right hand side is a different variable. TakeValue invalidates
2777 // the target, with an implicit promise that it will be written to again
2778 // before it is read.
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002779 if (literal != NULL || (right_var != NULL && right_var != var)) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002780 target.TakeValue();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002781 } else {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002782 target.GetValue();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002783 }
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002784 Load(node->value());
ager@chromium.orgac091b72010-05-05 07:34:42 +00002785 BinaryOperation expr(node, node->binary_op(), node->target(),
2786 node->value());
2787 GenericBinaryOperation(&expr,
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002788 overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002789 }
2790
2791 if (var != NULL &&
2792 var->mode() == Variable::CONST &&
2793 node->op() != Token::INIT_VAR && node->op() != Token::INIT_CONST) {
2794 // Assignment ignored - leave the value on the stack.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002795 UnloadReference(&target);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002796 } else {
2797 CodeForSourcePosition(node->position());
2798 if (node->op() == Token::INIT_CONST) {
2799 // Dynamic constant initializations must use the function context
2800 // and initialize the actual constant declared. Dynamic variable
2801 // initializations are simply assignments and use SetValue.
2802 target.SetValue(CONST_INIT);
2803 } else {
2804 target.SetValue(NOT_CONST_INIT);
2805 }
2806 if (node->ends_initialization_block()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002807 ASSERT(target.type() == Reference::UNLOADED);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002808 // End of initialization block. Revert to fast case. The
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002809 // argument to the runtime call is the extra copy of the receiver,
2810 // which is below the value of the assignment.
2811 // Swap the receiver and the value of the assignment expression.
2812 Result lhs = frame_->Pop();
2813 Result receiver = frame_->Pop();
2814 frame_->Push(&lhs);
2815 frame_->Push(&receiver);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002816 Result ignored = frame_->CallRuntime(Runtime::kToFastProperties, 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002817 }
2818 }
2819 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002820}
2821
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002822
2823void CodeGenerator::VisitThrow(Throw* node) {
2824 Comment cmnt(masm_, "[ Throw");
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002825 Load(node->exception());
2826 Result result = frame_->CallRuntime(Runtime::kThrow, 1);
2827 frame_->Push(&result);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002828}
2829
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002830
2831void CodeGenerator::VisitProperty(Property* node) {
2832 Comment cmnt(masm_, "[ Property");
2833 Reference property(this, node);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002834 property.GetValue();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002835}
2836
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002837
2838void CodeGenerator::VisitCall(Call* node) {
2839 Comment cmnt(masm_, "[ Call");
2840
2841 ZoneList<Expression*>* args = node->arguments();
2842
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002843 // Check if the function is a variable or a property.
2844 Expression* function = node->expression();
2845 Variable* var = function->AsVariableProxy()->AsVariable();
2846 Property* property = function->AsProperty();
2847
2848 // ------------------------------------------------------------------------
2849 // Fast-case: Use inline caching.
2850 // ---
2851 // According to ECMA-262, section 11.2.3, page 44, the function to call
2852 // must be resolved after the arguments have been evaluated. The IC code
2853 // automatically handles this by loading the arguments before the function
2854 // is resolved in cache misses (this also holds for megamorphic calls).
2855 // ------------------------------------------------------------------------
2856
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00002857 if (var != NULL && var->is_possibly_eval()) {
2858 // ----------------------------------
2859 // JavaScript example: 'eval(arg)' // eval is not known to be shadowed
2860 // ----------------------------------
2861
2862 // In a call to eval, we first call %ResolvePossiblyDirectEval to
2863 // resolve the function we need to call and the receiver of the
2864 // call. Then we call the resolved function using the given
2865 // arguments.
2866
2867 // Prepare the stack for the call to the resolved function.
2868 Load(function);
2869
2870 // Allocate a frame slot for the receiver.
2871 frame_->Push(Factory::undefined_value());
2872 int arg_count = args->length();
2873 for (int i = 0; i < arg_count; i++) {
2874 Load(args->at(i));
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00002875 frame_->SpillTop();
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00002876 }
2877
2878 // Prepare the stack for the call to ResolvePossiblyDirectEval.
2879 frame_->PushElementAt(arg_count + 1);
2880 if (arg_count > 0) {
2881 frame_->PushElementAt(arg_count);
2882 } else {
2883 frame_->Push(Factory::undefined_value());
2884 }
2885
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002886 // Push the receiver.
2887 frame_->PushParameterAt(-1);
2888
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00002889 // Resolve the call.
2890 Result result =
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002891 frame_->CallRuntime(Runtime::kResolvePossiblyDirectEval, 3);
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00002892
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002893 // The runtime call returns a pair of values in rax (function) and
2894 // rdx (receiver). Touch up the stack with the right values.
2895 Result receiver = allocator_->Allocate(rdx);
2896 frame_->SetElementAt(arg_count + 1, &result);
2897 frame_->SetElementAt(arg_count, &receiver);
2898 receiver.Unuse();
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00002899
2900 // Call the function.
2901 CodeForSourcePosition(node->position());
2902 InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002903 CallFunctionStub call_function(arg_count, in_loop, RECEIVER_MIGHT_BE_VALUE);
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00002904 result = frame_->CallStub(&call_function, arg_count + 1);
2905
2906 // Restore the context and overwrite the function on the stack with
2907 // the result.
2908 frame_->RestoreContextRegister();
2909 frame_->SetElementAt(0, &result);
2910
2911 } else if (var != NULL && !var->is_this() && var->is_global()) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002912 // ----------------------------------
2913 // JavaScript example: 'foo(1, 2, 3)' // foo is global
2914 // ----------------------------------
2915
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002916 // Pass the global object as the receiver and let the IC stub
2917 // patch the stack to use the global proxy as 'this' in the
2918 // invoked function.
2919 LoadGlobal();
2920
2921 // Load the arguments.
2922 int arg_count = args->length();
2923 for (int i = 0; i < arg_count; i++) {
2924 Load(args->at(i));
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00002925 frame_->SpillTop();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002926 }
2927
ager@chromium.org5c838252010-02-19 08:53:10 +00002928 // Push the name of the function on the frame.
2929 frame_->Push(var->name());
2930
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002931 // Call the IC initialization code.
2932 CodeForSourcePosition(node->position());
2933 Result result = frame_->CallCallIC(RelocInfo::CODE_TARGET_CONTEXT,
2934 arg_count,
2935 loop_nesting());
2936 frame_->RestoreContextRegister();
2937 // Replace the function on the stack with the result.
ager@chromium.org5c838252010-02-19 08:53:10 +00002938 frame_->Push(&result);
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00002939
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002940 } else if (var != NULL && var->slot() != NULL &&
2941 var->slot()->type() == Slot::LOOKUP) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002942 // ----------------------------------
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00002943 // JavaScript examples:
2944 //
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00002945 // with (obj) foo(1, 2, 3) // foo may be in obj.
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00002946 //
2947 // function f() {};
2948 // function g() {
2949 // eval(...);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00002950 // f(); // f could be in extension object.
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00002951 // }
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002952 // ----------------------------------
2953
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00002954 JumpTarget slow, done;
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00002955 Result function;
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00002956
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00002957 // Generate fast case for loading functions from slots that
2958 // correspond to local/global variables or arguments unless they
2959 // are shadowed by eval-introduced bindings.
2960 EmitDynamicLoadFromSlotFastCase(var->slot(),
2961 NOT_INSIDE_TYPEOF,
2962 &function,
2963 &slow,
2964 &done);
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00002965
2966 slow.Bind();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002967 // Load the function from the context. Sync the frame so we can
2968 // push the arguments directly into place.
2969 frame_->SyncRange(0, frame_->element_count() - 1);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00002970 frame_->EmitPush(rsi);
2971 frame_->EmitPush(var->name());
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002972 frame_->CallRuntime(Runtime::kLoadContextSlot, 2);
ager@chromium.org3e875802009-06-29 08:26:34 +00002973 // The runtime call returns a pair of values in rax and rdx. The
2974 // looked-up function is in rax and the receiver is in rdx. These
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002975 // register references are not ref counted here. We spill them
2976 // eagerly since they are arguments to an inevitable call (and are
2977 // not sharable by the arguments).
ager@chromium.org3e875802009-06-29 08:26:34 +00002978 ASSERT(!allocator()->is_used(rax));
2979 frame_->EmitPush(rax);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002980
2981 // Load the receiver.
ager@chromium.org3e875802009-06-29 08:26:34 +00002982 ASSERT(!allocator()->is_used(rdx));
2983 frame_->EmitPush(rdx);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002984
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00002985 // If fast case code has been generated, emit code to push the
2986 // function and receiver and have the slow path jump around this
2987 // code.
2988 if (done.is_linked()) {
2989 JumpTarget call;
2990 call.Jump();
2991 done.Bind(&function);
2992 frame_->Push(&function);
2993 LoadGlobalReceiver();
2994 call.Bind();
2995 }
2996
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002997 // Call the function.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002998 CallWithArguments(args, NO_CALL_FUNCTION_FLAGS, node->position());
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00002999
ager@chromium.org5aa501c2009-06-23 07:57:28 +00003000 } else if (property != NULL) {
3001 // Check if the key is a literal string.
3002 Literal* literal = property->key()->AsLiteral();
3003
3004 if (literal != NULL && literal->handle()->IsSymbol()) {
3005 // ------------------------------------------------------------------
3006 // JavaScript example: 'object.foo(1, 2, 3)' or 'map["key"](1, 2, 3)'
3007 // ------------------------------------------------------------------
3008
ager@chromium.org18ad94b2009-09-02 08:22:29 +00003009 Handle<String> name = Handle<String>::cast(literal->handle());
ager@chromium.org3e875802009-06-29 08:26:34 +00003010
ager@chromium.org18ad94b2009-09-02 08:22:29 +00003011 if (ArgumentsMode() == LAZY_ARGUMENTS_ALLOCATION &&
3012 name->IsEqualTo(CStrVector("apply")) &&
3013 args->length() == 2 &&
3014 args->at(1)->AsVariableProxy() != NULL &&
3015 args->at(1)->AsVariableProxy()->IsArguments()) {
3016 // Use the optimized Function.prototype.apply that avoids
3017 // allocating lazily allocated arguments objects.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003018 CallApplyLazy(property->obj(),
ager@chromium.org18ad94b2009-09-02 08:22:29 +00003019 args->at(0),
3020 args->at(1)->AsVariableProxy(),
3021 node->position());
ager@chromium.org5aa501c2009-06-23 07:57:28 +00003022
ager@chromium.org18ad94b2009-09-02 08:22:29 +00003023 } else {
ager@chromium.org5c838252010-02-19 08:53:10 +00003024 // Push the receiver onto the frame.
ager@chromium.org18ad94b2009-09-02 08:22:29 +00003025 Load(property->obj());
3026
3027 // Load the arguments.
3028 int arg_count = args->length();
3029 for (int i = 0; i < arg_count; i++) {
3030 Load(args->at(i));
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00003031 frame_->SpillTop();
ager@chromium.org18ad94b2009-09-02 08:22:29 +00003032 }
3033
ager@chromium.org5c838252010-02-19 08:53:10 +00003034 // Push the name of the function onto the frame.
3035 frame_->Push(name);
3036
ager@chromium.org18ad94b2009-09-02 08:22:29 +00003037 // Call the IC initialization code.
3038 CodeForSourcePosition(node->position());
3039 Result result = frame_->CallCallIC(RelocInfo::CODE_TARGET,
3040 arg_count,
3041 loop_nesting());
3042 frame_->RestoreContextRegister();
ager@chromium.org5c838252010-02-19 08:53:10 +00003043 frame_->Push(&result);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00003044 }
3045
ager@chromium.org5aa501c2009-06-23 07:57:28 +00003046 } else {
3047 // -------------------------------------------
3048 // JavaScript example: 'array[index](1, 2, 3)'
3049 // -------------------------------------------
3050
3051 // Load the function to call from the property through a reference.
ager@chromium.org5aa501c2009-06-23 07:57:28 +00003052 if (property->is_synthetic()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003053 Reference ref(this, property, false);
3054 ref.GetValue();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00003055 // Use global object as receiver.
3056 LoadGlobalReceiver();
3057 } else {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003058 Reference ref(this, property, false);
3059 ASSERT(ref.size() == 2);
3060 Result key = frame_->Pop();
3061 frame_->Dup(); // Duplicate the receiver.
3062 frame_->Push(&key);
3063 ref.GetValue();
3064 // Top of frame contains function to call, with duplicate copy of
3065 // receiver below it. Swap them.
3066 Result function = frame_->Pop();
3067 Result receiver = frame_->Pop();
3068 frame_->Push(&function);
3069 frame_->Push(&receiver);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00003070 }
3071
3072 // Call the function.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003073 CallWithArguments(args, RECEIVER_MIGHT_BE_VALUE, node->position());
ager@chromium.org5aa501c2009-06-23 07:57:28 +00003074 }
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003075
ager@chromium.org5aa501c2009-06-23 07:57:28 +00003076 } else {
3077 // ----------------------------------
3078 // JavaScript example: 'foo(1, 2, 3)' // foo is not global
3079 // ----------------------------------
3080
3081 // Load the function.
3082 Load(function);
3083
3084 // Pass the global proxy as the receiver.
3085 LoadGlobalReceiver();
3086
3087 // Call the function.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003088 CallWithArguments(args, NO_CALL_FUNCTION_FLAGS, node->position());
ager@chromium.org5aa501c2009-06-23 07:57:28 +00003089 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00003090}
3091
ager@chromium.org5aa501c2009-06-23 07:57:28 +00003092
ager@chromium.org3e875802009-06-29 08:26:34 +00003093void CodeGenerator::VisitCallNew(CallNew* node) {
3094 Comment cmnt(masm_, "[ CallNew");
ager@chromium.org3e875802009-06-29 08:26:34 +00003095
3096 // According to ECMA-262, section 11.2.2, page 44, the function
3097 // expression in new calls must be evaluated before the
3098 // arguments. This is different from ordinary calls, where the
3099 // actual function to call is resolved after the arguments have been
3100 // evaluated.
3101
3102 // Compute function to call and use the global object as the
3103 // receiver. There is no need to use the global proxy here because
3104 // it will always be replaced with a newly allocated object.
3105 Load(node->expression());
3106 LoadGlobal();
3107
3108 // Push the arguments ("left-to-right") on the stack.
3109 ZoneList<Expression*>* args = node->arguments();
3110 int arg_count = args->length();
3111 for (int i = 0; i < arg_count; i++) {
3112 Load(args->at(i));
3113 }
3114
3115 // Call the construct call builtin that handles allocation and
3116 // constructor invocation.
3117 CodeForSourcePosition(node->position());
3118 Result result = frame_->CallConstructor(arg_count);
3119 // Replace the function on the stack with the result.
3120 frame_->SetElementAt(0, &result);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00003121}
3122
ager@chromium.org5aa501c2009-06-23 07:57:28 +00003123
ager@chromium.org3e875802009-06-29 08:26:34 +00003124void CodeGenerator::VisitCallRuntime(CallRuntime* node) {
3125 if (CheckForInlineRuntimeCall(node)) {
3126 return;
3127 }
3128
3129 ZoneList<Expression*>* args = node->arguments();
3130 Comment cmnt(masm_, "[ CallRuntime");
3131 Runtime::Function* function = node->function();
3132
3133 if (function == NULL) {
ager@chromium.org3e875802009-06-29 08:26:34 +00003134 // Push the builtins object found in the current global object.
3135 Result temp = allocator()->Allocate();
3136 ASSERT(temp.is_valid());
3137 __ movq(temp.reg(), GlobalObject());
3138 __ movq(temp.reg(),
3139 FieldOperand(temp.reg(), GlobalObject::kBuiltinsOffset));
3140 frame_->Push(&temp);
3141 }
3142
3143 // Push the arguments ("left-to-right").
3144 int arg_count = args->length();
3145 for (int i = 0; i < arg_count; i++) {
3146 Load(args->at(i));
3147 }
3148
3149 if (function == NULL) {
3150 // Call the JS runtime function.
ager@chromium.org5c838252010-02-19 08:53:10 +00003151 frame_->Push(node->name());
ager@chromium.org3e875802009-06-29 08:26:34 +00003152 Result answer = frame_->CallCallIC(RelocInfo::CODE_TARGET,
3153 arg_count,
3154 loop_nesting_);
3155 frame_->RestoreContextRegister();
ager@chromium.org5c838252010-02-19 08:53:10 +00003156 frame_->Push(&answer);
ager@chromium.org3e875802009-06-29 08:26:34 +00003157 } else {
3158 // Call the C runtime function.
3159 Result answer = frame_->CallRuntime(function, arg_count);
3160 frame_->Push(&answer);
3161 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00003162}
3163
ager@chromium.org5aa501c2009-06-23 07:57:28 +00003164
ager@chromium.org3e875802009-06-29 08:26:34 +00003165void CodeGenerator::VisitUnaryOperation(UnaryOperation* node) {
ager@chromium.org3e875802009-06-29 08:26:34 +00003166 Comment cmnt(masm_, "[ UnaryOperation");
3167
3168 Token::Value op = node->op();
3169
3170 if (op == Token::NOT) {
3171 // Swap the true and false targets but keep the same actual label
3172 // as the fall through.
3173 destination()->Invert();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003174 LoadCondition(node->expression(), destination(), true);
ager@chromium.org3e875802009-06-29 08:26:34 +00003175 // Swap the labels back.
3176 destination()->Invert();
3177
3178 } else if (op == Token::DELETE) {
3179 Property* property = node->expression()->AsProperty();
3180 if (property != NULL) {
3181 Load(property->obj());
3182 Load(property->key());
3183 Result answer = frame_->InvokeBuiltin(Builtins::DELETE, CALL_FUNCTION, 2);
3184 frame_->Push(&answer);
3185 return;
3186 }
3187
3188 Variable* variable = node->expression()->AsVariableProxy()->AsVariable();
3189 if (variable != NULL) {
3190 Slot* slot = variable->slot();
3191 if (variable->is_global()) {
3192 LoadGlobal();
3193 frame_->Push(variable->name());
3194 Result answer = frame_->InvokeBuiltin(Builtins::DELETE,
3195 CALL_FUNCTION, 2);
3196 frame_->Push(&answer);
3197 return;
3198
3199 } else if (slot != NULL && slot->type() == Slot::LOOKUP) {
3200 // Call the runtime to look up the context holding the named
3201 // variable. Sync the virtual frame eagerly so we can push the
3202 // arguments directly into place.
3203 frame_->SyncRange(0, frame_->element_count() - 1);
3204 frame_->EmitPush(rsi);
3205 frame_->EmitPush(variable->name());
3206 Result context = frame_->CallRuntime(Runtime::kLookupContext, 2);
3207 ASSERT(context.is_register());
3208 frame_->EmitPush(context.reg());
3209 context.Unuse();
3210 frame_->EmitPush(variable->name());
3211 Result answer = frame_->InvokeBuiltin(Builtins::DELETE,
3212 CALL_FUNCTION, 2);
3213 frame_->Push(&answer);
3214 return;
3215 }
3216
3217 // Default: Result of deleting non-global, not dynamically
3218 // introduced variables is false.
3219 frame_->Push(Factory::false_value());
3220
3221 } else {
3222 // Default: Result of deleting expressions is true.
3223 Load(node->expression()); // may have side-effects
3224 frame_->SetElementAt(0, Factory::true_value());
3225 }
3226
3227 } else if (op == Token::TYPEOF) {
3228 // Special case for loading the typeof expression; see comment on
3229 // LoadTypeofExpression().
3230 LoadTypeofExpression(node->expression());
3231 Result answer = frame_->CallRuntime(Runtime::kTypeof, 1);
3232 frame_->Push(&answer);
3233
3234 } else if (op == Token::VOID) {
3235 Expression* expression = node->expression();
3236 if (expression && expression->AsLiteral() && (
3237 expression->AsLiteral()->IsTrue() ||
3238 expression->AsLiteral()->IsFalse() ||
3239 expression->AsLiteral()->handle()->IsNumber() ||
3240 expression->AsLiteral()->handle()->IsString() ||
3241 expression->AsLiteral()->handle()->IsJSRegExp() ||
3242 expression->AsLiteral()->IsNull())) {
3243 // Omit evaluating the value of the primitive literal.
3244 // It will be discarded anyway, and can have no side effect.
3245 frame_->Push(Factory::undefined_value());
3246 } else {
3247 Load(node->expression());
3248 frame_->SetElementAt(0, Factory::undefined_value());
3249 }
3250
3251 } else {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003252 bool overwrite =
3253 (node->expression()->AsBinaryOperation() != NULL &&
3254 node->expression()->AsBinaryOperation()->ResultOverwriteAllowed());
ager@chromium.org3e875802009-06-29 08:26:34 +00003255 Load(node->expression());
3256 switch (op) {
3257 case Token::NOT:
3258 case Token::DELETE:
3259 case Token::TYPEOF:
3260 UNREACHABLE(); // handled above
3261 break;
3262
3263 case Token::SUB: {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003264 GenericUnaryOpStub stub(Token::SUB, overwrite);
ager@chromium.org3e875802009-06-29 08:26:34 +00003265 Result operand = frame_->Pop();
3266 Result answer = frame_->CallStub(&stub, &operand);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00003267 answer.set_type_info(TypeInfo::Number());
ager@chromium.org3e875802009-06-29 08:26:34 +00003268 frame_->Push(&answer);
3269 break;
3270 }
3271
3272 case Token::BIT_NOT: {
3273 // Smi check.
3274 JumpTarget smi_label;
3275 JumpTarget continue_label;
3276 Result operand = frame_->Pop();
3277 operand.ToRegister();
ager@chromium.org4af710e2009-09-15 12:20:11 +00003278
3279 Condition is_smi = masm_->CheckSmi(operand.reg());
3280 smi_label.Branch(is_smi, &operand);
ager@chromium.org3e875802009-06-29 08:26:34 +00003281
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003282 GenericUnaryOpStub stub(Token::BIT_NOT, overwrite);
3283 Result answer = frame_->CallStub(&stub, &operand);
ager@chromium.org3e875802009-06-29 08:26:34 +00003284 continue_label.Jump(&answer);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003285
ager@chromium.org3e875802009-06-29 08:26:34 +00003286 smi_label.Bind(&answer);
3287 answer.ToRegister();
3288 frame_->Spill(answer.reg());
ager@chromium.org4af710e2009-09-15 12:20:11 +00003289 __ SmiNot(answer.reg(), answer.reg());
ager@chromium.org3e875802009-06-29 08:26:34 +00003290 continue_label.Bind(&answer);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00003291 answer.set_type_info(TypeInfo::Smi());
ager@chromium.org3e875802009-06-29 08:26:34 +00003292 frame_->Push(&answer);
3293 break;
3294 }
3295
3296 case Token::ADD: {
3297 // Smi check.
3298 JumpTarget continue_label;
3299 Result operand = frame_->Pop();
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00003300 TypeInfo operand_info = operand.type_info();
ager@chromium.org3e875802009-06-29 08:26:34 +00003301 operand.ToRegister();
ager@chromium.org4af710e2009-09-15 12:20:11 +00003302 Condition is_smi = masm_->CheckSmi(operand.reg());
3303 continue_label.Branch(is_smi, &operand);
ager@chromium.org3e875802009-06-29 08:26:34 +00003304 frame_->Push(&operand);
3305 Result answer = frame_->InvokeBuiltin(Builtins::TO_NUMBER,
3306 CALL_FUNCTION, 1);
3307
3308 continue_label.Bind(&answer);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00003309 if (operand_info.IsSmi()) {
3310 answer.set_type_info(TypeInfo::Smi());
3311 } else if (operand_info.IsInteger32()) {
3312 answer.set_type_info(TypeInfo::Integer32());
3313 } else {
3314 answer.set_type_info(TypeInfo::Number());
3315 }
ager@chromium.org3e875802009-06-29 08:26:34 +00003316 frame_->Push(&answer);
3317 break;
3318 }
ager@chromium.org3e875802009-06-29 08:26:34 +00003319 default:
3320 UNREACHABLE();
3321 }
3322 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00003323}
3324
ager@chromium.org3e875802009-06-29 08:26:34 +00003325
lrn@chromium.org25156de2010-04-06 13:10:27 +00003326// The value in dst was optimistically incremented or decremented.
3327// The result overflowed or was not smi tagged. Call into the runtime
3328// to convert the argument to a number, and call the specialized add
3329// or subtract stub. The result is left in dst.
ager@chromium.org3e875802009-06-29 08:26:34 +00003330class DeferredPrefixCountOperation: public DeferredCode {
3331 public:
lrn@chromium.org25156de2010-04-06 13:10:27 +00003332 DeferredPrefixCountOperation(Register dst,
3333 bool is_increment,
3334 TypeInfo input_type)
3335 : dst_(dst), is_increment_(is_increment), input_type_(input_type) {
ager@chromium.org3e875802009-06-29 08:26:34 +00003336 set_comment("[ DeferredCountOperation");
3337 }
3338
3339 virtual void Generate();
3340
3341 private:
3342 Register dst_;
3343 bool is_increment_;
lrn@chromium.org25156de2010-04-06 13:10:27 +00003344 TypeInfo input_type_;
ager@chromium.org3e875802009-06-29 08:26:34 +00003345};
3346
3347
3348void DeferredPrefixCountOperation::Generate() {
lrn@chromium.org25156de2010-04-06 13:10:27 +00003349 Register left;
3350 if (input_type_.IsNumber()) {
3351 left = dst_;
ager@chromium.org3e875802009-06-29 08:26:34 +00003352 } else {
lrn@chromium.org25156de2010-04-06 13:10:27 +00003353 __ push(dst_);
3354 __ InvokeBuiltin(Builtins::TO_NUMBER, CALL_FUNCTION);
3355 left = rax;
ager@chromium.org3e875802009-06-29 08:26:34 +00003356 }
lrn@chromium.org25156de2010-04-06 13:10:27 +00003357
3358 GenericBinaryOpStub stub(is_increment_ ? Token::ADD : Token::SUB,
3359 NO_OVERWRITE,
3360 NO_GENERIC_BINARY_FLAGS,
3361 TypeInfo::Number());
3362 stub.GenerateCall(masm_, left, Smi::FromInt(1));
3363
ager@chromium.org3e875802009-06-29 08:26:34 +00003364 if (!dst_.is(rax)) __ movq(dst_, rax);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00003365}
3366
ager@chromium.org3e875802009-06-29 08:26:34 +00003367
lrn@chromium.org25156de2010-04-06 13:10:27 +00003368// The value in dst was optimistically incremented or decremented.
3369// The result overflowed or was not smi tagged. Call into the runtime
3370// to convert the argument to a number. Update the original value in
3371// old. Call the specialized add or subtract stub. The result is
3372// left in dst.
ager@chromium.org3e875802009-06-29 08:26:34 +00003373class DeferredPostfixCountOperation: public DeferredCode {
3374 public:
lrn@chromium.org25156de2010-04-06 13:10:27 +00003375 DeferredPostfixCountOperation(Register dst,
3376 Register old,
3377 bool is_increment,
3378 TypeInfo input_type)
3379 : dst_(dst),
3380 old_(old),
3381 is_increment_(is_increment),
3382 input_type_(input_type) {
ager@chromium.org3e875802009-06-29 08:26:34 +00003383 set_comment("[ DeferredCountOperation");
3384 }
3385
3386 virtual void Generate();
3387
3388 private:
3389 Register dst_;
3390 Register old_;
3391 bool is_increment_;
lrn@chromium.org25156de2010-04-06 13:10:27 +00003392 TypeInfo input_type_;
ager@chromium.org3e875802009-06-29 08:26:34 +00003393};
3394
3395
3396void DeferredPostfixCountOperation::Generate() {
lrn@chromium.org25156de2010-04-06 13:10:27 +00003397 Register left;
3398 if (input_type_.IsNumber()) {
3399 __ push(dst_); // Save the input to use as the old value.
3400 left = dst_;
ager@chromium.org3e875802009-06-29 08:26:34 +00003401 } else {
lrn@chromium.org25156de2010-04-06 13:10:27 +00003402 __ push(dst_);
3403 __ InvokeBuiltin(Builtins::TO_NUMBER, CALL_FUNCTION);
3404 __ push(rax); // Save the result of ToNumber to use as the old value.
3405 left = rax;
ager@chromium.org3e875802009-06-29 08:26:34 +00003406 }
lrn@chromium.org25156de2010-04-06 13:10:27 +00003407
3408 GenericBinaryOpStub stub(is_increment_ ? Token::ADD : Token::SUB,
3409 NO_OVERWRITE,
3410 NO_GENERIC_BINARY_FLAGS,
3411 TypeInfo::Number());
3412 stub.GenerateCall(masm_, left, Smi::FromInt(1));
3413
ager@chromium.org3e875802009-06-29 08:26:34 +00003414 if (!dst_.is(rax)) __ movq(dst_, rax);
3415 __ pop(old_);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00003416}
3417
ager@chromium.org3e875802009-06-29 08:26:34 +00003418
3419void CodeGenerator::VisitCountOperation(CountOperation* node) {
3420 Comment cmnt(masm_, "[ CountOperation");
3421
3422 bool is_postfix = node->is_postfix();
3423 bool is_increment = node->op() == Token::INC;
3424
3425 Variable* var = node->expression()->AsVariableProxy()->AsVariable();
3426 bool is_const = (var != NULL && var->mode() == Variable::CONST);
3427
3428 // Postfix operations need a stack slot under the reference to hold
3429 // the old value while the new value is being stored. This is so that
3430 // in the case that storing the new value requires a call, the old
3431 // value will be in the frame to be spilled.
3432 if (is_postfix) frame_->Push(Smi::FromInt(0));
3433
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003434 // A constant reference is not saved to, so the reference is not a
3435 // compound assignment reference.
3436 { Reference target(this, node->expression(), !is_const);
ager@chromium.org3e875802009-06-29 08:26:34 +00003437 if (target.is_illegal()) {
3438 // Spoof the virtual frame to have the expected height (one higher
3439 // than on entry).
3440 if (!is_postfix) frame_->Push(Smi::FromInt(0));
3441 return;
3442 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003443 target.TakeValue();
ager@chromium.org3e875802009-06-29 08:26:34 +00003444
3445 Result new_value = frame_->Pop();
3446 new_value.ToRegister();
3447
3448 Result old_value; // Only allocated in the postfix case.
3449 if (is_postfix) {
3450 // Allocate a temporary to preserve the old value.
3451 old_value = allocator_->Allocate();
3452 ASSERT(old_value.is_valid());
3453 __ movq(old_value.reg(), new_value.reg());
lrn@chromium.org25156de2010-04-06 13:10:27 +00003454
3455 // The return value for postfix operations is ToNumber(input).
3456 // Keep more precise type info if the input is some kind of
3457 // number already. If the input is not a number we have to wait
3458 // for the deferred code to convert it.
3459 if (new_value.type_info().IsNumber()) {
3460 old_value.set_type_info(new_value.type_info());
3461 }
ager@chromium.org3e875802009-06-29 08:26:34 +00003462 }
3463 // Ensure the new value is writable.
3464 frame_->Spill(new_value.reg());
3465
ager@chromium.org3e875802009-06-29 08:26:34 +00003466 DeferredCode* deferred = NULL;
3467 if (is_postfix) {
3468 deferred = new DeferredPostfixCountOperation(new_value.reg(),
3469 old_value.reg(),
lrn@chromium.org25156de2010-04-06 13:10:27 +00003470 is_increment,
3471 new_value.type_info());
ager@chromium.org3e875802009-06-29 08:26:34 +00003472 } else {
3473 deferred = new DeferredPrefixCountOperation(new_value.reg(),
lrn@chromium.org25156de2010-04-06 13:10:27 +00003474 is_increment,
3475 new_value.type_info());
ager@chromium.org3e875802009-06-29 08:26:34 +00003476 }
3477
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00003478 if (new_value.is_smi()) {
3479 if (FLAG_debug_code) { __ AbortIfNotSmi(new_value.reg()); }
3480 } else {
3481 __ JumpIfNotSmi(new_value.reg(), deferred->entry_label());
3482 }
ager@chromium.org3e875802009-06-29 08:26:34 +00003483 if (is_increment) {
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00003484 __ SmiAddConstant(kScratchRegister,
3485 new_value.reg(),
3486 Smi::FromInt(1),
3487 deferred->entry_label());
ager@chromium.org3e875802009-06-29 08:26:34 +00003488 } else {
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00003489 __ SmiSubConstant(kScratchRegister,
3490 new_value.reg(),
3491 Smi::FromInt(1),
3492 deferred->entry_label());
ager@chromium.org3e875802009-06-29 08:26:34 +00003493 }
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00003494 __ movq(new_value.reg(), kScratchRegister);
ager@chromium.org3e875802009-06-29 08:26:34 +00003495 deferred->BindExit();
3496
lrn@chromium.org25156de2010-04-06 13:10:27 +00003497 // Postfix count operations return their input converted to
3498 // number. The case when the input is already a number is covered
3499 // above in the allocation code for old_value.
3500 if (is_postfix && !new_value.type_info().IsNumber()) {
3501 old_value.set_type_info(TypeInfo::Number());
3502 }
3503
3504 new_value.set_type_info(TypeInfo::Number());
3505
ager@chromium.org3e875802009-06-29 08:26:34 +00003506 // Postfix: store the old value in the allocated slot under the
3507 // reference.
3508 if (is_postfix) frame_->SetElementAt(target.size(), &old_value);
3509
3510 frame_->Push(&new_value);
3511 // Non-constant: update the reference.
3512 if (!is_const) target.SetValue(NOT_CONST_INIT);
3513 }
3514
3515 // Postfix: drop the new value and use the old.
3516 if (is_postfix) frame_->Drop();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00003517}
3518
ager@chromium.org3e875802009-06-29 08:26:34 +00003519
ager@chromium.orgb26c50a2010-03-26 09:27:16 +00003520void CodeGenerator::GenerateLogicalBooleanOperation(BinaryOperation* node) {
ager@chromium.org3e875802009-06-29 08:26:34 +00003521 // According to ECMA-262 section 11.11, page 58, the binary logical
3522 // operators must yield the result of one of the two expressions
3523 // before any ToBoolean() conversions. This means that the value
3524 // produced by a && or || operator is not necessarily a boolean.
3525
3526 // NOTE: If the left hand side produces a materialized value (not
3527 // control flow), we force the right hand side to do the same. This
3528 // is necessary because we assume that if we get control flow on the
3529 // last path out of an expression we got it on all paths.
ager@chromium.orgb26c50a2010-03-26 09:27:16 +00003530 if (node->op() == Token::AND) {
ager@chromium.org3e875802009-06-29 08:26:34 +00003531 JumpTarget is_true;
3532 ControlDestination dest(&is_true, destination()->false_target(), true);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003533 LoadCondition(node->left(), &dest, false);
ager@chromium.org3e875802009-06-29 08:26:34 +00003534
3535 if (dest.false_was_fall_through()) {
3536 // The current false target was used as the fall-through. If
3537 // there are no dangling jumps to is_true then the left
3538 // subexpression was unconditionally false. Otherwise we have
3539 // paths where we do have to evaluate the right subexpression.
3540 if (is_true.is_linked()) {
3541 // We need to compile the right subexpression. If the jump to
3542 // the current false target was a forward jump then we have a
3543 // valid frame, we have just bound the false target, and we
3544 // have to jump around the code for the right subexpression.
3545 if (has_valid_frame()) {
3546 destination()->false_target()->Unuse();
3547 destination()->false_target()->Jump();
3548 }
3549 is_true.Bind();
3550 // The left subexpression compiled to control flow, so the
3551 // right one is free to do so as well.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003552 LoadCondition(node->right(), destination(), false);
ager@chromium.org3e875802009-06-29 08:26:34 +00003553 } else {
3554 // We have actually just jumped to or bound the current false
3555 // target but the current control destination is not marked as
3556 // used.
3557 destination()->Use(false);
3558 }
3559
3560 } else if (dest.is_used()) {
3561 // The left subexpression compiled to control flow (and is_true
3562 // was just bound), so the right is free to do so as well.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003563 LoadCondition(node->right(), destination(), false);
ager@chromium.org3e875802009-06-29 08:26:34 +00003564
3565 } else {
3566 // We have a materialized value on the frame, so we exit with
3567 // one on all paths. There are possibly also jumps to is_true
3568 // from nested subexpressions.
3569 JumpTarget pop_and_continue;
3570 JumpTarget exit;
3571
3572 // Avoid popping the result if it converts to 'false' using the
3573 // standard ToBoolean() conversion as described in ECMA-262,
3574 // section 9.2, page 30.
3575 //
3576 // Duplicate the TOS value. The duplicate will be popped by
3577 // ToBoolean.
3578 frame_->Dup();
3579 ControlDestination dest(&pop_and_continue, &exit, true);
3580 ToBoolean(&dest);
3581
3582 // Pop the result of evaluating the first part.
3583 frame_->Drop();
3584
3585 // Compile right side expression.
3586 is_true.Bind();
3587 Load(node->right());
3588
3589 // Exit (always with a materialized value).
3590 exit.Bind();
3591 }
3592
ager@chromium.orgb26c50a2010-03-26 09:27:16 +00003593 } else {
3594 ASSERT(node->op() == Token::OR);
ager@chromium.org3e875802009-06-29 08:26:34 +00003595 JumpTarget is_false;
3596 ControlDestination dest(destination()->true_target(), &is_false, false);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003597 LoadCondition(node->left(), &dest, false);
ager@chromium.org3e875802009-06-29 08:26:34 +00003598
3599 if (dest.true_was_fall_through()) {
3600 // The current true target was used as the fall-through. If
3601 // there are no dangling jumps to is_false then the left
3602 // subexpression was unconditionally true. Otherwise we have
3603 // paths where we do have to evaluate the right subexpression.
3604 if (is_false.is_linked()) {
3605 // We need to compile the right subexpression. If the jump to
3606 // the current true target was a forward jump then we have a
3607 // valid frame, we have just bound the true target, and we
3608 // have to jump around the code for the right subexpression.
3609 if (has_valid_frame()) {
3610 destination()->true_target()->Unuse();
3611 destination()->true_target()->Jump();
3612 }
3613 is_false.Bind();
3614 // The left subexpression compiled to control flow, so the
3615 // right one is free to do so as well.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003616 LoadCondition(node->right(), destination(), false);
ager@chromium.org3e875802009-06-29 08:26:34 +00003617 } else {
3618 // We have just jumped to or bound the current true target but
3619 // the current control destination is not marked as used.
3620 destination()->Use(true);
3621 }
3622
3623 } else if (dest.is_used()) {
3624 // The left subexpression compiled to control flow (and is_false
3625 // was just bound), so the right is free to do so as well.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003626 LoadCondition(node->right(), destination(), false);
ager@chromium.org3e875802009-06-29 08:26:34 +00003627
3628 } else {
3629 // We have a materialized value on the frame, so we exit with
3630 // one on all paths. There are possibly also jumps to is_false
3631 // from nested subexpressions.
3632 JumpTarget pop_and_continue;
3633 JumpTarget exit;
3634
3635 // Avoid popping the result if it converts to 'true' using the
3636 // standard ToBoolean() conversion as described in ECMA-262,
3637 // section 9.2, page 30.
3638 //
3639 // Duplicate the TOS value. The duplicate will be popped by
3640 // ToBoolean.
3641 frame_->Dup();
3642 ControlDestination dest(&exit, &pop_and_continue, false);
3643 ToBoolean(&dest);
3644
3645 // Pop the result of evaluating the first part.
3646 frame_->Drop();
3647
3648 // Compile right side expression.
3649 is_false.Bind();
3650 Load(node->right());
3651
3652 // Exit (always with a materialized value).
3653 exit.Bind();
3654 }
ager@chromium.orgb26c50a2010-03-26 09:27:16 +00003655 }
3656}
ager@chromium.org3e875802009-06-29 08:26:34 +00003657
ager@chromium.orgb26c50a2010-03-26 09:27:16 +00003658void CodeGenerator::VisitBinaryOperation(BinaryOperation* node) {
3659 Comment cmnt(masm_, "[ BinaryOperation");
3660
3661 if (node->op() == Token::AND || node->op() == Token::OR) {
3662 GenerateLogicalBooleanOperation(node);
ager@chromium.org3e875802009-06-29 08:26:34 +00003663 } else {
3664 // NOTE: The code below assumes that the slow cases (calls to runtime)
3665 // never return a constant/immutable object.
3666 OverwriteMode overwrite_mode = NO_OVERWRITE;
3667 if (node->left()->AsBinaryOperation() != NULL &&
3668 node->left()->AsBinaryOperation()->ResultOverwriteAllowed()) {
3669 overwrite_mode = OVERWRITE_LEFT;
3670 } else if (node->right()->AsBinaryOperation() != NULL &&
3671 node->right()->AsBinaryOperation()->ResultOverwriteAllowed()) {
3672 overwrite_mode = OVERWRITE_RIGHT;
3673 }
3674
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00003675 if (node->left()->IsTrivial()) {
3676 Load(node->right());
3677 Result right = frame_->Pop();
3678 frame_->Push(node->left());
3679 frame_->Push(&right);
3680 } else {
3681 Load(node->left());
3682 Load(node->right());
3683 }
ager@chromium.orgac091b72010-05-05 07:34:42 +00003684 GenericBinaryOperation(node, overwrite_mode);
ager@chromium.org3e875802009-06-29 08:26:34 +00003685 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00003686}
3687
ager@chromium.org3e875802009-06-29 08:26:34 +00003688
3689
3690void CodeGenerator::VisitCompareOperation(CompareOperation* node) {
3691 Comment cmnt(masm_, "[ CompareOperation");
3692
3693 // Get the expressions from the node.
3694 Expression* left = node->left();
3695 Expression* right = node->right();
3696 Token::Value op = node->op();
3697 // To make typeof testing for natives implemented in JavaScript really
3698 // efficient, we generate special code for expressions of the form:
3699 // 'typeof <expression> == <string>'.
3700 UnaryOperation* operation = left->AsUnaryOperation();
3701 if ((op == Token::EQ || op == Token::EQ_STRICT) &&
3702 (operation != NULL && operation->op() == Token::TYPEOF) &&
3703 (right->AsLiteral() != NULL &&
3704 right->AsLiteral()->handle()->IsString())) {
3705 Handle<String> check(Handle<String>::cast(right->AsLiteral()->handle()));
3706
3707 // Load the operand and move it to a register.
3708 LoadTypeofExpression(operation->expression());
3709 Result answer = frame_->Pop();
3710 answer.ToRegister();
3711
3712 if (check->Equals(Heap::number_symbol())) {
ager@chromium.org4af710e2009-09-15 12:20:11 +00003713 Condition is_smi = masm_->CheckSmi(answer.reg());
3714 destination()->true_target()->Branch(is_smi);
ager@chromium.org3e875802009-06-29 08:26:34 +00003715 frame_->Spill(answer.reg());
3716 __ movq(answer.reg(), FieldOperand(answer.reg(), HeapObject::kMapOffset));
ager@chromium.org18ad94b2009-09-02 08:22:29 +00003717 __ CompareRoot(answer.reg(), Heap::kHeapNumberMapRootIndex);
ager@chromium.org3e875802009-06-29 08:26:34 +00003718 answer.Unuse();
3719 destination()->Split(equal);
3720
3721 } else if (check->Equals(Heap::string_symbol())) {
ager@chromium.org4af710e2009-09-15 12:20:11 +00003722 Condition is_smi = masm_->CheckSmi(answer.reg());
3723 destination()->false_target()->Branch(is_smi);
ager@chromium.org3e875802009-06-29 08:26:34 +00003724
3725 // It can be an undetectable string object.
3726 __ movq(kScratchRegister,
3727 FieldOperand(answer.reg(), HeapObject::kMapOffset));
3728 __ testb(FieldOperand(kScratchRegister, Map::kBitFieldOffset),
3729 Immediate(1 << Map::kIsUndetectable));
3730 destination()->false_target()->Branch(not_zero);
3731 __ CmpInstanceType(kScratchRegister, FIRST_NONSTRING_TYPE);
3732 answer.Unuse();
3733 destination()->Split(below); // Unsigned byte comparison needed.
3734
3735 } else if (check->Equals(Heap::boolean_symbol())) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00003736 __ CompareRoot(answer.reg(), Heap::kTrueValueRootIndex);
ager@chromium.org3e875802009-06-29 08:26:34 +00003737 destination()->true_target()->Branch(equal);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00003738 __ CompareRoot(answer.reg(), Heap::kFalseValueRootIndex);
ager@chromium.org3e875802009-06-29 08:26:34 +00003739 answer.Unuse();
3740 destination()->Split(equal);
3741
3742 } else if (check->Equals(Heap::undefined_symbol())) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00003743 __ CompareRoot(answer.reg(), Heap::kUndefinedValueRootIndex);
ager@chromium.org3e875802009-06-29 08:26:34 +00003744 destination()->true_target()->Branch(equal);
3745
ager@chromium.org4af710e2009-09-15 12:20:11 +00003746 Condition is_smi = masm_->CheckSmi(answer.reg());
3747 destination()->false_target()->Branch(is_smi);
ager@chromium.org3e875802009-06-29 08:26:34 +00003748
3749 // It can be an undetectable object.
3750 __ movq(kScratchRegister,
3751 FieldOperand(answer.reg(), HeapObject::kMapOffset));
3752 __ testb(FieldOperand(kScratchRegister, Map::kBitFieldOffset),
3753 Immediate(1 << Map::kIsUndetectable));
3754 answer.Unuse();
3755 destination()->Split(not_zero);
3756
3757 } else if (check->Equals(Heap::function_symbol())) {
ager@chromium.org4af710e2009-09-15 12:20:11 +00003758 Condition is_smi = masm_->CheckSmi(answer.reg());
3759 destination()->false_target()->Branch(is_smi);
ager@chromium.org3e875802009-06-29 08:26:34 +00003760 frame_->Spill(answer.reg());
3761 __ CmpObjectType(answer.reg(), JS_FUNCTION_TYPE, answer.reg());
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003762 destination()->true_target()->Branch(equal);
3763 // Regular expressions are callable so typeof == 'function'.
3764 __ CmpInstanceType(answer.reg(), JS_REGEXP_TYPE);
ager@chromium.org3e875802009-06-29 08:26:34 +00003765 answer.Unuse();
3766 destination()->Split(equal);
3767
3768 } else if (check->Equals(Heap::object_symbol())) {
ager@chromium.org4af710e2009-09-15 12:20:11 +00003769 Condition is_smi = masm_->CheckSmi(answer.reg());
3770 destination()->false_target()->Branch(is_smi);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00003771 __ CompareRoot(answer.reg(), Heap::kNullValueRootIndex);
ager@chromium.org3e875802009-06-29 08:26:34 +00003772 destination()->true_target()->Branch(equal);
3773
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003774 // Regular expressions are typeof == 'function', not 'object'.
3775 __ CmpObjectType(answer.reg(), JS_REGEXP_TYPE, kScratchRegister);
3776 destination()->false_target()->Branch(equal);
3777
ager@chromium.org3e875802009-06-29 08:26:34 +00003778 // It can be an undetectable object.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00003779 __ testb(FieldOperand(kScratchRegister, Map::kBitFieldOffset),
3780 Immediate(1 << Map::kIsUndetectable));
ager@chromium.org3e875802009-06-29 08:26:34 +00003781 destination()->false_target()->Branch(not_zero);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003782 __ CmpInstanceType(kScratchRegister, FIRST_JS_OBJECT_TYPE);
ager@chromium.org3e875802009-06-29 08:26:34 +00003783 destination()->false_target()->Branch(below);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003784 __ CmpInstanceType(kScratchRegister, LAST_JS_OBJECT_TYPE);
ager@chromium.org3e875802009-06-29 08:26:34 +00003785 answer.Unuse();
3786 destination()->Split(below_equal);
3787 } else {
3788 // Uncommon case: typeof testing against a string literal that is
3789 // never returned from the typeof operator.
3790 answer.Unuse();
3791 destination()->Goto(false);
3792 }
3793 return;
3794 }
3795
3796 Condition cc = no_condition;
3797 bool strict = false;
3798 switch (op) {
3799 case Token::EQ_STRICT:
3800 strict = true;
3801 // Fall through
3802 case Token::EQ:
3803 cc = equal;
3804 break;
3805 case Token::LT:
3806 cc = less;
3807 break;
3808 case Token::GT:
3809 cc = greater;
3810 break;
3811 case Token::LTE:
3812 cc = less_equal;
3813 break;
3814 case Token::GTE:
3815 cc = greater_equal;
3816 break;
3817 case Token::IN: {
3818 Load(left);
3819 Load(right);
3820 Result answer = frame_->InvokeBuiltin(Builtins::IN, CALL_FUNCTION, 2);
3821 frame_->Push(&answer); // push the result
3822 return;
3823 }
3824 case Token::INSTANCEOF: {
3825 Load(left);
3826 Load(right);
3827 InstanceofStub stub;
3828 Result answer = frame_->CallStub(&stub, 2);
3829 answer.ToRegister();
3830 __ testq(answer.reg(), answer.reg());
3831 answer.Unuse();
3832 destination()->Split(zero);
3833 return;
3834 }
3835 default:
3836 UNREACHABLE();
3837 }
3838 Load(left);
3839 Load(right);
ager@chromium.org5c838252010-02-19 08:53:10 +00003840 Comparison(node, cc, strict, destination());
ager@chromium.org3e875802009-06-29 08:26:34 +00003841}
3842
3843
3844void CodeGenerator::VisitThisFunction(ThisFunction* node) {
3845 frame_->PushFunction();
3846}
3847
3848
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00003849void CodeGenerator::GenerateArguments(ZoneList<Expression*>* args) {
ager@chromium.org3e875802009-06-29 08:26:34 +00003850 ASSERT(args->length() == 1);
3851
3852 // ArgumentsAccessStub expects the key in rdx and the formal
3853 // parameter count in rax.
3854 Load(args->at(0));
3855 Result key = frame_->Pop();
3856 // Explicitly create a constant result.
ager@chromium.org5c838252010-02-19 08:53:10 +00003857 Result count(Handle<Smi>(Smi::FromInt(scope()->num_parameters())));
ager@chromium.org3e875802009-06-29 08:26:34 +00003858 // Call the shared stub to get to arguments[key].
3859 ArgumentsAccessStub stub(ArgumentsAccessStub::READ_ELEMENT);
3860 Result result = frame_->CallStub(&stub, &key, &count);
3861 frame_->Push(&result);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00003862}
3863
ager@chromium.orgeadaf222009-06-16 09:43:10 +00003864
ager@chromium.org5aa501c2009-06-23 07:57:28 +00003865void CodeGenerator::GenerateIsArray(ZoneList<Expression*>* args) {
ager@chromium.org3e875802009-06-29 08:26:34 +00003866 ASSERT(args->length() == 1);
3867 Load(args->at(0));
3868 Result value = frame_->Pop();
3869 value.ToRegister();
3870 ASSERT(value.is_valid());
ager@chromium.org4af710e2009-09-15 12:20:11 +00003871 Condition is_smi = masm_->CheckSmi(value.reg());
3872 destination()->false_target()->Branch(is_smi);
ager@chromium.org3e875802009-06-29 08:26:34 +00003873 // It is a heap object - get map.
3874 // Check if the object is a JS array or not.
3875 __ CmpObjectType(value.reg(), JS_ARRAY_TYPE, kScratchRegister);
3876 value.Unuse();
3877 destination()->Split(equal);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00003878}
3879
ager@chromium.org3e875802009-06-29 08:26:34 +00003880
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00003881void CodeGenerator::GenerateIsRegExp(ZoneList<Expression*>* args) {
3882 ASSERT(args->length() == 1);
3883 Load(args->at(0));
3884 Result value = frame_->Pop();
3885 value.ToRegister();
3886 ASSERT(value.is_valid());
3887 Condition is_smi = masm_->CheckSmi(value.reg());
3888 destination()->false_target()->Branch(is_smi);
3889 // It is a heap object - get map.
3890 // Check if the object is a regexp.
3891 __ CmpObjectType(value.reg(), JS_REGEXP_TYPE, kScratchRegister);
3892 value.Unuse();
3893 destination()->Split(equal);
3894}
3895
3896
ager@chromium.org6141cbe2009-11-20 12:14:52 +00003897void CodeGenerator::GenerateIsObject(ZoneList<Expression*>* args) {
3898 // This generates a fast version of:
3899 // (typeof(arg) === 'object' || %_ClassOf(arg) == 'RegExp')
3900 ASSERT(args->length() == 1);
3901 Load(args->at(0));
3902 Result obj = frame_->Pop();
3903 obj.ToRegister();
3904 Condition is_smi = masm_->CheckSmi(obj.reg());
3905 destination()->false_target()->Branch(is_smi);
3906
3907 __ Move(kScratchRegister, Factory::null_value());
3908 __ cmpq(obj.reg(), kScratchRegister);
3909 destination()->true_target()->Branch(equal);
3910
3911 __ movq(kScratchRegister, FieldOperand(obj.reg(), HeapObject::kMapOffset));
3912 // Undetectable objects behave like undefined when tested with typeof.
3913 __ testb(FieldOperand(kScratchRegister, Map::kBitFieldOffset),
3914 Immediate(1 << Map::kIsUndetectable));
3915 destination()->false_target()->Branch(not_zero);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00003916 __ movzxbq(kScratchRegister,
3917 FieldOperand(kScratchRegister, Map::kInstanceTypeOffset));
3918 __ cmpq(kScratchRegister, Immediate(FIRST_JS_OBJECT_TYPE));
3919 destination()->false_target()->Branch(below);
3920 __ cmpq(kScratchRegister, Immediate(LAST_JS_OBJECT_TYPE));
ager@chromium.org6141cbe2009-11-20 12:14:52 +00003921 obj.Unuse();
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00003922 destination()->Split(below_equal);
ager@chromium.org6141cbe2009-11-20 12:14:52 +00003923}
3924
3925
3926void CodeGenerator::GenerateIsFunction(ZoneList<Expression*>* args) {
3927 // This generates a fast version of:
3928 // (%_ClassOf(arg) === 'Function')
3929 ASSERT(args->length() == 1);
3930 Load(args->at(0));
3931 Result obj = frame_->Pop();
3932 obj.ToRegister();
3933 Condition is_smi = masm_->CheckSmi(obj.reg());
3934 destination()->false_target()->Branch(is_smi);
3935 __ CmpObjectType(obj.reg(), JS_FUNCTION_TYPE, kScratchRegister);
3936 obj.Unuse();
3937 destination()->Split(equal);
3938}
3939
3940
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003941void CodeGenerator::GenerateIsUndetectableObject(ZoneList<Expression*>* args) {
3942 ASSERT(args->length() == 1);
3943 Load(args->at(0));
3944 Result obj = frame_->Pop();
3945 obj.ToRegister();
3946 Condition is_smi = masm_->CheckSmi(obj.reg());
3947 destination()->false_target()->Branch(is_smi);
3948 __ movq(kScratchRegister, FieldOperand(obj.reg(), HeapObject::kMapOffset));
3949 __ movzxbl(kScratchRegister,
3950 FieldOperand(kScratchRegister, Map::kBitFieldOffset));
3951 __ testl(kScratchRegister, Immediate(1 << Map::kIsUndetectable));
3952 obj.Unuse();
3953 destination()->Split(not_zero);
3954}
3955
3956
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00003957void CodeGenerator::GenerateIsConstructCall(ZoneList<Expression*>* args) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00003958 ASSERT(args->length() == 0);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003959
3960 // Get the frame pointer for the calling frame.
3961 Result fp = allocator()->Allocate();
3962 __ movq(fp.reg(), Operand(rbp, StandardFrameConstants::kCallerFPOffset));
3963
3964 // Skip the arguments adaptor frame if it exists.
3965 Label check_frame_marker;
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00003966 __ SmiCompare(Operand(fp.reg(), StandardFrameConstants::kContextOffset),
3967 Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR));
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003968 __ j(not_equal, &check_frame_marker);
3969 __ movq(fp.reg(), Operand(fp.reg(), StandardFrameConstants::kCallerFPOffset));
3970
3971 // Check the marker in the calling frame.
3972 __ bind(&check_frame_marker);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00003973 __ SmiCompare(Operand(fp.reg(), StandardFrameConstants::kMarkerOffset),
3974 Smi::FromInt(StackFrame::CONSTRUCT));
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003975 fp.Unuse();
3976 destination()->Split(equal);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00003977}
3978
3979
ager@chromium.org3e875802009-06-29 08:26:34 +00003980void CodeGenerator::GenerateArgumentsLength(ZoneList<Expression*>* args) {
3981 ASSERT(args->length() == 0);
lrn@chromium.org25156de2010-04-06 13:10:27 +00003982
3983 Result fp = allocator_->Allocate();
3984 Result result = allocator_->Allocate();
3985 ASSERT(fp.is_valid() && result.is_valid());
3986
3987 Label exit;
3988
3989 // Get the number of formal parameters.
3990 __ Move(result.reg(), Smi::FromInt(scope()->num_parameters()));
3991
3992 // Check if the calling frame is an arguments adaptor frame.
3993 __ movq(fp.reg(), Operand(rbp, StandardFrameConstants::kCallerFPOffset));
3994 __ SmiCompare(Operand(fp.reg(), StandardFrameConstants::kContextOffset),
3995 Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR));
3996 __ j(not_equal, &exit);
3997
3998 // Arguments adaptor case: Read the arguments length from the
3999 // adaptor frame.
4000 __ movq(result.reg(),
4001 Operand(fp.reg(), ArgumentsAdaptorFrameConstants::kLengthOffset));
4002
4003 __ bind(&exit);
4004 result.set_type_info(TypeInfo::Smi());
4005 if (FLAG_debug_code) {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00004006 __ AbortIfNotSmi(result.reg());
lrn@chromium.org25156de2010-04-06 13:10:27 +00004007 }
ager@chromium.org3e875802009-06-29 08:26:34 +00004008 frame_->Push(&result);
4009}
4010
4011
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004012class DeferredStringCharCodeAt : public DeferredCode {
4013 public:
4014 DeferredStringCharCodeAt(Register object,
4015 Register index,
4016 Register scratch,
4017 Register result)
4018 : result_(result),
4019 char_code_at_generator_(object,
4020 index,
4021 scratch,
4022 result,
4023 &need_conversion_,
4024 &need_conversion_,
4025 &index_out_of_range_,
4026 STRING_INDEX_IS_NUMBER) {}
4027
4028 StringCharCodeAtGenerator* fast_case_generator() {
4029 return &char_code_at_generator_;
4030 }
4031
4032 virtual void Generate() {
4033 VirtualFrameRuntimeCallHelper call_helper(frame_state());
4034 char_code_at_generator_.GenerateSlow(masm(), call_helper);
4035
4036 __ bind(&need_conversion_);
4037 // Move the undefined value into the result register, which will
4038 // trigger conversion.
4039 __ LoadRoot(result_, Heap::kUndefinedValueRootIndex);
4040 __ jmp(exit_label());
4041
4042 __ bind(&index_out_of_range_);
4043 // When the index is out of range, the spec requires us to return
4044 // NaN.
4045 __ LoadRoot(result_, Heap::kNanValueRootIndex);
4046 __ jmp(exit_label());
4047 }
4048
4049 private:
4050 Register result_;
4051
4052 Label need_conversion_;
4053 Label index_out_of_range_;
4054
4055 StringCharCodeAtGenerator char_code_at_generator_;
4056};
4057
4058
4059// This generates code that performs a String.prototype.charCodeAt() call
4060// or returns a smi in order to trigger conversion.
4061void CodeGenerator::GenerateStringCharCodeAt(ZoneList<Expression*>* args) {
4062 Comment(masm_, "[ GenerateStringCharCodeAt");
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00004063 ASSERT(args->length() == 2);
4064
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00004065 Load(args->at(0));
4066 Load(args->at(1));
4067 Result index = frame_->Pop();
4068 Result object = frame_->Pop();
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00004069 object.ToRegister();
4070 index.ToRegister();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004071 // We might mutate the object register.
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00004072 frame_->Spill(object.reg());
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00004073
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00004074 // We need two extra registers.
4075 Result result = allocator()->Allocate();
4076 ASSERT(result.is_valid());
4077 Result scratch = allocator()->Allocate();
4078 ASSERT(scratch.is_valid());
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00004079
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004080 DeferredStringCharCodeAt* deferred =
4081 new DeferredStringCharCodeAt(object.reg(),
4082 index.reg(),
4083 scratch.reg(),
4084 result.reg());
4085 deferred->fast_case_generator()->GenerateFast(masm_);
4086 deferred->BindExit();
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00004087 frame_->Push(&result);
ager@chromium.org3e875802009-06-29 08:26:34 +00004088}
4089
4090
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004091class DeferredStringCharFromCode : public DeferredCode {
4092 public:
4093 DeferredStringCharFromCode(Register code,
4094 Register result)
4095 : char_from_code_generator_(code, result) {}
4096
4097 StringCharFromCodeGenerator* fast_case_generator() {
4098 return &char_from_code_generator_;
4099 }
4100
4101 virtual void Generate() {
4102 VirtualFrameRuntimeCallHelper call_helper(frame_state());
4103 char_from_code_generator_.GenerateSlow(masm(), call_helper);
4104 }
4105
4106 private:
4107 StringCharFromCodeGenerator char_from_code_generator_;
4108};
4109
4110
4111// Generates code for creating a one-char string from a char code.
4112void CodeGenerator::GenerateStringCharFromCode(ZoneList<Expression*>* args) {
4113 Comment(masm_, "[ GenerateStringCharFromCode");
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004114 ASSERT(args->length() == 1);
4115
4116 Load(args->at(0));
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00004117
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004118 Result code = frame_->Pop();
4119 code.ToRegister();
4120 ASSERT(code.is_valid());
4121
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00004122 Result result = allocator()->Allocate();
4123 ASSERT(result.is_valid());
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004124
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004125 DeferredStringCharFromCode* deferred = new DeferredStringCharFromCode(
4126 code.reg(), result.reg());
4127 deferred->fast_case_generator()->GenerateFast(masm_);
4128 deferred->BindExit();
4129 frame_->Push(&result);
4130}
4131
4132
4133class DeferredStringCharAt : public DeferredCode {
4134 public:
4135 DeferredStringCharAt(Register object,
4136 Register index,
4137 Register scratch1,
4138 Register scratch2,
4139 Register result)
4140 : result_(result),
4141 char_at_generator_(object,
4142 index,
4143 scratch1,
4144 scratch2,
4145 result,
4146 &need_conversion_,
4147 &need_conversion_,
4148 &index_out_of_range_,
4149 STRING_INDEX_IS_NUMBER) {}
4150
4151 StringCharAtGenerator* fast_case_generator() {
4152 return &char_at_generator_;
4153 }
4154
4155 virtual void Generate() {
4156 VirtualFrameRuntimeCallHelper call_helper(frame_state());
4157 char_at_generator_.GenerateSlow(masm(), call_helper);
4158
4159 __ bind(&need_conversion_);
4160 // Move smi zero into the result register, which will trigger
4161 // conversion.
4162 __ Move(result_, Smi::FromInt(0));
4163 __ jmp(exit_label());
4164
4165 __ bind(&index_out_of_range_);
4166 // When the index is out of range, the spec requires us to return
4167 // the empty string.
4168 __ LoadRoot(result_, Heap::kEmptyStringRootIndex);
4169 __ jmp(exit_label());
4170 }
4171
4172 private:
4173 Register result_;
4174
4175 Label need_conversion_;
4176 Label index_out_of_range_;
4177
4178 StringCharAtGenerator char_at_generator_;
4179};
4180
4181
4182// This generates code that performs a String.prototype.charAt() call
4183// or returns a smi in order to trigger conversion.
4184void CodeGenerator::GenerateStringCharAt(ZoneList<Expression*>* args) {
4185 Comment(masm_, "[ GenerateStringCharAt");
4186 ASSERT(args->length() == 2);
4187
4188 Load(args->at(0));
4189 Load(args->at(1));
4190 Result index = frame_->Pop();
4191 Result object = frame_->Pop();
4192 object.ToRegister();
4193 index.ToRegister();
4194 // We might mutate the object register.
4195 frame_->Spill(object.reg());
4196
4197 // We need three extra registers.
4198 Result result = allocator()->Allocate();
4199 ASSERT(result.is_valid());
4200 Result scratch1 = allocator()->Allocate();
4201 ASSERT(scratch1.is_valid());
4202 Result scratch2 = allocator()->Allocate();
4203 ASSERT(scratch2.is_valid());
4204
4205 DeferredStringCharAt* deferred =
4206 new DeferredStringCharAt(object.reg(),
4207 index.reg(),
4208 scratch1.reg(),
4209 scratch2.reg(),
4210 result.reg());
4211 deferred->fast_case_generator()->GenerateFast(masm_);
4212 deferred->BindExit();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004213 frame_->Push(&result);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004214}
4215
4216
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004217void CodeGenerator::GenerateIsNonNegativeSmi(ZoneList<Expression*>* args) {
ager@chromium.org3e875802009-06-29 08:26:34 +00004218 ASSERT(args->length() == 1);
4219 Load(args->at(0));
4220 Result value = frame_->Pop();
4221 value.ToRegister();
4222 ASSERT(value.is_valid());
ager@chromium.org4af710e2009-09-15 12:20:11 +00004223 Condition positive_smi = masm_->CheckPositiveSmi(value.reg());
ager@chromium.org3e875802009-06-29 08:26:34 +00004224 value.Unuse();
ager@chromium.org4af710e2009-09-15 12:20:11 +00004225 destination()->Split(positive_smi);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00004226}
4227
ager@chromium.org3e875802009-06-29 08:26:34 +00004228
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00004229// Generates the Math.pow method. Only handles special cases and
4230// branches to the runtime system for everything else. Please note
4231// that this function assumes that the callsite has executed ToNumber
4232// on both arguments.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004233void CodeGenerator::GenerateMathPow(ZoneList<Expression*>* args) {
4234 ASSERT(args->length() == 2);
4235 Load(args->at(0));
4236 Load(args->at(1));
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00004237
4238 Label allocate_return;
4239 // Load the two operands while leaving the values on the frame.
4240 frame()->Dup();
4241 Result exponent = frame()->Pop();
4242 exponent.ToRegister();
4243 frame()->Spill(exponent.reg());
4244 frame()->PushElementAt(1);
4245 Result base = frame()->Pop();
4246 base.ToRegister();
4247 frame()->Spill(base.reg());
4248
4249 Result answer = allocator()->Allocate();
4250 ASSERT(answer.is_valid());
4251 ASSERT(!exponent.reg().is(base.reg()));
4252 JumpTarget call_runtime;
4253
4254 // Save 1 in xmm3 - we need this several times later on.
4255 __ movl(answer.reg(), Immediate(1));
4256 __ cvtlsi2sd(xmm3, answer.reg());
4257
4258 Label exponent_nonsmi;
4259 Label base_nonsmi;
4260 // If the exponent is a heap number go to that specific case.
4261 __ JumpIfNotSmi(exponent.reg(), &exponent_nonsmi);
4262 __ JumpIfNotSmi(base.reg(), &base_nonsmi);
4263
4264 // Optimized version when y is an integer.
4265 Label powi;
4266 __ SmiToInteger32(base.reg(), base.reg());
4267 __ cvtlsi2sd(xmm0, base.reg());
4268 __ jmp(&powi);
4269 // exponent is smi and base is a heapnumber.
4270 __ bind(&base_nonsmi);
4271 __ CompareRoot(FieldOperand(base.reg(), HeapObject::kMapOffset),
4272 Heap::kHeapNumberMapRootIndex);
4273 call_runtime.Branch(not_equal);
4274
4275 __ movsd(xmm0, FieldOperand(base.reg(), HeapNumber::kValueOffset));
4276
4277 // Optimized version of pow if y is an integer.
4278 __ bind(&powi);
4279 __ SmiToInteger32(exponent.reg(), exponent.reg());
4280
4281 // Save exponent in base as we need to check if exponent is negative later.
4282 // We know that base and exponent are in different registers.
4283 __ movl(base.reg(), exponent.reg());
4284
4285 // Get absolute value of exponent.
4286 Label no_neg;
4287 __ cmpl(exponent.reg(), Immediate(0));
4288 __ j(greater_equal, &no_neg);
4289 __ negl(exponent.reg());
4290 __ bind(&no_neg);
4291
4292 // Load xmm1 with 1.
4293 __ movsd(xmm1, xmm3);
4294 Label while_true;
4295 Label no_multiply;
4296
4297 __ bind(&while_true);
4298 __ shrl(exponent.reg(), Immediate(1));
4299 __ j(not_carry, &no_multiply);
4300 __ mulsd(xmm1, xmm0);
4301 __ bind(&no_multiply);
4302 __ testl(exponent.reg(), exponent.reg());
4303 __ mulsd(xmm0, xmm0);
4304 __ j(not_zero, &while_true);
4305
4306 // x has the original value of y - if y is negative return 1/result.
4307 __ testl(base.reg(), base.reg());
4308 __ j(positive, &allocate_return);
4309 // Special case if xmm1 has reached infinity.
4310 __ movl(answer.reg(), Immediate(0x7FB00000));
4311 __ movd(xmm0, answer.reg());
4312 __ cvtss2sd(xmm0, xmm0);
4313 __ ucomisd(xmm0, xmm1);
4314 call_runtime.Branch(equal);
4315 __ divsd(xmm3, xmm1);
4316 __ movsd(xmm1, xmm3);
4317 __ jmp(&allocate_return);
4318
4319 // exponent (or both) is a heapnumber - no matter what we should now work
4320 // on doubles.
4321 __ bind(&exponent_nonsmi);
4322 __ CompareRoot(FieldOperand(exponent.reg(), HeapObject::kMapOffset),
4323 Heap::kHeapNumberMapRootIndex);
4324 call_runtime.Branch(not_equal);
4325 __ movsd(xmm1, FieldOperand(exponent.reg(), HeapNumber::kValueOffset));
4326 // Test if exponent is nan.
4327 __ ucomisd(xmm1, xmm1);
4328 call_runtime.Branch(parity_even);
4329
4330 Label base_not_smi;
4331 Label handle_special_cases;
ager@chromium.orgac091b72010-05-05 07:34:42 +00004332 __ JumpIfNotSmi(base.reg(), &base_not_smi);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00004333 __ SmiToInteger32(base.reg(), base.reg());
4334 __ cvtlsi2sd(xmm0, base.reg());
4335 __ jmp(&handle_special_cases);
4336 __ bind(&base_not_smi);
4337 __ CompareRoot(FieldOperand(base.reg(), HeapObject::kMapOffset),
4338 Heap::kHeapNumberMapRootIndex);
4339 call_runtime.Branch(not_equal);
4340 __ movl(answer.reg(), FieldOperand(base.reg(), HeapNumber::kExponentOffset));
4341 __ andl(answer.reg(), Immediate(HeapNumber::kExponentMask));
4342 __ cmpl(answer.reg(), Immediate(HeapNumber::kExponentMask));
4343 // base is NaN or +/-Infinity
4344 call_runtime.Branch(greater_equal);
4345 __ movsd(xmm0, FieldOperand(base.reg(), HeapNumber::kValueOffset));
4346
4347 // base is in xmm0 and exponent is in xmm1.
4348 __ bind(&handle_special_cases);
4349 Label not_minus_half;
4350 // Test for -0.5.
4351 // Load xmm2 with -0.5.
4352 __ movl(answer.reg(), Immediate(0xBF000000));
4353 __ movd(xmm2, answer.reg());
4354 __ cvtss2sd(xmm2, xmm2);
4355 // xmm2 now has -0.5.
4356 __ ucomisd(xmm2, xmm1);
4357 __ j(not_equal, &not_minus_half);
4358
4359 // Calculates reciprocal of square root.
4360 // Note that 1/sqrt(x) = sqrt(1/x))
4361 __ divsd(xmm3, xmm0);
4362 __ movsd(xmm1, xmm3);
4363 __ sqrtsd(xmm1, xmm1);
4364 __ jmp(&allocate_return);
4365
4366 // Test for 0.5.
4367 __ bind(&not_minus_half);
4368 // Load xmm2 with 0.5.
4369 // Since xmm3 is 1 and xmm2 is -0.5 this is simply xmm2 + xmm3.
4370 __ addsd(xmm2, xmm3);
4371 // xmm2 now has 0.5.
4372 __ comisd(xmm2, xmm1);
4373 call_runtime.Branch(not_equal);
4374
4375 // Calculates square root.
4376 __ movsd(xmm1, xmm0);
4377 __ sqrtsd(xmm1, xmm1);
4378
4379 JumpTarget done;
4380 Label failure, success;
4381 __ bind(&allocate_return);
4382 // Make a copy of the frame to enable us to handle allocation
4383 // failure after the JumpTarget jump.
4384 VirtualFrame* clone = new VirtualFrame(frame());
4385 __ AllocateHeapNumber(answer.reg(), exponent.reg(), &failure);
4386 __ movsd(FieldOperand(answer.reg(), HeapNumber::kValueOffset), xmm1);
4387 // Remove the two original values from the frame - we only need those
4388 // in the case where we branch to runtime.
4389 frame()->Drop(2);
4390 exponent.Unuse();
4391 base.Unuse();
4392 done.Jump(&answer);
4393 // Use the copy of the original frame as our current frame.
4394 RegisterFile empty_regs;
4395 SetFrame(clone, &empty_regs);
4396 // If we experience an allocation failure we branch to runtime.
4397 __ bind(&failure);
4398 call_runtime.Bind();
4399 answer = frame()->CallRuntime(Runtime::kMath_pow_cfunction, 2);
4400
4401 done.Bind(&answer);
4402 frame()->Push(&answer);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004403}
4404
4405
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00004406// Generates the Math.sqrt method. Please note - this function assumes that
4407// the callsite has executed ToNumber on the argument.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004408void CodeGenerator::GenerateMathSqrt(ZoneList<Expression*>* args) {
4409 ASSERT(args->length() == 1);
4410 Load(args->at(0));
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00004411
4412 // Leave original value on the frame if we need to call runtime.
4413 frame()->Dup();
4414 Result result = frame()->Pop();
4415 result.ToRegister();
4416 frame()->Spill(result.reg());
4417 Label runtime;
4418 Label non_smi;
4419 Label load_done;
4420 JumpTarget end;
4421
4422 __ JumpIfNotSmi(result.reg(), &non_smi);
4423 __ SmiToInteger32(result.reg(), result.reg());
4424 __ cvtlsi2sd(xmm0, result.reg());
4425 __ jmp(&load_done);
4426 __ bind(&non_smi);
4427 __ CompareRoot(FieldOperand(result.reg(), HeapObject::kMapOffset),
4428 Heap::kHeapNumberMapRootIndex);
4429 __ j(not_equal, &runtime);
4430 __ movsd(xmm0, FieldOperand(result.reg(), HeapNumber::kValueOffset));
4431
4432 __ bind(&load_done);
4433 __ sqrtsd(xmm0, xmm0);
4434 // A copy of the virtual frame to allow us to go to runtime after the
4435 // JumpTarget jump.
4436 Result scratch = allocator()->Allocate();
4437 VirtualFrame* clone = new VirtualFrame(frame());
4438 __ AllocateHeapNumber(result.reg(), scratch.reg(), &runtime);
4439
4440 __ movsd(FieldOperand(result.reg(), HeapNumber::kValueOffset), xmm0);
4441 frame()->Drop(1);
4442 scratch.Unuse();
4443 end.Jump(&result);
4444 // We only branch to runtime if we have an allocation error.
4445 // Use the copy of the original frame as our current frame.
4446 RegisterFile empty_regs;
4447 SetFrame(clone, &empty_regs);
4448 __ bind(&runtime);
4449 result = frame()->CallRuntime(Runtime::kMath_sqrt, 1);
4450
4451 end.Bind(&result);
4452 frame()->Push(&result);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004453}
4454
4455
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004456void CodeGenerator::GenerateIsSmi(ZoneList<Expression*>* args) {
ager@chromium.org3e875802009-06-29 08:26:34 +00004457 ASSERT(args->length() == 1);
4458 Load(args->at(0));
4459 Result value = frame_->Pop();
4460 value.ToRegister();
4461 ASSERT(value.is_valid());
ager@chromium.org4af710e2009-09-15 12:20:11 +00004462 Condition is_smi = masm_->CheckSmi(value.reg());
ager@chromium.org3e875802009-06-29 08:26:34 +00004463 value.Unuse();
ager@chromium.org4af710e2009-09-15 12:20:11 +00004464 destination()->Split(is_smi);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00004465}
4466
ager@chromium.org3e875802009-06-29 08:26:34 +00004467
4468void CodeGenerator::GenerateLog(ZoneList<Expression*>* args) {
4469 // Conditionally generate a log call.
4470 // Args:
4471 // 0 (literal string): The type of logging (corresponds to the flags).
4472 // This is used to determine whether or not to generate the log call.
4473 // 1 (string): Format string. Access the string at argument index 2
4474 // with '%2s' (see Logger::LogRuntime for all the formats).
4475 // 2 (array): Arguments to the format string.
4476 ASSERT_EQ(args->length(), 3);
4477#ifdef ENABLE_LOGGING_AND_PROFILING
4478 if (ShouldGenerateLog(args->at(0))) {
4479 Load(args->at(1));
4480 Load(args->at(2));
4481 frame_->CallRuntime(Runtime::kLog, 2);
4482 }
4483#endif
4484 // Finally, we're expected to leave a value on the top of the stack.
4485 frame_->Push(Factory::undefined_value());
ager@chromium.orgeadaf222009-06-16 09:43:10 +00004486}
4487
ager@chromium.org3e875802009-06-29 08:26:34 +00004488
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004489void CodeGenerator::GenerateObjectEquals(ZoneList<Expression*>* args) {
ager@chromium.org3e875802009-06-29 08:26:34 +00004490 ASSERT(args->length() == 2);
4491
4492 // Load the two objects into registers and perform the comparison.
4493 Load(args->at(0));
4494 Load(args->at(1));
4495 Result right = frame_->Pop();
4496 Result left = frame_->Pop();
4497 right.ToRegister();
4498 left.ToRegister();
4499 __ cmpq(right.reg(), left.reg());
4500 right.Unuse();
4501 left.Unuse();
4502 destination()->Split(equal);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00004503}
4504
ager@chromium.org3e875802009-06-29 08:26:34 +00004505
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +00004506void CodeGenerator::GenerateGetFramePointer(ZoneList<Expression*>* args) {
4507 ASSERT(args->length() == 0);
ager@chromium.org4af710e2009-09-15 12:20:11 +00004508 // RBP value is aligned, so it should be tagged as a smi (without necesarily
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004509 // being padded as a smi, so it should not be treated as a smi.).
ager@chromium.org4af710e2009-09-15 12:20:11 +00004510 ASSERT(kSmiTag == 0 && kSmiTagSize == 1);
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +00004511 Result rbp_as_smi = allocator_->Allocate();
4512 ASSERT(rbp_as_smi.is_valid());
4513 __ movq(rbp_as_smi.reg(), rbp);
4514 frame_->Push(&rbp_as_smi);
4515}
4516
4517
ager@chromium.org357bf652010-04-12 11:30:10 +00004518void CodeGenerator::GenerateRandomHeapNumber(
4519 ZoneList<Expression*>* args) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004520 ASSERT(args->length() == 0);
4521 frame_->SpillAll();
ager@chromium.org3e875802009-06-29 08:26:34 +00004522
ager@chromium.org357bf652010-04-12 11:30:10 +00004523 Label slow_allocate_heapnumber;
4524 Label heapnumber_allocated;
4525 __ AllocateHeapNumber(rbx, rcx, &slow_allocate_heapnumber);
4526 __ jmp(&heapnumber_allocated);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004527
ager@chromium.org357bf652010-04-12 11:30:10 +00004528 __ bind(&slow_allocate_heapnumber);
4529 // To allocate a heap number, and ensure that it is not a smi, we
4530 // call the runtime function FUnaryMinus on 0, returning the double
4531 // -0.0. A new, distinct heap number is returned each time.
4532 __ Push(Smi::FromInt(0));
4533 __ CallRuntime(Runtime::kNumberUnaryMinus, 1);
4534 __ movq(rbx, rax);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004535
ager@chromium.org357bf652010-04-12 11:30:10 +00004536 __ bind(&heapnumber_allocated);
4537
4538 // Return a random uint32 number in rax.
4539 // The fresh HeapNumber is in rbx, which is callee-save on both x64 ABIs.
4540 __ PrepareCallCFunction(0);
4541 __ CallCFunction(ExternalReference::random_uint32_function(), 0);
4542
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00004543 // Convert 32 random bits in rax to 0.(32 random bits) in a double
ager@chromium.org357bf652010-04-12 11:30:10 +00004544 // by computing:
4545 // ( 1.(20 0s)(32 random bits) x 2^20 ) - (1.0 x 2^20)).
4546 __ movl(rcx, Immediate(0x49800000)); // 1.0 x 2^20 as single.
4547 __ movd(xmm1, rcx);
4548 __ movd(xmm0, rax);
4549 __ cvtss2sd(xmm1, xmm1);
4550 __ xorpd(xmm0, xmm1);
4551 __ subsd(xmm0, xmm1);
4552 __ movsd(FieldOperand(rbx, HeapNumber::kValueOffset), xmm0);
4553
4554 __ movq(rax, rbx);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004555 Result result = allocator_->Allocate(rax);
4556 frame_->Push(&result);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00004557}
4558
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004559
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004560void CodeGenerator::GenerateRegExpExec(ZoneList<Expression*>* args) {
4561 ASSERT_EQ(args->length(), 4);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00004562
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004563 // Load the arguments on the stack and call the runtime system.
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00004564 Load(args->at(0));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004565 Load(args->at(1));
4566 Load(args->at(2));
4567 Load(args->at(3));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004568 RegExpExecStub stub;
4569 Result result = frame_->CallStub(&stub, 4);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004570 frame_->Push(&result);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00004571}
4572
ager@chromium.org3e875802009-06-29 08:26:34 +00004573
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00004574void CodeGenerator::GenerateRegExpConstructResult(ZoneList<Expression*>* args) {
4575 // No stub. This code only occurs a few times in regexp.js.
4576 const int kMaxInlineLength = 100;
4577 ASSERT_EQ(3, args->length());
4578 Load(args->at(0)); // Size of array, smi.
4579 Load(args->at(1)); // "index" property value.
4580 Load(args->at(2)); // "input" property value.
4581 {
4582 VirtualFrame::SpilledScope spilled_scope;
4583
4584 Label slowcase;
4585 Label done;
4586 __ movq(r8, Operand(rsp, kPointerSize * 2));
4587 __ JumpIfNotSmi(r8, &slowcase);
4588 __ SmiToInteger32(rbx, r8);
4589 __ cmpl(rbx, Immediate(kMaxInlineLength));
4590 __ j(above, &slowcase);
4591 // Smi-tagging is equivalent to multiplying by 2.
4592 STATIC_ASSERT(kSmiTag == 0);
4593 STATIC_ASSERT(kSmiTagSize == 1);
4594 // Allocate RegExpResult followed by FixedArray with size in ebx.
4595 // JSArray: [Map][empty properties][Elements][Length-smi][index][input]
4596 // Elements: [Map][Length][..elements..]
4597 __ AllocateInNewSpace(JSRegExpResult::kSize + FixedArray::kHeaderSize,
4598 times_pointer_size,
4599 rbx, // In: Number of elements.
4600 rax, // Out: Start of allocation (tagged).
4601 rcx, // Out: End of allocation.
4602 rdx, // Scratch register
4603 &slowcase,
4604 TAG_OBJECT);
4605 // rax: Start of allocated area, object-tagged.
4606 // rbx: Number of array elements as int32.
4607 // r8: Number of array elements as smi.
4608
4609 // Set JSArray map to global.regexp_result_map().
4610 __ movq(rdx, ContextOperand(rsi, Context::GLOBAL_INDEX));
4611 __ movq(rdx, FieldOperand(rdx, GlobalObject::kGlobalContextOffset));
4612 __ movq(rdx, ContextOperand(rdx, Context::REGEXP_RESULT_MAP_INDEX));
4613 __ movq(FieldOperand(rax, HeapObject::kMapOffset), rdx);
4614
4615 // Set empty properties FixedArray.
4616 __ Move(FieldOperand(rax, JSObject::kPropertiesOffset),
4617 Factory::empty_fixed_array());
4618
4619 // Set elements to point to FixedArray allocated right after the JSArray.
4620 __ lea(rcx, Operand(rax, JSRegExpResult::kSize));
4621 __ movq(FieldOperand(rax, JSObject::kElementsOffset), rcx);
4622
4623 // Set input, index and length fields from arguments.
4624 __ pop(FieldOperand(rax, JSRegExpResult::kInputOffset));
4625 __ pop(FieldOperand(rax, JSRegExpResult::kIndexOffset));
4626 __ lea(rsp, Operand(rsp, kPointerSize));
4627 __ movq(FieldOperand(rax, JSArray::kLengthOffset), r8);
4628
4629 // Fill out the elements FixedArray.
4630 // rax: JSArray.
4631 // rcx: FixedArray.
4632 // rbx: Number of elements in array as int32.
4633
4634 // Set map.
4635 __ Move(FieldOperand(rcx, HeapObject::kMapOffset),
4636 Factory::fixed_array_map());
4637 // Set length.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004638 __ Integer32ToSmi(rdx, rbx);
4639 __ movq(FieldOperand(rcx, FixedArray::kLengthOffset), rdx);
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00004640 // Fill contents of fixed-array with the-hole.
4641 __ Move(rdx, Factory::the_hole_value());
4642 __ lea(rcx, FieldOperand(rcx, FixedArray::kHeaderSize));
4643 // Fill fixed array elements with hole.
4644 // rax: JSArray.
4645 // rbx: Number of elements in array that remains to be filled, as int32.
4646 // rcx: Start of elements in FixedArray.
4647 // rdx: the hole.
4648 Label loop;
4649 __ testl(rbx, rbx);
4650 __ bind(&loop);
4651 __ j(less_equal, &done); // Jump if ecx is negative or zero.
4652 __ subl(rbx, Immediate(1));
4653 __ movq(Operand(rcx, rbx, times_pointer_size, 0), rdx);
4654 __ jmp(&loop);
4655
4656 __ bind(&slowcase);
4657 __ CallRuntime(Runtime::kRegExpConstructResult, 3);
4658
4659 __ bind(&done);
4660 }
4661 frame_->Forget(3);
4662 frame_->Push(rax);
4663}
4664
4665
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00004666class DeferredSearchCache: public DeferredCode {
4667 public:
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00004668 DeferredSearchCache(Register dst,
4669 Register cache,
4670 Register key,
4671 Register scratch)
4672 : dst_(dst), cache_(cache), key_(key), scratch_(scratch) {
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00004673 set_comment("[ DeferredSearchCache");
4674 }
4675
4676 virtual void Generate();
4677
4678 private:
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004679 Register dst_; // on invocation index of finger (as int32), on exit
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00004680 // holds value being looked up.
4681 Register cache_; // instance of JSFunctionResultCache.
4682 Register key_; // key being looked up.
4683 Register scratch_;
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00004684};
4685
4686
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00004687// Return a position of the element at |index| + |additional_offset|
4688// in FixedArray pointer to which is held in |array|. |index| is int32.
4689static Operand ArrayElement(Register array,
4690 Register index,
4691 int additional_offset = 0) {
4692 int offset = FixedArray::kHeaderSize + additional_offset * kPointerSize;
4693 return FieldOperand(array, index, times_pointer_size, offset);
4694}
4695
4696
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00004697void DeferredSearchCache::Generate() {
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00004698 Label first_loop, search_further, second_loop, cache_miss;
4699
4700 Immediate kEntriesIndexImm = Immediate(JSFunctionResultCache::kEntriesIndex);
4701 Immediate kEntrySizeImm = Immediate(JSFunctionResultCache::kEntrySize);
4702
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00004703 // Check the cache from finger to start of the cache.
4704 __ bind(&first_loop);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004705 __ subl(dst_, kEntrySizeImm);
4706 __ cmpl(dst_, kEntriesIndexImm);
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00004707 __ j(less, &search_further);
4708
4709 __ cmpq(ArrayElement(cache_, dst_), key_);
4710 __ j(not_equal, &first_loop);
4711
4712 __ Integer32ToSmi(scratch_, dst_);
4713 __ movq(FieldOperand(cache_, JSFunctionResultCache::kFingerOffset), scratch_);
4714 __ movq(dst_, ArrayElement(cache_, dst_, 1));
4715 __ jmp(exit_label());
4716
4717 __ bind(&search_further);
4718
4719 // Check the cache from end of cache up to finger.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004720 __ SmiToInteger32(dst_,
4721 FieldOperand(cache_,
4722 JSFunctionResultCache::kCacheSizeOffset));
4723 __ SmiToInteger32(scratch_,
4724 FieldOperand(cache_, JSFunctionResultCache::kFingerOffset));
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00004725
4726 __ bind(&second_loop);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004727 __ subl(dst_, kEntrySizeImm);
4728 __ cmpl(dst_, scratch_);
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00004729 __ j(less_equal, &cache_miss);
4730
4731 __ cmpq(ArrayElement(cache_, dst_), key_);
4732 __ j(not_equal, &second_loop);
4733
4734 __ Integer32ToSmi(scratch_, dst_);
4735 __ movq(FieldOperand(cache_, JSFunctionResultCache::kFingerOffset), scratch_);
4736 __ movq(dst_, ArrayElement(cache_, dst_, 1));
4737 __ jmp(exit_label());
4738
4739 __ bind(&cache_miss);
4740 __ push(cache_); // store a reference to cache
4741 __ push(key_); // store a key
4742 Handle<Object> receiver(Top::global_context()->global());
4743 __ Push(receiver);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00004744 __ push(key_);
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00004745 // On x64 function must be in rdi.
4746 __ movq(rdi, FieldOperand(cache_, JSFunctionResultCache::kFactoryOffset));
4747 ParameterCount expected(1);
4748 __ InvokeFunction(rdi, expected, CALL_FUNCTION);
4749
4750 // Find a place to put new cached value into.
4751 Label add_new_entry, update_cache;
4752 __ movq(rcx, Operand(rsp, kPointerSize)); // restore the cache
4753 // Possible optimization: cache size is constant for the given cache
4754 // so technically we could use a constant here. However, if we have
4755 // cache miss this optimization would hardly matter much.
4756
4757 // Check if we could add new entry to cache.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004758 __ movq(rbx, FieldOperand(rcx, FixedArray::kLengthOffset));
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00004759 __ movq(r9, FieldOperand(rcx, JSFunctionResultCache::kCacheSizeOffset));
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004760 __ SmiCompare(rbx, r9);
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00004761 __ j(greater, &add_new_entry);
4762
4763 // Check if we could evict entry after finger.
4764 __ movq(rdx, FieldOperand(rcx, JSFunctionResultCache::kFingerOffset));
4765 __ SmiToInteger32(rdx, rdx);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004766 __ SmiToInteger32(rbx, rbx);
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00004767 __ addq(rdx, kEntrySizeImm);
4768 Label forward;
4769 __ cmpq(rbx, rdx);
4770 __ j(greater, &forward);
4771 // Need to wrap over the cache.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004772 __ movl(rdx, kEntriesIndexImm);
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00004773 __ bind(&forward);
4774 __ Integer32ToSmi(r9, rdx);
4775 __ jmp(&update_cache);
4776
4777 __ bind(&add_new_entry);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004778 // r9 holds cache size as smi.
4779 __ SmiToInteger32(rdx, r9);
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00004780 __ SmiAddConstant(rbx, r9, Smi::FromInt(JSFunctionResultCache::kEntrySize));
4781 __ movq(FieldOperand(rcx, JSFunctionResultCache::kCacheSizeOffset), rbx);
4782
4783 // Update the cache itself.
4784 // rdx holds the index as int.
4785 // r9 holds the index as smi.
4786 __ bind(&update_cache);
4787 __ pop(rbx); // restore the key
4788 __ movq(FieldOperand(rcx, JSFunctionResultCache::kFingerOffset), r9);
4789 // Store key.
4790 __ movq(ArrayElement(rcx, rdx), rbx);
4791 __ RecordWrite(rcx, 0, rbx, r9);
4792
4793 // Store value.
4794 __ pop(rcx); // restore the cache.
4795 __ movq(rdx, FieldOperand(rcx, JSFunctionResultCache::kFingerOffset));
4796 __ SmiAddConstant(rdx, rdx, Smi::FromInt(1));
4797 __ movq(r9, rdx);
4798 __ SmiToInteger32(rdx, rdx);
4799 __ movq(rbx, rax);
4800 __ movq(ArrayElement(rcx, rdx), rbx);
4801 __ RecordWrite(rcx, 0, rbx, r9);
4802
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00004803 if (!dst_.is(rax)) {
4804 __ movq(dst_, rax);
4805 }
4806}
4807
4808
4809void CodeGenerator::GenerateGetFromCache(ZoneList<Expression*>* args) {
4810 ASSERT_EQ(2, args->length());
4811
4812 ASSERT_NE(NULL, args->at(0)->AsLiteral());
4813 int cache_id = Smi::cast(*(args->at(0)->AsLiteral()->handle()))->value();
4814
4815 Handle<FixedArray> jsfunction_result_caches(
4816 Top::global_context()->jsfunction_result_caches());
4817 if (jsfunction_result_caches->length() <= cache_id) {
4818 __ Abort("Attempt to use undefined cache.");
4819 frame_->Push(Factory::undefined_value());
4820 return;
4821 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00004822
4823 Load(args->at(1));
4824 Result key = frame_->Pop();
4825 key.ToRegister();
4826
4827 Result cache = allocator()->Allocate();
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00004828 ASSERT(cache.is_valid());
4829 __ movq(cache.reg(), ContextOperand(rsi, Context::GLOBAL_INDEX));
4830 __ movq(cache.reg(),
4831 FieldOperand(cache.reg(), GlobalObject::kGlobalContextOffset));
4832 __ movq(cache.reg(),
4833 ContextOperand(cache.reg(), Context::JSFUNCTION_RESULT_CACHES_INDEX));
4834 __ movq(cache.reg(),
4835 FieldOperand(cache.reg(), FixedArray::OffsetOfElementAt(cache_id)));
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00004836
4837 Result tmp = allocator()->Allocate();
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00004838 ASSERT(tmp.is_valid());
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00004839
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00004840 Result scratch = allocator()->Allocate();
4841 ASSERT(scratch.is_valid());
4842
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00004843 DeferredSearchCache* deferred = new DeferredSearchCache(tmp.reg(),
4844 cache.reg(),
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00004845 key.reg(),
4846 scratch.reg());
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00004847
4848 const int kFingerOffset =
4849 FixedArray::OffsetOfElementAt(JSFunctionResultCache::kFingerIndex);
4850 // tmp.reg() now holds finger offset as a smi.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004851 __ SmiToInteger32(tmp.reg(), FieldOperand(cache.reg(), kFingerOffset));
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00004852 __ cmpq(key.reg(), FieldOperand(cache.reg(),
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004853 tmp.reg(), times_pointer_size,
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00004854 FixedArray::kHeaderSize));
4855 deferred->Branch(not_equal);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00004856 __ movq(tmp.reg(), FieldOperand(cache.reg(),
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004857 tmp.reg(), times_pointer_size,
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00004858 FixedArray::kHeaderSize + kPointerSize));
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00004859
4860 deferred->BindExit();
4861 frame_->Push(&tmp);
4862}
4863
4864
ager@chromium.org5c838252010-02-19 08:53:10 +00004865void CodeGenerator::GenerateNumberToString(ZoneList<Expression*>* args) {
4866 ASSERT_EQ(args->length(), 1);
4867
4868 // Load the argument on the stack and jump to the runtime.
4869 Load(args->at(0));
4870
fschneider@chromium.org086aac62010-03-17 13:18:24 +00004871 NumberToStringStub stub;
4872 Result result = frame_->CallStub(&stub, 1);
4873 frame_->Push(&result);
ager@chromium.org5c838252010-02-19 08:53:10 +00004874}
4875
4876
ager@chromium.orgac091b72010-05-05 07:34:42 +00004877class DeferredSwapElements: public DeferredCode {
4878 public:
4879 DeferredSwapElements(Register object, Register index1, Register index2)
4880 : object_(object), index1_(index1), index2_(index2) {
4881 set_comment("[ DeferredSwapElements");
4882 }
4883
4884 virtual void Generate();
4885
4886 private:
4887 Register object_, index1_, index2_;
4888};
4889
4890
4891void DeferredSwapElements::Generate() {
4892 __ push(object_);
4893 __ push(index1_);
4894 __ push(index2_);
4895 __ CallRuntime(Runtime::kSwapElements, 3);
4896}
4897
4898
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00004899void CodeGenerator::GenerateSwapElements(ZoneList<Expression*>* args) {
4900 Comment cmnt(masm_, "[ GenerateSwapElements");
4901
4902 ASSERT_EQ(3, args->length());
4903
4904 Load(args->at(0));
4905 Load(args->at(1));
4906 Load(args->at(2));
4907
ager@chromium.orgac091b72010-05-05 07:34:42 +00004908 Result index2 = frame_->Pop();
4909 index2.ToRegister();
4910
4911 Result index1 = frame_->Pop();
4912 index1.ToRegister();
4913
4914 Result object = frame_->Pop();
4915 object.ToRegister();
4916
4917 Result tmp1 = allocator()->Allocate();
4918 tmp1.ToRegister();
4919 Result tmp2 = allocator()->Allocate();
4920 tmp2.ToRegister();
4921
4922 frame_->Spill(object.reg());
4923 frame_->Spill(index1.reg());
4924 frame_->Spill(index2.reg());
4925
4926 DeferredSwapElements* deferred = new DeferredSwapElements(object.reg(),
4927 index1.reg(),
4928 index2.reg());
4929
4930 // Fetch the map and check if array is in fast case.
4931 // Check that object doesn't require security checks and
4932 // has no indexed interceptor.
4933 __ CmpObjectType(object.reg(), FIRST_JS_OBJECT_TYPE, tmp1.reg());
4934 deferred->Branch(below);
4935 __ testb(FieldOperand(tmp1.reg(), Map::kBitFieldOffset),
4936 Immediate(KeyedLoadIC::kSlowCaseBitFieldMask));
4937 deferred->Branch(not_zero);
4938
4939 // Check the object's elements are in fast case.
4940 __ movq(tmp1.reg(), FieldOperand(object.reg(), JSObject::kElementsOffset));
4941 __ CompareRoot(FieldOperand(tmp1.reg(), HeapObject::kMapOffset),
4942 Heap::kFixedArrayMapRootIndex);
4943 deferred->Branch(not_equal);
4944
4945 // Check that both indices are smis.
4946 Condition both_smi = __ CheckBothSmi(index1.reg(), index2.reg());
4947 deferred->Branch(NegateCondition(both_smi));
4948
4949 // Bring addresses into index1 and index2.
4950 __ SmiToInteger32(index1.reg(), index1.reg());
4951 __ lea(index1.reg(), FieldOperand(tmp1.reg(),
4952 index1.reg(),
4953 times_pointer_size,
4954 FixedArray::kHeaderSize));
4955 __ SmiToInteger32(index2.reg(), index2.reg());
4956 __ lea(index2.reg(), FieldOperand(tmp1.reg(),
4957 index2.reg(),
4958 times_pointer_size,
4959 FixedArray::kHeaderSize));
4960
4961 // Swap elements.
4962 __ movq(object.reg(), Operand(index1.reg(), 0));
4963 __ movq(tmp2.reg(), Operand(index2.reg(), 0));
4964 __ movq(Operand(index2.reg(), 0), object.reg());
4965 __ movq(Operand(index1.reg(), 0), tmp2.reg());
4966
4967 Label done;
4968 __ InNewSpace(tmp1.reg(), tmp2.reg(), equal, &done);
4969 // Possible optimization: do a check that both values are Smis
4970 // (or them and test against Smi mask.)
4971
4972 __ movq(tmp2.reg(), tmp1.reg());
4973 RecordWriteStub recordWrite1(tmp2.reg(), index1.reg(), object.reg());
4974 __ CallStub(&recordWrite1);
4975
4976 RecordWriteStub recordWrite2(tmp1.reg(), index2.reg(), object.reg());
4977 __ CallStub(&recordWrite2);
4978
4979 __ bind(&done);
4980
4981 deferred->BindExit();
4982 frame_->Push(Factory::undefined_value());
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00004983}
4984
4985
ager@chromium.org357bf652010-04-12 11:30:10 +00004986void CodeGenerator::GenerateCallFunction(ZoneList<Expression*>* args) {
4987 Comment cmnt(masm_, "[ GenerateCallFunction");
4988
4989 ASSERT(args->length() >= 2);
4990
4991 int n_args = args->length() - 2; // for receiver and function.
4992 Load(args->at(0)); // receiver
4993 for (int i = 0; i < n_args; i++) {
4994 Load(args->at(i + 1));
4995 }
4996 Load(args->at(n_args + 1)); // function
4997 Result result = frame_->CallJSFunction(n_args);
4998 frame_->Push(&result);
4999}
5000
5001
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005002void CodeGenerator::GenerateMathSin(ZoneList<Expression*>* args) {
5003 ASSERT_EQ(args->length(), 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005004 Load(args->at(0));
ager@chromium.orgac091b72010-05-05 07:34:42 +00005005 TranscendentalCacheStub stub(TranscendentalCache::SIN);
5006 Result result = frame_->CallStub(&stub, 1);
5007 frame_->Push(&result);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005008}
5009
5010
5011void CodeGenerator::GenerateMathCos(ZoneList<Expression*>* args) {
5012 ASSERT_EQ(args->length(), 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005013 Load(args->at(0));
ager@chromium.orgac091b72010-05-05 07:34:42 +00005014 TranscendentalCacheStub stub(TranscendentalCache::COS);
5015 Result result = frame_->CallStub(&stub, 1);
5016 frame_->Push(&result);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005017}
5018
5019
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00005020void CodeGenerator::GenerateStringAdd(ZoneList<Expression*>* args) {
5021 ASSERT_EQ(2, args->length());
5022
5023 Load(args->at(0));
5024 Load(args->at(1));
5025
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00005026 StringAddStub stub(NO_STRING_ADD_FLAGS);
5027 Result answer = frame_->CallStub(&stub, 2);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00005028 frame_->Push(&answer);
5029}
5030
5031
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005032void CodeGenerator::GenerateSubString(ZoneList<Expression*>* args) {
5033 ASSERT_EQ(3, args->length());
5034
5035 Load(args->at(0));
5036 Load(args->at(1));
5037 Load(args->at(2));
5038
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005039 SubStringStub stub;
5040 Result answer = frame_->CallStub(&stub, 3);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005041 frame_->Push(&answer);
5042}
5043
5044
5045void CodeGenerator::GenerateStringCompare(ZoneList<Expression*>* args) {
5046 ASSERT_EQ(2, args->length());
5047
5048 Load(args->at(0));
5049 Load(args->at(1));
5050
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005051 StringCompareStub stub;
5052 Result answer = frame_->CallStub(&stub, 2);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005053 frame_->Push(&answer);
5054}
5055
5056
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005057void CodeGenerator::GenerateClassOf(ZoneList<Expression*>* args) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005058 ASSERT(args->length() == 1);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00005059 JumpTarget leave, null, function, non_function_constructor;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005060 Load(args->at(0)); // Load the object.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00005061 Result obj = frame_->Pop();
5062 obj.ToRegister();
5063 frame_->Spill(obj.reg());
5064
5065 // If the object is a smi, we return null.
ager@chromium.org4af710e2009-09-15 12:20:11 +00005066 Condition is_smi = masm_->CheckSmi(obj.reg());
5067 null.Branch(is_smi);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00005068
5069 // Check that the object is a JS object but take special care of JS
5070 // functions to make sure they have 'Function' as their class.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00005071
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00005072 __ CmpObjectType(obj.reg(), FIRST_JS_OBJECT_TYPE, obj.reg());
kasperl@chromium.orge959c182009-07-27 08:59:04 +00005073 null.Branch(below);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00005074
5075 // As long as JS_FUNCTION_TYPE is the last instance type and it is
5076 // right after LAST_JS_OBJECT_TYPE, we can avoid checking for
5077 // LAST_JS_OBJECT_TYPE.
5078 ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
5079 ASSERT(JS_FUNCTION_TYPE == LAST_JS_OBJECT_TYPE + 1);
5080 __ CmpInstanceType(obj.reg(), JS_FUNCTION_TYPE);
5081 function.Branch(equal);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00005082
5083 // Check if the constructor in the map is a function.
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00005084 __ movq(obj.reg(), FieldOperand(obj.reg(), Map::kConstructorOffset));
5085 __ CmpObjectType(obj.reg(), JS_FUNCTION_TYPE, kScratchRegister);
5086 non_function_constructor.Branch(not_equal);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00005087
5088 // The obj register now contains the constructor function. Grab the
5089 // instance class name from there.
5090 __ movq(obj.reg(),
5091 FieldOperand(obj.reg(), JSFunction::kSharedFunctionInfoOffset));
5092 __ movq(obj.reg(),
5093 FieldOperand(obj.reg(),
5094 SharedFunctionInfo::kInstanceClassNameOffset));
5095 frame_->Push(&obj);
5096 leave.Jump();
5097
5098 // Functions have class 'Function'.
5099 function.Bind();
5100 frame_->Push(Factory::function_class_symbol());
5101 leave.Jump();
5102
5103 // Objects with a non-function constructor have class 'Object'.
5104 non_function_constructor.Bind();
5105 frame_->Push(Factory::Object_symbol());
5106 leave.Jump();
5107
5108 // Non-JS objects have class null.
5109 null.Bind();
5110 frame_->Push(Factory::null_value());
5111
5112 // All done.
5113 leave.Bind();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005114}
5115
5116
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005117void CodeGenerator::GenerateSetValueOf(ZoneList<Expression*>* args) {
ager@chromium.org3e875802009-06-29 08:26:34 +00005118 ASSERT(args->length() == 2);
5119 JumpTarget leave;
5120 Load(args->at(0)); // Load the object.
5121 Load(args->at(1)); // Load the value.
5122 Result value = frame_->Pop();
5123 Result object = frame_->Pop();
5124 value.ToRegister();
5125 object.ToRegister();
5126
5127 // if (object->IsSmi()) return value.
ager@chromium.org4af710e2009-09-15 12:20:11 +00005128 Condition is_smi = masm_->CheckSmi(object.reg());
5129 leave.Branch(is_smi, &value);
ager@chromium.org3e875802009-06-29 08:26:34 +00005130
5131 // It is a heap object - get its map.
5132 Result scratch = allocator_->Allocate();
5133 ASSERT(scratch.is_valid());
5134 // if (!object->IsJSValue()) return value.
5135 __ CmpObjectType(object.reg(), JS_VALUE_TYPE, scratch.reg());
5136 leave.Branch(not_equal, &value);
5137
5138 // Store the value.
5139 __ movq(FieldOperand(object.reg(), JSValue::kValueOffset), value.reg());
5140 // Update the write barrier. Save the value as it will be
5141 // overwritten by the write barrier code and is needed afterward.
5142 Result duplicate_value = allocator_->Allocate();
5143 ASSERT(duplicate_value.is_valid());
5144 __ movq(duplicate_value.reg(), value.reg());
5145 // The object register is also overwritten by the write barrier and
5146 // possibly aliased in the frame.
5147 frame_->Spill(object.reg());
5148 __ RecordWrite(object.reg(), JSValue::kValueOffset, duplicate_value.reg(),
5149 scratch.reg());
5150 object.Unuse();
5151 scratch.Unuse();
5152 duplicate_value.Unuse();
5153
5154 // Leave.
5155 leave.Bind(&value);
5156 frame_->Push(&value);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00005157}
5158
ager@chromium.org3e875802009-06-29 08:26:34 +00005159
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005160void CodeGenerator::GenerateValueOf(ZoneList<Expression*>* args) {
ager@chromium.org3e875802009-06-29 08:26:34 +00005161 ASSERT(args->length() == 1);
5162 JumpTarget leave;
5163 Load(args->at(0)); // Load the object.
5164 frame_->Dup();
5165 Result object = frame_->Pop();
5166 object.ToRegister();
5167 ASSERT(object.is_valid());
5168 // if (object->IsSmi()) return object.
ager@chromium.org4af710e2009-09-15 12:20:11 +00005169 Condition is_smi = masm_->CheckSmi(object.reg());
5170 leave.Branch(is_smi);
ager@chromium.org3e875802009-06-29 08:26:34 +00005171 // It is a heap object - get map.
5172 Result temp = allocator()->Allocate();
5173 ASSERT(temp.is_valid());
5174 // if (!object->IsJSValue()) return object.
5175 __ CmpObjectType(object.reg(), JS_VALUE_TYPE, temp.reg());
5176 leave.Branch(not_equal);
5177 __ movq(temp.reg(), FieldOperand(object.reg(), JSValue::kValueOffset));
5178 object.Unuse();
5179 frame_->SetElementAt(0, &temp);
5180 leave.Bind();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00005181}
5182
ager@chromium.org3e875802009-06-29 08:26:34 +00005183
ager@chromium.orgeadaf222009-06-16 09:43:10 +00005184// -----------------------------------------------------------------------------
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005185// CodeGenerator implementation of Expressions
ager@chromium.orgeadaf222009-06-16 09:43:10 +00005186
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005187void CodeGenerator::LoadAndSpill(Expression* expression) {
ager@chromium.org3e875802009-06-29 08:26:34 +00005188 // TODO(x64): No architecture specific code. Move to shared location.
5189 ASSERT(in_spilled_code());
5190 set_in_spilled_code(false);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005191 Load(expression);
ager@chromium.org3e875802009-06-29 08:26:34 +00005192 frame_->SpillAll();
5193 set_in_spilled_code(true);
5194}
5195
5196
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005197void CodeGenerator::Load(Expression* expr) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005198#ifdef DEBUG
5199 int original_height = frame_->height();
5200#endif
5201 ASSERT(!in_spilled_code());
5202 JumpTarget true_target;
5203 JumpTarget false_target;
5204 ControlDestination dest(&true_target, &false_target, true);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005205 LoadCondition(expr, &dest, false);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005206
5207 if (dest.false_was_fall_through()) {
5208 // The false target was just bound.
5209 JumpTarget loaded;
5210 frame_->Push(Factory::false_value());
5211 // There may be dangling jumps to the true target.
5212 if (true_target.is_linked()) {
5213 loaded.Jump();
5214 true_target.Bind();
5215 frame_->Push(Factory::true_value());
5216 loaded.Bind();
5217 }
5218
5219 } else if (dest.is_used()) {
5220 // There is true, and possibly false, control flow (with true as
5221 // the fall through).
5222 JumpTarget loaded;
5223 frame_->Push(Factory::true_value());
5224 if (false_target.is_linked()) {
5225 loaded.Jump();
5226 false_target.Bind();
5227 frame_->Push(Factory::false_value());
5228 loaded.Bind();
5229 }
5230
5231 } else {
5232 // We have a valid value on top of the frame, but we still may
5233 // have dangling jumps to the true and false targets from nested
5234 // subexpressions (eg, the left subexpressions of the
5235 // short-circuited boolean operators).
5236 ASSERT(has_valid_frame());
5237 if (true_target.is_linked() || false_target.is_linked()) {
5238 JumpTarget loaded;
5239 loaded.Jump(); // Don't lose the current TOS.
5240 if (true_target.is_linked()) {
5241 true_target.Bind();
5242 frame_->Push(Factory::true_value());
5243 if (false_target.is_linked()) {
5244 loaded.Jump();
5245 }
5246 }
5247 if (false_target.is_linked()) {
5248 false_target.Bind();
5249 frame_->Push(Factory::false_value());
5250 }
5251 loaded.Bind();
5252 }
5253 }
5254
5255 ASSERT(has_valid_frame());
5256 ASSERT(frame_->height() == original_height + 1);
5257}
5258
5259
5260// Emit code to load the value of an expression to the top of the
5261// frame. If the expression is boolean-valued it may be compiled (or
5262// partially compiled) into control flow to the control destination.
5263// If force_control is true, control flow is forced.
5264void CodeGenerator::LoadCondition(Expression* x,
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005265 ControlDestination* dest,
5266 bool force_control) {
5267 ASSERT(!in_spilled_code());
5268 int original_height = frame_->height();
5269
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005270 { CodeGenState new_state(this, dest);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005271 Visit(x);
5272
5273 // If we hit a stack overflow, we may not have actually visited
5274 // the expression. In that case, we ensure that we have a
5275 // valid-looking frame state because we will continue to generate
5276 // code as we unwind the C++ stack.
5277 //
5278 // It's possible to have both a stack overflow and a valid frame
5279 // state (eg, a subexpression overflowed, visiting it returned
5280 // with a dummied frame state, and visiting this expression
5281 // returned with a normal-looking state).
5282 if (HasStackOverflow() &&
5283 !dest->is_used() &&
5284 frame_->height() == original_height) {
5285 dest->Goto(true);
5286 }
5287 }
5288
5289 if (force_control && !dest->is_used()) {
5290 // Convert the TOS value into flow to the control destination.
5291 // TODO(X64): Make control flow to control destinations work.
5292 ToBoolean(dest);
5293 }
5294
5295 ASSERT(!(force_control && !dest->is_used()));
5296 ASSERT(dest->is_used() || frame_->height() == original_height + 1);
5297}
5298
5299
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005300// ECMA-262, section 9.2, page 30: ToBoolean(). Pop the top of stack and
5301// convert it to a boolean in the condition code register or jump to
5302// 'false_target'/'true_target' as appropriate.
5303void CodeGenerator::ToBoolean(ControlDestination* dest) {
5304 Comment cmnt(masm_, "[ ToBoolean");
5305
5306 // The value to convert should be popped from the frame.
5307 Result value = frame_->Pop();
5308 value.ToRegister();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005309
ager@chromium.org5c838252010-02-19 08:53:10 +00005310 if (value.is_number()) {
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00005311 // Fast case if TypeInfo indicates only numbers.
ager@chromium.org5c838252010-02-19 08:53:10 +00005312 if (FLAG_debug_code) {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005313 __ AbortIfNotNumber(value.reg());
ager@chromium.org5c838252010-02-19 08:53:10 +00005314 }
5315 // Smi => false iff zero.
5316 __ SmiCompare(value.reg(), Smi::FromInt(0));
5317 dest->false_target()->Branch(equal);
5318 Condition is_smi = masm_->CheckSmi(value.reg());
5319 dest->true_target()->Branch(is_smi);
5320 __ fldz();
5321 __ fld_d(FieldOperand(value.reg(), HeapNumber::kValueOffset));
5322 __ FCmp();
5323 value.Unuse();
5324 dest->Split(not_zero);
5325 } else {
5326 // Fast case checks.
5327 // 'false' => false.
5328 __ CompareRoot(value.reg(), Heap::kFalseValueRootIndex);
5329 dest->false_target()->Branch(equal);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005330
ager@chromium.org5c838252010-02-19 08:53:10 +00005331 // 'true' => true.
5332 __ CompareRoot(value.reg(), Heap::kTrueValueRootIndex);
5333 dest->true_target()->Branch(equal);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005334
ager@chromium.org5c838252010-02-19 08:53:10 +00005335 // 'undefined' => false.
5336 __ CompareRoot(value.reg(), Heap::kUndefinedValueRootIndex);
5337 dest->false_target()->Branch(equal);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005338
ager@chromium.org5c838252010-02-19 08:53:10 +00005339 // Smi => false iff zero.
5340 __ SmiCompare(value.reg(), Smi::FromInt(0));
5341 dest->false_target()->Branch(equal);
5342 Condition is_smi = masm_->CheckSmi(value.reg());
5343 dest->true_target()->Branch(is_smi);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005344
ager@chromium.org5c838252010-02-19 08:53:10 +00005345 // Call the stub for all other cases.
5346 frame_->Push(&value); // Undo the Pop() from above.
5347 ToBooleanStub stub;
5348 Result temp = frame_->CallStub(&stub, 1);
5349 // Convert the result to a condition code.
5350 __ testq(temp.reg(), temp.reg());
5351 temp.Unuse();
5352 dest->Split(not_equal);
5353 }
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005354}
5355
5356
5357void CodeGenerator::LoadUnsafeSmi(Register target, Handle<Object> value) {
5358 UNIMPLEMENTED();
5359 // TODO(X64): Implement security policy for loads of smis.
5360}
5361
5362
5363bool CodeGenerator::IsUnsafeSmi(Handle<Object> value) {
5364 return false;
5365}
5366
5367//------------------------------------------------------------------------------
5368// CodeGenerator implementation of variables, lookups, and stores.
5369
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005370Reference::Reference(CodeGenerator* cgen,
5371 Expression* expression,
5372 bool persist_after_get)
5373 : cgen_(cgen),
5374 expression_(expression),
5375 type_(ILLEGAL),
5376 persist_after_get_(persist_after_get) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005377 cgen->LoadReference(this);
5378}
5379
5380
5381Reference::~Reference() {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005382 ASSERT(is_unloaded() || is_illegal());
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005383}
5384
5385
5386void CodeGenerator::LoadReference(Reference* ref) {
5387 // References are loaded from both spilled and unspilled code. Set the
5388 // state to unspilled to allow that (and explicitly spill after
5389 // construction at the construction sites).
5390 bool was_in_spilled_code = in_spilled_code_;
5391 in_spilled_code_ = false;
5392
5393 Comment cmnt(masm_, "[ LoadReference");
5394 Expression* e = ref->expression();
5395 Property* property = e->AsProperty();
5396 Variable* var = e->AsVariableProxy()->AsVariable();
5397
5398 if (property != NULL) {
5399 // The expression is either a property or a variable proxy that rewrites
5400 // to a property.
5401 Load(property->obj());
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005402 if (property->key()->IsPropertyName()) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005403 ref->set_type(Reference::NAMED);
5404 } else {
5405 Load(property->key());
5406 ref->set_type(Reference::KEYED);
5407 }
5408 } else if (var != NULL) {
5409 // The expression is a variable proxy that does not rewrite to a
5410 // property. Global variables are treated as named property references.
5411 if (var->is_global()) {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005412 // If rax is free, the register allocator prefers it. Thus the code
5413 // generator will load the global object into rax, which is where
5414 // LoadIC wants it. Most uses of Reference call LoadIC directly
5415 // after the reference is created.
5416 frame_->Spill(rax);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005417 LoadGlobal();
5418 ref->set_type(Reference::NAMED);
5419 } else {
5420 ASSERT(var->slot() != NULL);
5421 ref->set_type(Reference::SLOT);
5422 }
5423 } else {
5424 // Anything else is a runtime error.
5425 Load(e);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005426 frame_->CallRuntime(Runtime::kThrowReferenceError, 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005427 }
5428
5429 in_spilled_code_ = was_in_spilled_code;
5430}
5431
5432
5433void CodeGenerator::UnloadReference(Reference* ref) {
5434 // Pop a reference from the stack while preserving TOS.
5435 Comment cmnt(masm_, "[ UnloadReference");
5436 frame_->Nip(ref->size());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005437 ref->set_unloaded();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005438}
5439
5440
5441Operand CodeGenerator::SlotOperand(Slot* slot, Register tmp) {
5442 // Currently, this assertion will fail if we try to assign to
5443 // a constant variable that is constant because it is read-only
5444 // (such as the variable referring to a named function expression).
5445 // We need to implement assignments to read-only variables.
5446 // Ideally, we should do this during AST generation (by converting
5447 // such assignments into expression statements); however, in general
5448 // we may not be able to make the decision until past AST generation,
5449 // that is when the entire program is known.
5450 ASSERT(slot != NULL);
5451 int index = slot->index();
5452 switch (slot->type()) {
5453 case Slot::PARAMETER:
5454 return frame_->ParameterAt(index);
5455
5456 case Slot::LOCAL:
5457 return frame_->LocalAt(index);
5458
5459 case Slot::CONTEXT: {
5460 // Follow the context chain if necessary.
5461 ASSERT(!tmp.is(rsi)); // do not overwrite context register
5462 Register context = rsi;
5463 int chain_length = scope()->ContextChainLength(slot->var()->scope());
5464 for (int i = 0; i < chain_length; i++) {
5465 // Load the closure.
5466 // (All contexts, even 'with' contexts, have a closure,
5467 // and it is the same for all contexts inside a function.
5468 // There is no need to go to the function context first.)
5469 __ movq(tmp, ContextOperand(context, Context::CLOSURE_INDEX));
5470 // Load the function context (which is the incoming, outer context).
5471 __ movq(tmp, FieldOperand(tmp, JSFunction::kContextOffset));
5472 context = tmp;
5473 }
5474 // We may have a 'with' context now. Get the function context.
5475 // (In fact this mov may never be the needed, since the scope analysis
5476 // may not permit a direct context access in this case and thus we are
5477 // always at a function context. However it is safe to dereference be-
5478 // cause the function context of a function context is itself. Before
5479 // deleting this mov we should try to create a counter-example first,
5480 // though...)
5481 __ movq(tmp, ContextOperand(context, Context::FCONTEXT_INDEX));
5482 return ContextOperand(tmp, index);
5483 }
5484
5485 default:
5486 UNREACHABLE();
5487 return Operand(rsp, 0);
5488 }
5489}
5490
5491
5492Operand CodeGenerator::ContextSlotOperandCheckExtensions(Slot* slot,
5493 Result tmp,
5494 JumpTarget* slow) {
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00005495 ASSERT(slot->type() == Slot::CONTEXT);
5496 ASSERT(tmp.is_register());
5497 Register context = rsi;
5498
5499 for (Scope* s = scope(); s != slot->var()->scope(); s = s->outer_scope()) {
5500 if (s->num_heap_slots() > 0) {
5501 if (s->calls_eval()) {
5502 // Check that extension is NULL.
5503 __ cmpq(ContextOperand(context, Context::EXTENSION_INDEX),
5504 Immediate(0));
5505 slow->Branch(not_equal, not_taken);
5506 }
5507 __ movq(tmp.reg(), ContextOperand(context, Context::CLOSURE_INDEX));
5508 __ movq(tmp.reg(), FieldOperand(tmp.reg(), JSFunction::kContextOffset));
5509 context = tmp.reg();
5510 }
5511 }
5512 // Check that last extension is NULL.
5513 __ cmpq(ContextOperand(context, Context::EXTENSION_INDEX), Immediate(0));
5514 slow->Branch(not_equal, not_taken);
5515 __ movq(tmp.reg(), ContextOperand(context, Context::FCONTEXT_INDEX));
5516 return ContextOperand(tmp.reg(), slot->index());
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005517}
5518
5519
5520void CodeGenerator::LoadFromSlot(Slot* slot, TypeofState typeof_state) {
5521 if (slot->type() == Slot::LOOKUP) {
5522 ASSERT(slot->var()->is_dynamic());
5523
5524 JumpTarget slow;
5525 JumpTarget done;
5526 Result value;
5527
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005528 // Generate fast case for loading from slots that correspond to
5529 // local/global variables or arguments unless they are shadowed by
5530 // eval-introduced bindings.
5531 EmitDynamicLoadFromSlotFastCase(slot,
5532 typeof_state,
5533 &value,
5534 &slow,
5535 &done);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005536
5537 slow.Bind();
5538 // A runtime call is inevitable. We eagerly sync frame elements
5539 // to memory so that we can push the arguments directly into place
5540 // on top of the frame.
5541 frame_->SyncRange(0, frame_->element_count() - 1);
5542 frame_->EmitPush(rsi);
5543 __ movq(kScratchRegister, slot->var()->name(), RelocInfo::EMBEDDED_OBJECT);
5544 frame_->EmitPush(kScratchRegister);
5545 if (typeof_state == INSIDE_TYPEOF) {
ager@chromium.org3e875802009-06-29 08:26:34 +00005546 value =
5547 frame_->CallRuntime(Runtime::kLoadContextSlotNoReferenceError, 2);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005548 } else {
ager@chromium.org3e875802009-06-29 08:26:34 +00005549 value = frame_->CallRuntime(Runtime::kLoadContextSlot, 2);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005550 }
5551
5552 done.Bind(&value);
5553 frame_->Push(&value);
5554
5555 } else if (slot->var()->mode() == Variable::CONST) {
5556 // Const slots may contain 'the hole' value (the constant hasn't been
5557 // initialized yet) which needs to be converted into the 'undefined'
5558 // value.
5559 //
5560 // We currently spill the virtual frame because constants use the
5561 // potentially unsafe direct-frame access of SlotOperand.
5562 VirtualFrame::SpilledScope spilled_scope;
5563 Comment cmnt(masm_, "[ Load const");
5564 JumpTarget exit;
5565 __ movq(rcx, SlotOperand(slot, rcx));
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005566 __ CompareRoot(rcx, Heap::kTheHoleValueRootIndex);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005567 exit.Branch(not_equal);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005568 __ LoadRoot(rcx, Heap::kUndefinedValueRootIndex);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005569 exit.Bind();
5570 frame_->EmitPush(rcx);
5571
5572 } else if (slot->type() == Slot::PARAMETER) {
5573 frame_->PushParameterAt(slot->index());
5574
5575 } else if (slot->type() == Slot::LOCAL) {
5576 frame_->PushLocalAt(slot->index());
5577
5578 } else {
5579 // The other remaining slot types (LOOKUP and GLOBAL) cannot reach
5580 // here.
5581 //
5582 // The use of SlotOperand below is safe for an unspilled frame
5583 // because it will always be a context slot.
5584 ASSERT(slot->type() == Slot::CONTEXT);
5585 Result temp = allocator_->Allocate();
5586 ASSERT(temp.is_valid());
5587 __ movq(temp.reg(), SlotOperand(slot, temp.reg()));
5588 frame_->Push(&temp);
5589 }
5590}
5591
5592
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005593void CodeGenerator::LoadFromSlotCheckForArguments(Slot* slot,
5594 TypeofState state) {
5595 LoadFromSlot(slot, state);
5596
5597 // Bail out quickly if we're not using lazy arguments allocation.
5598 if (ArgumentsMode() != LAZY_ARGUMENTS_ALLOCATION) return;
5599
5600 // ... or if the slot isn't a non-parameter arguments slot.
5601 if (slot->type() == Slot::PARAMETER || !slot->is_arguments()) return;
5602
5603 // Pop the loaded value from the stack.
5604 Result value = frame_->Pop();
5605
5606 // If the loaded value is a constant, we know if the arguments
5607 // object has been lazily loaded yet.
5608 if (value.is_constant()) {
5609 if (value.handle()->IsTheHole()) {
5610 Result arguments = StoreArgumentsObject(false);
5611 frame_->Push(&arguments);
5612 } else {
5613 frame_->Push(&value);
5614 }
5615 return;
5616 }
5617
5618 // The loaded value is in a register. If it is the sentinel that
5619 // indicates that we haven't loaded the arguments object yet, we
5620 // need to do it now.
5621 JumpTarget exit;
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005622 __ CompareRoot(value.reg(), Heap::kTheHoleValueRootIndex);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005623 frame_->Push(&value);
5624 exit.Branch(not_equal);
5625 Result arguments = StoreArgumentsObject(false);
5626 frame_->SetElementAt(0, &arguments);
5627 exit.Bind();
5628}
5629
5630
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005631void CodeGenerator::StoreToSlot(Slot* slot, InitState init_state) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005632 if (slot->type() == Slot::LOOKUP) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005633 ASSERT(slot->var()->is_dynamic());
5634
5635 // For now, just do a runtime call. Since the call is inevitable,
5636 // we eagerly sync the virtual frame so we can directly push the
5637 // arguments into place.
5638 frame_->SyncRange(0, frame_->element_count() - 1);
5639
ager@chromium.org3e875802009-06-29 08:26:34 +00005640 frame_->EmitPush(rsi);
5641 frame_->EmitPush(slot->var()->name());
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005642
5643 Result value;
5644 if (init_state == CONST_INIT) {
5645 // Same as the case for a normal store, but ignores attribute
5646 // (e.g. READ_ONLY) of context slot so that we can initialize const
5647 // properties (introduced via eval("const foo = (some expr);")). Also,
5648 // uses the current function context instead of the top context.
5649 //
5650 // Note that we must declare the foo upon entry of eval(), via a
5651 // context slot declaration, but we cannot initialize it at the same
5652 // time, because the const declaration may be at the end of the eval
5653 // code (sigh...) and the const variable may have been used before
5654 // (where its value is 'undefined'). Thus, we can only do the
5655 // initialization when we actually encounter the expression and when
5656 // the expression operands are defined and valid, and thus we need the
5657 // split into 2 operations: declaration of the context slot followed
5658 // by initialization.
5659 value = frame_->CallRuntime(Runtime::kInitializeConstContextSlot, 3);
5660 } else {
5661 value = frame_->CallRuntime(Runtime::kStoreContextSlot, 3);
5662 }
5663 // Storing a variable must keep the (new) value on the expression
5664 // stack. This is necessary for compiling chained assignment
5665 // expressions.
5666 frame_->Push(&value);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005667 } else {
5668 ASSERT(!slot->var()->is_dynamic());
5669
5670 JumpTarget exit;
5671 if (init_state == CONST_INIT) {
5672 ASSERT(slot->var()->mode() == Variable::CONST);
5673 // Only the first const initialization must be executed (the slot
5674 // still contains 'the hole' value). When the assignment is executed,
5675 // the code is identical to a normal store (see below).
5676 //
5677 // We spill the frame in the code below because the direct-frame
5678 // access of SlotOperand is potentially unsafe with an unspilled
5679 // frame.
5680 VirtualFrame::SpilledScope spilled_scope;
5681 Comment cmnt(masm_, "[ Init const");
5682 __ movq(rcx, SlotOperand(slot, rcx));
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005683 __ CompareRoot(rcx, Heap::kTheHoleValueRootIndex);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005684 exit.Branch(not_equal);
5685 }
5686
5687 // We must execute the store. Storing a variable must keep the (new)
5688 // value on the stack. This is necessary for compiling assignment
5689 // expressions.
5690 //
5691 // Note: We will reach here even with slot->var()->mode() ==
5692 // Variable::CONST because of const declarations which will initialize
5693 // consts to 'the hole' value and by doing so, end up calling this code.
5694 if (slot->type() == Slot::PARAMETER) {
5695 frame_->StoreToParameterAt(slot->index());
5696 } else if (slot->type() == Slot::LOCAL) {
5697 frame_->StoreToLocalAt(slot->index());
5698 } else {
5699 // The other slot types (LOOKUP and GLOBAL) cannot reach here.
5700 //
5701 // The use of SlotOperand below is safe for an unspilled frame
5702 // because the slot is a context slot.
5703 ASSERT(slot->type() == Slot::CONTEXT);
5704 frame_->Dup();
5705 Result value = frame_->Pop();
5706 value.ToRegister();
5707 Result start = allocator_->Allocate();
5708 ASSERT(start.is_valid());
5709 __ movq(SlotOperand(slot, start.reg()), value.reg());
5710 // RecordWrite may destroy the value registers.
5711 //
5712 // TODO(204): Avoid actually spilling when the value is not
5713 // needed (probably the common case).
5714 frame_->Spill(value.reg());
5715 int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize;
5716 Result temp = allocator_->Allocate();
5717 ASSERT(temp.is_valid());
5718 __ RecordWrite(start.reg(), offset, value.reg(), temp.reg());
5719 // The results start, value, and temp are unused by going out of
5720 // scope.
5721 }
5722
5723 exit.Bind();
5724 }
5725}
5726
5727
5728Result CodeGenerator::LoadFromGlobalSlotCheckExtensions(
5729 Slot* slot,
5730 TypeofState typeof_state,
5731 JumpTarget* slow) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005732 // Check that no extension objects have been created by calls to
5733 // eval from the current scope to the global scope.
5734 Register context = rsi;
5735 Result tmp = allocator_->Allocate();
5736 ASSERT(tmp.is_valid()); // All non-reserved registers were available.
5737
5738 Scope* s = scope();
5739 while (s != NULL) {
5740 if (s->num_heap_slots() > 0) {
5741 if (s->calls_eval()) {
5742 // Check that extension is NULL.
5743 __ cmpq(ContextOperand(context, Context::EXTENSION_INDEX),
5744 Immediate(0));
5745 slow->Branch(not_equal, not_taken);
5746 }
5747 // Load next context in chain.
5748 __ movq(tmp.reg(), ContextOperand(context, Context::CLOSURE_INDEX));
5749 __ movq(tmp.reg(), FieldOperand(tmp.reg(), JSFunction::kContextOffset));
5750 context = tmp.reg();
5751 }
5752 // If no outer scope calls eval, we do not need to check more
5753 // context extensions. If we have reached an eval scope, we check
5754 // all extensions from this point.
5755 if (!s->outer_scope_calls_eval() || s->is_eval_scope()) break;
5756 s = s->outer_scope();
5757 }
5758
5759 if (s->is_eval_scope()) {
5760 // Loop up the context chain. There is no frame effect so it is
5761 // safe to use raw labels here.
5762 Label next, fast;
5763 if (!context.is(tmp.reg())) {
5764 __ movq(tmp.reg(), context);
5765 }
5766 // Load map for comparison into register, outside loop.
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005767 __ LoadRoot(kScratchRegister, Heap::kGlobalContextMapRootIndex);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005768 __ bind(&next);
5769 // Terminate at global context.
5770 __ cmpq(kScratchRegister, FieldOperand(tmp.reg(), HeapObject::kMapOffset));
5771 __ j(equal, &fast);
5772 // Check that extension is NULL.
5773 __ cmpq(ContextOperand(tmp.reg(), Context::EXTENSION_INDEX), Immediate(0));
5774 slow->Branch(not_equal);
5775 // Load next context in chain.
5776 __ movq(tmp.reg(), ContextOperand(tmp.reg(), Context::CLOSURE_INDEX));
5777 __ movq(tmp.reg(), FieldOperand(tmp.reg(), JSFunction::kContextOffset));
5778 __ jmp(&next);
5779 __ bind(&fast);
5780 }
5781 tmp.Unuse();
5782
5783 // All extension objects were empty and it is safe to use a global
5784 // load IC call.
5785 LoadGlobal();
5786 frame_->Push(slot->var()->name());
5787 RelocInfo::Mode mode = (typeof_state == INSIDE_TYPEOF)
5788 ? RelocInfo::CODE_TARGET
5789 : RelocInfo::CODE_TARGET_CONTEXT;
5790 Result answer = frame_->CallLoadIC(mode);
5791 // A test rax instruction following the call signals that the inobject
ager@chromium.orgab99eea2009-08-25 07:05:41 +00005792 // property case was inlined. Ensure that there is not a test rax
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005793 // instruction here.
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005794 masm_->nop();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005795 return answer;
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005796}
5797
5798
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005799void CodeGenerator::EmitDynamicLoadFromSlotFastCase(Slot* slot,
5800 TypeofState typeof_state,
5801 Result* result,
5802 JumpTarget* slow,
5803 JumpTarget* done) {
5804 // Generate fast-case code for variables that might be shadowed by
5805 // eval-introduced variables. Eval is used a lot without
5806 // introducing variables. In those cases, we do not want to
5807 // perform a runtime call for all variables in the scope
5808 // containing the eval.
5809 if (slot->var()->mode() == Variable::DYNAMIC_GLOBAL) {
5810 *result = LoadFromGlobalSlotCheckExtensions(slot, typeof_state, slow);
5811 done->Jump(result);
5812
5813 } else if (slot->var()->mode() == Variable::DYNAMIC_LOCAL) {
5814 Slot* potential_slot = slot->var()->local_if_not_shadowed()->slot();
5815 Expression* rewrite = slot->var()->local_if_not_shadowed()->rewrite();
5816 if (potential_slot != NULL) {
5817 // Generate fast case for locals that rewrite to slots.
5818 // Allocate a fresh register to use as a temp in
5819 // ContextSlotOperandCheckExtensions and to hold the result
5820 // value.
5821 *result = allocator_->Allocate();
5822 ASSERT(result->is_valid());
5823 __ movq(result->reg(),
5824 ContextSlotOperandCheckExtensions(potential_slot,
5825 *result,
5826 slow));
5827 if (potential_slot->var()->mode() == Variable::CONST) {
5828 __ CompareRoot(result->reg(), Heap::kTheHoleValueRootIndex);
5829 done->Branch(not_equal, result);
5830 __ LoadRoot(result->reg(), Heap::kUndefinedValueRootIndex);
5831 }
5832 done->Jump(result);
5833 } else if (rewrite != NULL) {
5834 // Generate fast case for argument loads.
5835 Property* property = rewrite->AsProperty();
5836 if (property != NULL) {
5837 VariableProxy* obj_proxy = property->obj()->AsVariableProxy();
5838 Literal* key_literal = property->key()->AsLiteral();
5839 if (obj_proxy != NULL &&
5840 key_literal != NULL &&
5841 obj_proxy->IsArguments() &&
5842 key_literal->handle()->IsSmi()) {
5843 // Load arguments object if there are no eval-introduced
5844 // variables. Then load the argument from the arguments
5845 // object using keyed load.
5846 Result arguments = allocator()->Allocate();
5847 ASSERT(arguments.is_valid());
5848 __ movq(arguments.reg(),
5849 ContextSlotOperandCheckExtensions(obj_proxy->var()->slot(),
5850 arguments,
5851 slow));
5852 frame_->Push(&arguments);
5853 frame_->Push(key_literal->handle());
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00005854 *result = EmitKeyedLoad();
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005855 frame_->Drop(2); // Drop key and receiver.
5856 done->Jump(result);
5857 }
5858 }
5859 }
5860 }
5861}
5862
5863
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005864void CodeGenerator::LoadGlobal() {
5865 if (in_spilled_code()) {
5866 frame_->EmitPush(GlobalObject());
5867 } else {
5868 Result temp = allocator_->Allocate();
5869 __ movq(temp.reg(), GlobalObject());
5870 frame_->Push(&temp);
5871 }
5872}
5873
5874
5875void CodeGenerator::LoadGlobalReceiver() {
5876 Result temp = allocator_->Allocate();
5877 Register reg = temp.reg();
5878 __ movq(reg, GlobalObject());
5879 __ movq(reg, FieldOperand(reg, GlobalObject::kGlobalReceiverOffset));
5880 frame_->Push(&temp);
5881}
5882
ager@chromium.org3e875802009-06-29 08:26:34 +00005883
ager@chromium.org5c838252010-02-19 08:53:10 +00005884ArgumentsAllocationMode CodeGenerator::ArgumentsMode() {
5885 if (scope()->arguments() == NULL) return NO_ARGUMENTS_ALLOCATION;
5886 ASSERT(scope()->arguments_shadow() != NULL);
ager@chromium.org3e875802009-06-29 08:26:34 +00005887 // We don't want to do lazy arguments allocation for functions that
5888 // have heap-allocated contexts, because it interfers with the
5889 // uninitialized const tracking in the context objects.
ager@chromium.org5c838252010-02-19 08:53:10 +00005890 return (scope()->num_heap_slots() > 0)
ager@chromium.org3e875802009-06-29 08:26:34 +00005891 ? EAGER_ARGUMENTS_ALLOCATION
5892 : LAZY_ARGUMENTS_ALLOCATION;
5893}
5894
5895
5896Result CodeGenerator::StoreArgumentsObject(bool initial) {
5897 ArgumentsAllocationMode mode = ArgumentsMode();
5898 ASSERT(mode != NO_ARGUMENTS_ALLOCATION);
5899
5900 Comment cmnt(masm_, "[ store arguments object");
5901 if (mode == LAZY_ARGUMENTS_ALLOCATION && initial) {
5902 // When using lazy arguments allocation, we store the hole value
5903 // as a sentinel indicating that the arguments object hasn't been
5904 // allocated yet.
5905 frame_->Push(Factory::the_hole_value());
5906 } else {
5907 ArgumentsAccessStub stub(ArgumentsAccessStub::NEW_OBJECT);
5908 frame_->PushFunction();
5909 frame_->PushReceiverSlotAddress();
ager@chromium.org5c838252010-02-19 08:53:10 +00005910 frame_->Push(Smi::FromInt(scope()->num_parameters()));
ager@chromium.org3e875802009-06-29 08:26:34 +00005911 Result result = frame_->CallStub(&stub, 3);
5912 frame_->Push(&result);
5913 }
5914
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005915
ager@chromium.org5c838252010-02-19 08:53:10 +00005916 Variable* arguments = scope()->arguments()->var();
5917 Variable* shadow = scope()->arguments_shadow()->var();
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005918 ASSERT(arguments != NULL && arguments->slot() != NULL);
5919 ASSERT(shadow != NULL && shadow->slot() != NULL);
5920 JumpTarget done;
5921 bool skip_arguments = false;
5922 if (mode == LAZY_ARGUMENTS_ALLOCATION && !initial) {
5923 // We have to skip storing into the arguments slot if it has
5924 // already been written to. This can happen if the a function
5925 // has a local variable named 'arguments'.
ager@chromium.org5c838252010-02-19 08:53:10 +00005926 LoadFromSlot(scope()->arguments()->var()->slot(), NOT_INSIDE_TYPEOF);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005927 Result probe = frame_->Pop();
5928 if (probe.is_constant()) {
5929 // We have to skip updating the arguments object if it has been
5930 // assigned a proper value.
5931 skip_arguments = !probe.handle()->IsTheHole();
5932 } else {
5933 __ CompareRoot(probe.reg(), Heap::kTheHoleValueRootIndex);
5934 probe.Unuse();
5935 done.Branch(not_equal);
ager@chromium.org3e875802009-06-29 08:26:34 +00005936 }
ager@chromium.org3e875802009-06-29 08:26:34 +00005937 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005938 if (!skip_arguments) {
5939 StoreToSlot(arguments->slot(), NOT_CONST_INIT);
5940 if (mode == LAZY_ARGUMENTS_ALLOCATION) done.Bind();
5941 }
5942 StoreToSlot(shadow->slot(), NOT_CONST_INIT);
ager@chromium.org3e875802009-06-29 08:26:34 +00005943 return frame_->Pop();
5944}
5945
5946
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005947void CodeGenerator::LoadTypeofExpression(Expression* expr) {
5948 // Special handling of identifiers as subexpressions of typeof.
5949 Variable* variable = expr->AsVariableProxy()->AsVariable();
ager@chromium.org3e875802009-06-29 08:26:34 +00005950 if (variable != NULL && !variable->is_this() && variable->is_global()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005951 // For a global variable we build the property reference
5952 // <global>.<variable> and perform a (regular non-contextual) property
5953 // load to make sure we do not get reference errors.
ager@chromium.org3e875802009-06-29 08:26:34 +00005954 Slot global(variable, Slot::CONTEXT, Context::GLOBAL_INDEX);
5955 Literal key(variable->name());
ager@chromium.org3e875802009-06-29 08:26:34 +00005956 Property property(&global, &key, RelocInfo::kNoPosition);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005957 Reference ref(this, &property);
5958 ref.GetValue();
5959 } else if (variable != NULL && variable->slot() != NULL) {
5960 // For a variable that rewrites to a slot, we signal it is the immediate
5961 // subexpression of a typeof.
5962 LoadFromSlotCheckForArguments(variable->slot(), INSIDE_TYPEOF);
ager@chromium.org3e875802009-06-29 08:26:34 +00005963 } else {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005964 // Anything else can be handled normally.
5965 Load(expr);
ager@chromium.org3e875802009-06-29 08:26:34 +00005966 }
5967}
5968
5969
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005970static bool CouldBeNaN(const Result& result) {
5971 if (result.type_info().IsSmi()) return false;
5972 if (result.type_info().IsInteger32()) return false;
5973 if (!result.is_constant()) return true;
5974 if (!result.handle()->IsHeapNumber()) return false;
5975 return isnan(HeapNumber::cast(*result.handle())->value());
5976}
5977
5978
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005979// Convert from signed to unsigned comparison to match the way EFLAGS are set
5980// by FPU and XMM compare instructions.
5981static Condition DoubleCondition(Condition cc) {
5982 switch (cc) {
5983 case less: return below;
5984 case equal: return equal;
5985 case less_equal: return below_equal;
5986 case greater: return above;
5987 case greater_equal: return above_equal;
5988 default: UNREACHABLE();
5989 }
5990 UNREACHABLE();
5991 return equal;
5992}
5993
5994
ager@chromium.org5c838252010-02-19 08:53:10 +00005995void CodeGenerator::Comparison(AstNode* node,
5996 Condition cc,
ager@chromium.org3e875802009-06-29 08:26:34 +00005997 bool strict,
5998 ControlDestination* dest) {
5999 // Strict only makes sense for equality comparisons.
6000 ASSERT(!strict || cc == equal);
6001
6002 Result left_side;
6003 Result right_side;
6004 // Implement '>' and '<=' by reversal to obtain ECMA-262 conversion order.
6005 if (cc == greater || cc == less_equal) {
6006 cc = ReverseCondition(cc);
6007 left_side = frame_->Pop();
6008 right_side = frame_->Pop();
6009 } else {
6010 right_side = frame_->Pop();
6011 left_side = frame_->Pop();
6012 }
6013 ASSERT(cc == less || cc == equal || cc == greater_equal);
6014
6015 // If either side is a constant smi, optimize the comparison.
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00006016 bool left_side_constant_smi = false;
6017 bool left_side_constant_null = false;
6018 bool left_side_constant_1_char_string = false;
6019 if (left_side.is_constant()) {
6020 left_side_constant_smi = left_side.handle()->IsSmi();
6021 left_side_constant_null = left_side.handle()->IsNull();
6022 left_side_constant_1_char_string =
6023 (left_side.handle()->IsString() &&
6024 String::cast(*left_side.handle())->length() == 1 &&
6025 String::cast(*left_side.handle())->IsAsciiRepresentation());
6026 }
6027 bool right_side_constant_smi = false;
6028 bool right_side_constant_null = false;
6029 bool right_side_constant_1_char_string = false;
6030 if (right_side.is_constant()) {
6031 right_side_constant_smi = right_side.handle()->IsSmi();
6032 right_side_constant_null = right_side.handle()->IsNull();
6033 right_side_constant_1_char_string =
6034 (right_side.handle()->IsString() &&
6035 String::cast(*right_side.handle())->length() == 1 &&
6036 String::cast(*right_side.handle())->IsAsciiRepresentation());
6037 }
ager@chromium.org3e875802009-06-29 08:26:34 +00006038
6039 if (left_side_constant_smi || right_side_constant_smi) {
6040 if (left_side_constant_smi && right_side_constant_smi) {
6041 // Trivial case, comparing two constants.
6042 int left_value = Smi::cast(*left_side.handle())->value();
6043 int right_value = Smi::cast(*right_side.handle())->value();
6044 switch (cc) {
6045 case less:
6046 dest->Goto(left_value < right_value);
6047 break;
6048 case equal:
6049 dest->Goto(left_value == right_value);
6050 break;
6051 case greater_equal:
6052 dest->Goto(left_value >= right_value);
6053 break;
6054 default:
6055 UNREACHABLE();
6056 }
ager@chromium.org5c838252010-02-19 08:53:10 +00006057 } else {
6058 // Only one side is a constant Smi.
ager@chromium.org3e875802009-06-29 08:26:34 +00006059 // If left side is a constant Smi, reverse the operands.
6060 // Since one side is a constant Smi, conversion order does not matter.
6061 if (left_side_constant_smi) {
6062 Result temp = left_side;
6063 left_side = right_side;
6064 right_side = temp;
6065 cc = ReverseCondition(cc);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006066 // This may re-introduce greater or less_equal as the value of cc.
ager@chromium.org3e875802009-06-29 08:26:34 +00006067 // CompareStub and the inline code both support all values of cc.
6068 }
6069 // Implement comparison against a constant Smi, inlining the case
6070 // where both sides are Smis.
6071 left_side.ToRegister();
ager@chromium.org5c838252010-02-19 08:53:10 +00006072 Register left_reg = left_side.reg();
6073 Handle<Object> right_val = right_side.handle();
ager@chromium.org3e875802009-06-29 08:26:34 +00006074
6075 // Here we split control flow to the stub call and inlined cases
6076 // before finally splitting it to the control destination. We use
6077 // a jump target and branching to duplicate the virtual frame at
6078 // the first split. We manually handle the off-frame references
6079 // by reconstituting them on the non-fall-through path.
6080 JumpTarget is_smi;
ager@chromium.org4af710e2009-09-15 12:20:11 +00006081
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00006082 if (left_side.is_smi()) {
6083 if (FLAG_debug_code) {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006084 __ AbortIfNotSmi(left_side.reg());
ager@chromium.org5c838252010-02-19 08:53:10 +00006085 }
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00006086 } else {
6087 Condition left_is_smi = masm_->CheckSmi(left_side.reg());
6088 is_smi.Branch(left_is_smi);
6089
6090 bool is_loop_condition = (node->AsExpression() != NULL) &&
6091 node->AsExpression()->is_loop_condition();
6092 if (!is_loop_condition && right_val->IsSmi()) {
6093 // Right side is a constant smi and left side has been checked
6094 // not to be a smi.
6095 JumpTarget not_number;
6096 __ Cmp(FieldOperand(left_reg, HeapObject::kMapOffset),
6097 Factory::heap_number_map());
6098 not_number.Branch(not_equal, &left_side);
6099 __ movsd(xmm1,
6100 FieldOperand(left_reg, HeapNumber::kValueOffset));
6101 int value = Smi::cast(*right_val)->value();
6102 if (value == 0) {
6103 __ xorpd(xmm0, xmm0);
6104 } else {
6105 Result temp = allocator()->Allocate();
6106 __ movl(temp.reg(), Immediate(value));
6107 __ cvtlsi2sd(xmm0, temp.reg());
6108 temp.Unuse();
6109 }
6110 __ ucomisd(xmm1, xmm0);
6111 // Jump to builtin for NaN.
6112 not_number.Branch(parity_even, &left_side);
6113 left_side.Unuse();
6114 dest->true_target()->Branch(DoubleCondition(cc));
6115 dest->false_target()->Jump();
6116 not_number.Bind(&left_side);
6117 }
6118
6119 // Setup and call the compare stub.
6120 CompareStub stub(cc, strict, kCantBothBeNaN);
6121 Result result = frame_->CallStub(&stub, &left_side, &right_side);
6122 result.ToRegister();
6123 __ testq(result.reg(), result.reg());
6124 result.Unuse();
6125 dest->true_target()->Branch(cc);
ager@chromium.org5c838252010-02-19 08:53:10 +00006126 dest->false_target()->Jump();
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00006127
6128 is_smi.Bind();
ager@chromium.org5c838252010-02-19 08:53:10 +00006129 }
6130
ager@chromium.org3e875802009-06-29 08:26:34 +00006131 left_side = Result(left_reg);
6132 right_side = Result(right_val);
6133 // Test smi equality and comparison by signed int comparison.
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00006134 // Both sides are smis, so we can use an Immediate.
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00006135 __ SmiCompare(left_side.reg(), Smi::cast(*right_side.handle()));
ager@chromium.org3e875802009-06-29 08:26:34 +00006136 left_side.Unuse();
6137 right_side.Unuse();
6138 dest->Split(cc);
6139 }
6140 } else if (cc == equal &&
6141 (left_side_constant_null || right_side_constant_null)) {
6142 // To make null checks efficient, we check if either the left side or
6143 // the right side is the constant 'null'.
6144 // If so, we optimize the code by inlining a null check instead of
6145 // calling the (very) general runtime routine for checking equality.
6146 Result operand = left_side_constant_null ? right_side : left_side;
6147 right_side.Unuse();
6148 left_side.Unuse();
6149 operand.ToRegister();
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006150 __ CompareRoot(operand.reg(), Heap::kNullValueRootIndex);
ager@chromium.org3e875802009-06-29 08:26:34 +00006151 if (strict) {
6152 operand.Unuse();
6153 dest->Split(equal);
6154 } else {
6155 // The 'null' value is only equal to 'undefined' if using non-strict
6156 // comparisons.
6157 dest->true_target()->Branch(equal);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006158 __ CompareRoot(operand.reg(), Heap::kUndefinedValueRootIndex);
ager@chromium.org3e875802009-06-29 08:26:34 +00006159 dest->true_target()->Branch(equal);
ager@chromium.org4af710e2009-09-15 12:20:11 +00006160 Condition is_smi = masm_->CheckSmi(operand.reg());
6161 dest->false_target()->Branch(is_smi);
ager@chromium.org3e875802009-06-29 08:26:34 +00006162
6163 // It can be an undetectable object.
6164 // Use a scratch register in preference to spilling operand.reg().
6165 Result temp = allocator()->Allocate();
6166 ASSERT(temp.is_valid());
6167 __ movq(temp.reg(),
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00006168 FieldOperand(operand.reg(), HeapObject::kMapOffset));
ager@chromium.org3e875802009-06-29 08:26:34 +00006169 __ testb(FieldOperand(temp.reg(), Map::kBitFieldOffset),
6170 Immediate(1 << Map::kIsUndetectable));
6171 temp.Unuse();
6172 operand.Unuse();
6173 dest->Split(not_zero);
6174 }
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00006175 } else if (left_side_constant_1_char_string ||
6176 right_side_constant_1_char_string) {
6177 if (left_side_constant_1_char_string && right_side_constant_1_char_string) {
6178 // Trivial case, comparing two constants.
6179 int left_value = String::cast(*left_side.handle())->Get(0);
6180 int right_value = String::cast(*right_side.handle())->Get(0);
6181 switch (cc) {
6182 case less:
6183 dest->Goto(left_value < right_value);
6184 break;
6185 case equal:
6186 dest->Goto(left_value == right_value);
6187 break;
6188 case greater_equal:
6189 dest->Goto(left_value >= right_value);
6190 break;
6191 default:
6192 UNREACHABLE();
6193 }
6194 } else {
6195 // Only one side is a constant 1 character string.
6196 // If left side is a constant 1-character string, reverse the operands.
6197 // Since one side is a constant string, conversion order does not matter.
6198 if (left_side_constant_1_char_string) {
6199 Result temp = left_side;
6200 left_side = right_side;
6201 right_side = temp;
6202 cc = ReverseCondition(cc);
6203 // This may reintroduce greater or less_equal as the value of cc.
6204 // CompareStub and the inline code both support all values of cc.
6205 }
6206 // Implement comparison against a constant string, inlining the case
6207 // where both sides are strings.
6208 left_side.ToRegister();
6209
6210 // Here we split control flow to the stub call and inlined cases
6211 // before finally splitting it to the control destination. We use
6212 // a jump target and branching to duplicate the virtual frame at
6213 // the first split. We manually handle the off-frame references
6214 // by reconstituting them on the non-fall-through path.
6215 JumpTarget is_not_string, is_string;
6216 Register left_reg = left_side.reg();
6217 Handle<Object> right_val = right_side.handle();
6218 ASSERT(StringShape(String::cast(*right_val)).IsSymbol());
6219 Condition is_smi = masm()->CheckSmi(left_reg);
6220 is_not_string.Branch(is_smi, &left_side);
6221 Result temp = allocator_->Allocate();
6222 ASSERT(temp.is_valid());
6223 __ movq(temp.reg(),
6224 FieldOperand(left_reg, HeapObject::kMapOffset));
6225 __ movzxbl(temp.reg(),
6226 FieldOperand(temp.reg(), Map::kInstanceTypeOffset));
6227 // If we are testing for equality then make use of the symbol shortcut.
6228 // Check if the left hand side has the same type as the right hand
6229 // side (which is always a symbol).
6230 if (cc == equal) {
6231 Label not_a_symbol;
6232 ASSERT(kSymbolTag != 0);
6233 // Ensure that no non-strings have the symbol bit set.
6234 ASSERT(kNotStringTag + kIsSymbolMask > LAST_TYPE);
6235 __ testb(temp.reg(), Immediate(kIsSymbolMask)); // Test the symbol bit.
6236 __ j(zero, &not_a_symbol);
6237 // They are symbols, so do identity compare.
6238 __ Cmp(left_reg, right_side.handle());
6239 dest->true_target()->Branch(equal);
6240 dest->false_target()->Branch(not_equal);
6241 __ bind(&not_a_symbol);
6242 }
6243 // Call the compare stub if the left side is not a flat ascii string.
6244 __ andb(temp.reg(),
6245 Immediate(kIsNotStringMask |
6246 kStringRepresentationMask |
6247 kStringEncodingMask));
6248 __ cmpb(temp.reg(),
6249 Immediate(kStringTag | kSeqStringTag | kAsciiStringTag));
6250 temp.Unuse();
6251 is_string.Branch(equal, &left_side);
6252
6253 // Setup and call the compare stub.
6254 is_not_string.Bind(&left_side);
6255 CompareStub stub(cc, strict, kCantBothBeNaN);
6256 Result result = frame_->CallStub(&stub, &left_side, &right_side);
6257 result.ToRegister();
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00006258 __ testq(result.reg(), result.reg());
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00006259 result.Unuse();
6260 dest->true_target()->Branch(cc);
6261 dest->false_target()->Jump();
6262
6263 is_string.Bind(&left_side);
6264 // left_side is a sequential ASCII string.
6265 ASSERT(left_side.reg().is(left_reg));
6266 right_side = Result(right_val);
6267 Result temp2 = allocator_->Allocate();
6268 ASSERT(temp2.is_valid());
6269 // Test string equality and comparison.
6270 if (cc == equal) {
6271 Label comparison_done;
ager@chromium.orgac091b72010-05-05 07:34:42 +00006272 __ SmiCompare(FieldOperand(left_side.reg(), String::kLengthOffset),
6273 Smi::FromInt(1));
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00006274 __ j(not_equal, &comparison_done);
6275 uint8_t char_value =
6276 static_cast<uint8_t>(String::cast(*right_val)->Get(0));
6277 __ cmpb(FieldOperand(left_side.reg(), SeqAsciiString::kHeaderSize),
6278 Immediate(char_value));
6279 __ bind(&comparison_done);
6280 } else {
ager@chromium.orgac091b72010-05-05 07:34:42 +00006281 __ movq(temp2.reg(),
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00006282 FieldOperand(left_side.reg(), String::kLengthOffset));
ager@chromium.orgac091b72010-05-05 07:34:42 +00006283 __ SmiSubConstant(temp2.reg(), temp2.reg(), Smi::FromInt(1));
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00006284 Label comparison;
6285 // If the length is 0 then the subtraction gave -1 which compares less
6286 // than any character.
6287 __ j(negative, &comparison);
6288 // Otherwise load the first character.
6289 __ movzxbl(temp2.reg(),
6290 FieldOperand(left_side.reg(), SeqAsciiString::kHeaderSize));
6291 __ bind(&comparison);
6292 // Compare the first character of the string with the
6293 // constant 1-character string.
6294 uint8_t char_value =
6295 static_cast<uint8_t>(String::cast(*right_side.handle())->Get(0));
6296 __ cmpb(temp2.reg(), Immediate(char_value));
6297 Label characters_were_different;
6298 __ j(not_equal, &characters_were_different);
6299 // If the first character is the same then the long string sorts after
6300 // the short one.
ager@chromium.orgac091b72010-05-05 07:34:42 +00006301 __ SmiCompare(FieldOperand(left_side.reg(), String::kLengthOffset),
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00006302 Smi::FromInt(1));
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00006303 __ bind(&characters_were_different);
6304 }
6305 temp2.Unuse();
6306 left_side.Unuse();
6307 right_side.Unuse();
6308 dest->Split(cc);
6309 }
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006310 } else {
6311 // Neither side is a constant Smi, constant 1-char string, or constant null.
ager@chromium.org3e875802009-06-29 08:26:34 +00006312 // If either side is a non-smi constant, skip the smi check.
6313 bool known_non_smi =
6314 (left_side.is_constant() && !left_side.handle()->IsSmi()) ||
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006315 (right_side.is_constant() && !right_side.handle()->IsSmi()) ||
6316 left_side.type_info().IsDouble() ||
6317 right_side.type_info().IsDouble();
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006318
6319 NaNInformation nan_info =
6320 (CouldBeNaN(left_side) && CouldBeNaN(right_side)) ?
6321 kBothCouldBeNaN :
6322 kCantBothBeNaN;
6323
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006324 // Inline number comparison handling any combination of smi's and heap
6325 // numbers if:
6326 // code is in a loop
6327 // the compare operation is different from equal
6328 // compare is not a for-loop comparison
6329 // The reason for excluding equal is that it will most likely be done
6330 // with smi's (not heap numbers) and the code to comparing smi's is inlined
6331 // separately. The same reason applies for for-loop comparison which will
6332 // also most likely be smi comparisons.
6333 bool is_loop_condition = (node->AsExpression() != NULL)
6334 && node->AsExpression()->is_loop_condition();
6335 bool inline_number_compare =
6336 loop_nesting() > 0 && cc != equal && !is_loop_condition;
6337
ager@chromium.org3e875802009-06-29 08:26:34 +00006338 left_side.ToRegister();
6339 right_side.ToRegister();
6340
6341 if (known_non_smi) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006342 // Inlined equality check:
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006343 // If at least one of the objects is not NaN, then if the objects
6344 // are identical, they are equal.
6345 if (nan_info == kCantBothBeNaN && cc == equal) {
6346 __ cmpq(left_side.reg(), right_side.reg());
6347 dest->true_target()->Branch(equal);
6348 }
6349
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006350 // Inlined number comparison:
6351 if (inline_number_compare) {
6352 GenerateInlineNumberComparison(&left_side, &right_side, cc, dest);
6353 }
6354
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00006355 CompareStub stub(cc, strict, nan_info, !inline_number_compare);
ager@chromium.org3e875802009-06-29 08:26:34 +00006356 Result answer = frame_->CallStub(&stub, &left_side, &right_side);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00006357 __ testq(answer.reg(), answer.reg()); // Sets both zero and sign flag.
ager@chromium.org3e875802009-06-29 08:26:34 +00006358 answer.Unuse();
6359 dest->Split(cc);
6360 } else {
6361 // Here we split control flow to the stub call and inlined cases
6362 // before finally splitting it to the control destination. We use
6363 // a jump target and branching to duplicate the virtual frame at
6364 // the first split. We manually handle the off-frame references
6365 // by reconstituting them on the non-fall-through path.
6366 JumpTarget is_smi;
6367 Register left_reg = left_side.reg();
6368 Register right_reg = right_side.reg();
6369
ager@chromium.org4af710e2009-09-15 12:20:11 +00006370 Condition both_smi = masm_->CheckBothSmi(left_reg, right_reg);
6371 is_smi.Branch(both_smi);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006372
6373 // Inline the equality check if both operands can't be a NaN. If both
6374 // objects are the same they are equal.
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006375 if (nan_info == kCantBothBeNaN && cc == equal) {
6376 __ cmpq(left_side.reg(), right_side.reg());
6377 dest->true_target()->Branch(equal);
6378 }
6379
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006380 // Inlined number comparison:
6381 if (inline_number_compare) {
6382 GenerateInlineNumberComparison(&left_side, &right_side, cc, dest);
6383 }
6384
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00006385 CompareStub stub(cc, strict, nan_info, !inline_number_compare);
ager@chromium.org3e875802009-06-29 08:26:34 +00006386 Result answer = frame_->CallStub(&stub, &left_side, &right_side);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00006387 __ testq(answer.reg(), answer.reg()); // Sets both zero and sign flags.
ager@chromium.org3e875802009-06-29 08:26:34 +00006388 answer.Unuse();
6389 dest->true_target()->Branch(cc);
6390 dest->false_target()->Jump();
6391
6392 is_smi.Bind();
6393 left_side = Result(left_reg);
6394 right_side = Result(right_reg);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00006395 __ SmiCompare(left_side.reg(), right_side.reg());
ager@chromium.org3e875802009-06-29 08:26:34 +00006396 right_side.Unuse();
6397 left_side.Unuse();
6398 dest->Split(cc);
6399 }
6400 }
6401}
6402
6403
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00006404// Load a comparison operand into into a XMM register. Jump to not_numbers jump
6405// target passing the left and right result if the operand is not a number.
6406static void LoadComparisonOperand(MacroAssembler* masm_,
6407 Result* operand,
6408 XMMRegister xmm_reg,
6409 Result* left_side,
6410 Result* right_side,
6411 JumpTarget* not_numbers) {
6412 Label done;
6413 if (operand->type_info().IsDouble()) {
6414 // Operand is known to be a heap number, just load it.
6415 __ movsd(xmm_reg, FieldOperand(operand->reg(), HeapNumber::kValueOffset));
6416 } else if (operand->type_info().IsSmi()) {
6417 // Operand is known to be a smi. Convert it to double and keep the original
6418 // smi.
6419 __ SmiToInteger32(kScratchRegister, operand->reg());
6420 __ cvtlsi2sd(xmm_reg, kScratchRegister);
6421 } else {
6422 // Operand type not known, check for smi or heap number.
6423 Label smi;
6424 __ JumpIfSmi(operand->reg(), &smi);
6425 if (!operand->type_info().IsNumber()) {
6426 __ LoadRoot(kScratchRegister, Heap::kHeapNumberMapRootIndex);
6427 __ cmpq(FieldOperand(operand->reg(), HeapObject::kMapOffset),
6428 kScratchRegister);
6429 not_numbers->Branch(not_equal, left_side, right_side, taken);
6430 }
6431 __ movsd(xmm_reg, FieldOperand(operand->reg(), HeapNumber::kValueOffset));
6432 __ jmp(&done);
6433
6434 __ bind(&smi);
6435 // Comvert smi to float and keep the original smi.
6436 __ SmiToInteger32(kScratchRegister, operand->reg());
6437 __ cvtlsi2sd(xmm_reg, kScratchRegister);
6438 __ jmp(&done);
6439 }
6440 __ bind(&done);
6441}
6442
6443
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006444void CodeGenerator::GenerateInlineNumberComparison(Result* left_side,
6445 Result* right_side,
6446 Condition cc,
6447 ControlDestination* dest) {
6448 ASSERT(left_side->is_register());
6449 ASSERT(right_side->is_register());
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00006450
6451 JumpTarget not_numbers;
6452 // Load left and right operand into registers xmm0 and xmm1 and compare.
6453 LoadComparisonOperand(masm_, left_side, xmm0, left_side, right_side,
6454 &not_numbers);
6455 LoadComparisonOperand(masm_, right_side, xmm1, left_side, right_side,
6456 &not_numbers);
6457 __ comisd(xmm0, xmm1);
6458 // Bail out if a NaN is involved.
6459 not_numbers.Branch(parity_even, left_side, right_side);
6460
6461 // Split to destination targets based on comparison.
6462 left_side->Unuse();
6463 right_side->Unuse();
6464 dest->true_target()->Branch(DoubleCondition(cc));
6465 dest->false_target()->Jump();
6466
6467 not_numbers.Bind(left_side, right_side);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006468}
6469
6470
ager@chromium.org3e875802009-06-29 08:26:34 +00006471class DeferredInlineBinaryOperation: public DeferredCode {
6472 public:
6473 DeferredInlineBinaryOperation(Token::Value op,
6474 Register dst,
6475 Register left,
6476 Register right,
6477 OverwriteMode mode)
6478 : op_(op), dst_(dst), left_(left), right_(right), mode_(mode) {
6479 set_comment("[ DeferredInlineBinaryOperation");
6480 }
6481
6482 virtual void Generate();
6483
6484 private:
6485 Token::Value op_;
6486 Register dst_;
6487 Register left_;
6488 Register right_;
6489 OverwriteMode mode_;
6490};
6491
6492
6493void DeferredInlineBinaryOperation::Generate() {
ager@chromium.orgac091b72010-05-05 07:34:42 +00006494 Label done;
6495 if ((op_ == Token::ADD)
6496 || (op_ ==Token::SUB)
6497 || (op_ == Token::MUL)
6498 || (op_ == Token::DIV)) {
6499 Label call_runtime;
6500 Label left_smi, right_smi, load_right, do_op;
6501 __ JumpIfSmi(left_, &left_smi);
6502 __ CompareRoot(FieldOperand(left_, HeapObject::kMapOffset),
6503 Heap::kHeapNumberMapRootIndex);
6504 __ j(not_equal, &call_runtime);
6505 __ movsd(xmm0, FieldOperand(left_, HeapNumber::kValueOffset));
6506 if (mode_ == OVERWRITE_LEFT) {
6507 __ movq(dst_, left_);
6508 }
6509 __ jmp(&load_right);
6510
6511 __ bind(&left_smi);
6512 __ SmiToInteger32(left_, left_);
6513 __ cvtlsi2sd(xmm0, left_);
6514 __ Integer32ToSmi(left_, left_);
6515 if (mode_ == OVERWRITE_LEFT) {
6516 Label alloc_failure;
6517 __ AllocateHeapNumber(dst_, no_reg, &call_runtime);
6518 }
6519
6520 __ bind(&load_right);
6521 __ JumpIfSmi(right_, &right_smi);
6522 __ CompareRoot(FieldOperand(right_, HeapObject::kMapOffset),
6523 Heap::kHeapNumberMapRootIndex);
6524 __ j(not_equal, &call_runtime);
6525 __ movsd(xmm1, FieldOperand(right_, HeapNumber::kValueOffset));
6526 if (mode_ == OVERWRITE_RIGHT) {
6527 __ movq(dst_, right_);
6528 } else if (mode_ == NO_OVERWRITE) {
6529 Label alloc_failure;
6530 __ AllocateHeapNumber(dst_, no_reg, &call_runtime);
6531 }
6532 __ jmp(&do_op);
6533
6534 __ bind(&right_smi);
6535 __ SmiToInteger32(right_, right_);
6536 __ cvtlsi2sd(xmm1, right_);
6537 __ Integer32ToSmi(right_, right_);
6538 if (mode_ == OVERWRITE_RIGHT || mode_ == NO_OVERWRITE) {
6539 Label alloc_failure;
6540 __ AllocateHeapNumber(dst_, no_reg, &call_runtime);
6541 }
6542
6543 __ bind(&do_op);
6544 switch (op_) {
6545 case Token::ADD: __ addsd(xmm0, xmm1); break;
6546 case Token::SUB: __ subsd(xmm0, xmm1); break;
6547 case Token::MUL: __ mulsd(xmm0, xmm1); break;
6548 case Token::DIV: __ divsd(xmm0, xmm1); break;
6549 default: UNREACHABLE();
6550 }
6551 __ movsd(FieldOperand(dst_, HeapNumber::kValueOffset), xmm0);
6552 __ jmp(&done);
6553
6554 __ bind(&call_runtime);
6555 }
ager@chromium.org3811b432009-10-28 14:53:37 +00006556 GenericBinaryOpStub stub(op_, mode_, NO_SMI_CODE_IN_STUB);
6557 stub.GenerateCall(masm_, left_, right_);
ager@chromium.org3e875802009-06-29 08:26:34 +00006558 if (!dst_.is(rax)) __ movq(dst_, rax);
ager@chromium.orgac091b72010-05-05 07:34:42 +00006559 __ bind(&done);
ager@chromium.org3e875802009-06-29 08:26:34 +00006560}
6561
6562
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006563static TypeInfo CalculateTypeInfo(TypeInfo operands_type,
6564 Token::Value op,
6565 const Result& right,
6566 const Result& left) {
6567 // Set TypeInfo of result according to the operation performed.
6568 // We rely on the fact that smis have a 32 bit payload on x64.
6569 STATIC_ASSERT(kSmiValueSize == 32);
6570 switch (op) {
6571 case Token::COMMA:
6572 return right.type_info();
6573 case Token::OR:
6574 case Token::AND:
6575 // Result type can be either of the two input types.
6576 return operands_type;
6577 case Token::BIT_OR:
6578 case Token::BIT_XOR:
6579 case Token::BIT_AND:
6580 // Result is always a smi.
6581 return TypeInfo::Smi();
6582 case Token::SAR:
6583 case Token::SHL:
6584 // Result is always a smi.
6585 return TypeInfo::Smi();
6586 case Token::SHR:
6587 // Result of x >>> y is always a smi if masked y >= 1, otherwise a number.
6588 return (right.is_constant() && right.handle()->IsSmi()
6589 && (Smi::cast(*right.handle())->value() & 0x1F) >= 1)
6590 ? TypeInfo::Smi()
6591 : TypeInfo::Number();
6592 case Token::ADD:
6593 if (operands_type.IsNumber()) {
6594 return TypeInfo::Number();
6595 } else if (left.type_info().IsString() || right.type_info().IsString()) {
6596 return TypeInfo::String();
6597 } else {
6598 return TypeInfo::Unknown();
6599 }
6600 case Token::SUB:
6601 case Token::MUL:
6602 case Token::DIV:
6603 case Token::MOD:
6604 // Result is always a number.
6605 return TypeInfo::Number();
6606 default:
6607 UNREACHABLE();
6608 }
6609 UNREACHABLE();
6610 return TypeInfo::Unknown();
6611}
6612
6613
ager@chromium.orgac091b72010-05-05 07:34:42 +00006614void CodeGenerator::GenericBinaryOperation(BinaryOperation* expr,
ager@chromium.org3e875802009-06-29 08:26:34 +00006615 OverwriteMode overwrite_mode) {
6616 Comment cmnt(masm_, "[ BinaryOperation");
ager@chromium.orgac091b72010-05-05 07:34:42 +00006617 Token::Value op = expr->op();
ager@chromium.org3e875802009-06-29 08:26:34 +00006618 Comment cmnt_token(masm_, Token::String(op));
6619
6620 if (op == Token::COMMA) {
6621 // Simply discard left value.
6622 frame_->Nip(1);
6623 return;
6624 }
6625
ager@chromium.org3e875802009-06-29 08:26:34 +00006626 Result right = frame_->Pop();
6627 Result left = frame_->Pop();
6628
6629 if (op == Token::ADD) {
lrn@chromium.org25156de2010-04-06 13:10:27 +00006630 const bool left_is_string = left.type_info().IsString();
6631 const bool right_is_string = right.type_info().IsString();
6632 // Make sure constant strings have string type info.
6633 ASSERT(!(left.is_constant() && left.handle()->IsString()) ||
6634 left_is_string);
6635 ASSERT(!(right.is_constant() && right.handle()->IsString()) ||
6636 right_is_string);
ager@chromium.org3e875802009-06-29 08:26:34 +00006637 if (left_is_string || right_is_string) {
6638 frame_->Push(&left);
6639 frame_->Push(&right);
6640 Result answer;
6641 if (left_is_string) {
6642 if (right_is_string) {
lrn@chromium.org25156de2010-04-06 13:10:27 +00006643 StringAddStub stub(NO_STRING_CHECK_IN_STUB);
6644 answer = frame_->CallStub(&stub, 2);
ager@chromium.org3e875802009-06-29 08:26:34 +00006645 } else {
6646 answer =
6647 frame_->InvokeBuiltin(Builtins::STRING_ADD_LEFT, CALL_FUNCTION, 2);
6648 }
6649 } else if (right_is_string) {
6650 answer =
6651 frame_->InvokeBuiltin(Builtins::STRING_ADD_RIGHT, CALL_FUNCTION, 2);
6652 }
lrn@chromium.org25156de2010-04-06 13:10:27 +00006653 answer.set_type_info(TypeInfo::String());
ager@chromium.org3e875802009-06-29 08:26:34 +00006654 frame_->Push(&answer);
6655 return;
6656 }
6657 // Neither operand is known to be a string.
6658 }
6659
ager@chromium.org5c838252010-02-19 08:53:10 +00006660 bool left_is_smi_constant = left.is_constant() && left.handle()->IsSmi();
6661 bool left_is_non_smi_constant = left.is_constant() && !left.handle()->IsSmi();
6662 bool right_is_smi_constant = right.is_constant() && right.handle()->IsSmi();
6663 bool right_is_non_smi_constant =
6664 right.is_constant() && !right.handle()->IsSmi();
ager@chromium.org3e875802009-06-29 08:26:34 +00006665
ager@chromium.org5c838252010-02-19 08:53:10 +00006666 if (left_is_smi_constant && right_is_smi_constant) {
ager@chromium.org3e875802009-06-29 08:26:34 +00006667 // Compute the constant result at compile time, and leave it on the frame.
6668 int left_int = Smi::cast(*left.handle())->value();
6669 int right_int = Smi::cast(*right.handle())->value();
6670 if (FoldConstantSmis(op, left_int, right_int)) return;
6671 }
6672
ager@chromium.org5c838252010-02-19 08:53:10 +00006673 // Get number type of left and right sub-expressions.
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00006674 TypeInfo operands_type =
6675 TypeInfo::Combine(left.type_info(), right.type_info());
ager@chromium.org5c838252010-02-19 08:53:10 +00006676
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006677 TypeInfo result_type = CalculateTypeInfo(operands_type, op, right, left);
6678
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006679 Result answer;
ager@chromium.org5c838252010-02-19 08:53:10 +00006680 if (left_is_non_smi_constant || right_is_non_smi_constant) {
ager@chromium.orgac091b72010-05-05 07:34:42 +00006681 // Go straight to the slow case, with no smi code.
ager@chromium.org5c838252010-02-19 08:53:10 +00006682 GenericBinaryOpStub stub(op,
6683 overwrite_mode,
6684 NO_SMI_CODE_IN_STUB,
6685 operands_type);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006686 answer = stub.GenerateCall(masm_, frame_, &left, &right);
ager@chromium.org5c838252010-02-19 08:53:10 +00006687 } else if (right_is_smi_constant) {
ager@chromium.orgac091b72010-05-05 07:34:42 +00006688 answer = ConstantSmiBinaryOperation(expr, &left, right.handle(),
6689 false, overwrite_mode);
ager@chromium.org5c838252010-02-19 08:53:10 +00006690 } else if (left_is_smi_constant) {
ager@chromium.orgac091b72010-05-05 07:34:42 +00006691 answer = ConstantSmiBinaryOperation(expr, &right, left.handle(),
6692 true, overwrite_mode);
ager@chromium.org3e875802009-06-29 08:26:34 +00006693 } else {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006694 // Set the flags based on the operation, type and loop nesting level.
6695 // Bit operations always assume they likely operate on Smis. Still only
6696 // generate the inline Smi check code if this operation is part of a loop.
6697 // For all other operations only inline the Smi check code for likely smis
6698 // if the operation is part of a loop.
ager@chromium.orgac091b72010-05-05 07:34:42 +00006699 if (loop_nesting() > 0 &&
6700 (Token::IsBitOp(op) ||
6701 operands_type.IsInteger32() ||
6702 expr->type()->IsLikelySmi())) {
6703 answer = LikelySmiBinaryOperation(expr, &left, &right, overwrite_mode);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006704 } else {
ager@chromium.org5c838252010-02-19 08:53:10 +00006705 GenericBinaryOpStub stub(op,
6706 overwrite_mode,
6707 NO_GENERIC_BINARY_FLAGS,
6708 operands_type);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006709 answer = stub.GenerateCall(masm_, frame_, &left, &right);
ager@chromium.org3e875802009-06-29 08:26:34 +00006710 }
ager@chromium.org3e875802009-06-29 08:26:34 +00006711 }
ager@chromium.org5c838252010-02-19 08:53:10 +00006712
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00006713 answer.set_type_info(result_type);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006714 frame_->Push(&answer);
ager@chromium.org3e875802009-06-29 08:26:34 +00006715}
6716
6717
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006718// Emit a LoadIC call to get the value from receiver and leave it in
6719// dst. The receiver register is restored after the call.
6720class DeferredReferenceGetNamedValue: public DeferredCode {
6721 public:
6722 DeferredReferenceGetNamedValue(Register dst,
6723 Register receiver,
6724 Handle<String> name)
6725 : dst_(dst), receiver_(receiver), name_(name) {
6726 set_comment("[ DeferredReferenceGetNamedValue");
6727 }
6728
6729 virtual void Generate();
6730
6731 Label* patch_site() { return &patch_site_; }
6732
6733 private:
6734 Label patch_site_;
6735 Register dst_;
6736 Register receiver_;
6737 Handle<String> name_;
6738};
6739
6740
6741void DeferredReferenceGetNamedValue::Generate() {
sgjesse@chromium.org82dbbab2010-06-02 08:57:44 +00006742 if (!receiver_.is(rax)) {
6743 __ movq(rax, receiver_);
6744 }
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006745 __ Move(rcx, name_);
6746 Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize));
6747 __ Call(ic, RelocInfo::CODE_TARGET);
ager@chromium.org3e875802009-06-29 08:26:34 +00006748 // The call must be followed by a test rax instruction to indicate
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006749 // that the inobject property case was inlined.
6750 //
6751 // Store the delta to the map check instruction here in the test
6752 // instruction. Use masm_-> instead of the __ macro since the
6753 // latter can't return a value.
6754 int delta_to_patch_site = masm_->SizeOfCodeGeneratedSince(patch_site());
6755 // Here we use masm_-> instead of the __ macro because this is the
6756 // instruction that gets patched and coverage code gets in the way.
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00006757 masm_->testl(rax, Immediate(-delta_to_patch_site));
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006758 __ IncrementCounter(&Counters::named_load_inline_miss, 1);
6759
6760 if (!dst_.is(rax)) __ movq(dst_, rax);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006761}
6762
6763
ager@chromium.org3e875802009-06-29 08:26:34 +00006764void DeferredInlineSmiAdd::Generate() {
ager@chromium.org3811b432009-10-28 14:53:37 +00006765 GenericBinaryOpStub igostub(Token::ADD, overwrite_mode_, NO_SMI_CODE_IN_STUB);
6766 igostub.GenerateCall(masm_, dst_, value_);
ager@chromium.org3e875802009-06-29 08:26:34 +00006767 if (!dst_.is(rax)) __ movq(dst_, rax);
6768}
6769
6770
ager@chromium.org3e875802009-06-29 08:26:34 +00006771void DeferredInlineSmiAddReversed::Generate() {
ager@chromium.org3811b432009-10-28 14:53:37 +00006772 GenericBinaryOpStub igostub(Token::ADD, overwrite_mode_, NO_SMI_CODE_IN_STUB);
6773 igostub.GenerateCall(masm_, value_, dst_);
ager@chromium.org3e875802009-06-29 08:26:34 +00006774 if (!dst_.is(rax)) __ movq(dst_, rax);
6775}
6776
6777
ager@chromium.org3e875802009-06-29 08:26:34 +00006778void DeferredInlineSmiSub::Generate() {
ager@chromium.org3811b432009-10-28 14:53:37 +00006779 GenericBinaryOpStub igostub(Token::SUB, overwrite_mode_, NO_SMI_CODE_IN_STUB);
6780 igostub.GenerateCall(masm_, dst_, value_);
ager@chromium.org3e875802009-06-29 08:26:34 +00006781 if (!dst_.is(rax)) __ movq(dst_, rax);
6782}
6783
6784
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00006785void DeferredInlineSmiOperation::Generate() {
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00006786 // For mod we don't generate all the Smi code inline.
6787 GenericBinaryOpStub stub(
6788 op_,
6789 overwrite_mode_,
ager@chromium.org3811b432009-10-28 14:53:37 +00006790 (op_ == Token::MOD) ? NO_GENERIC_BINARY_FLAGS : NO_SMI_CODE_IN_STUB);
6791 stub.GenerateCall(masm_, src_, value_);
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00006792 if (!dst_.is(rax)) __ movq(dst_, rax);
6793}
6794
6795
ager@chromium.orgac091b72010-05-05 07:34:42 +00006796void DeferredInlineSmiOperationReversed::Generate() {
6797 GenericBinaryOpStub stub(
6798 op_,
6799 overwrite_mode_,
6800 NO_SMI_CODE_IN_STUB);
6801 stub.GenerateCall(masm_, value_, src_);
6802 if (!dst_.is(rax)) __ movq(dst_, rax);
6803}
6804
6805
6806Result CodeGenerator::ConstantSmiBinaryOperation(BinaryOperation* expr,
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006807 Result* operand,
6808 Handle<Object> value,
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006809 bool reversed,
6810 OverwriteMode overwrite_mode) {
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00006811 // Generate inline code for a binary operation when one of the
6812 // operands is a constant smi. Consumes the argument "operand".
ager@chromium.org3e875802009-06-29 08:26:34 +00006813 if (IsUnsafeSmi(value)) {
6814 Result unsafe_operand(value);
6815 if (reversed) {
ager@chromium.orgac091b72010-05-05 07:34:42 +00006816 return LikelySmiBinaryOperation(expr, &unsafe_operand, operand,
ager@chromium.org3e875802009-06-29 08:26:34 +00006817 overwrite_mode);
6818 } else {
ager@chromium.orgac091b72010-05-05 07:34:42 +00006819 return LikelySmiBinaryOperation(expr, operand, &unsafe_operand,
ager@chromium.org3e875802009-06-29 08:26:34 +00006820 overwrite_mode);
6821 }
ager@chromium.org3e875802009-06-29 08:26:34 +00006822 }
6823
6824 // Get the literal value.
6825 Smi* smi_value = Smi::cast(*value);
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00006826 int int_value = smi_value->value();
ager@chromium.org3e875802009-06-29 08:26:34 +00006827
ager@chromium.orgac091b72010-05-05 07:34:42 +00006828 Token::Value op = expr->op();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006829 Result answer;
ager@chromium.org3e875802009-06-29 08:26:34 +00006830 switch (op) {
6831 case Token::ADD: {
6832 operand->ToRegister();
6833 frame_->Spill(operand->reg());
ager@chromium.org3e875802009-06-29 08:26:34 +00006834 DeferredCode* deferred = NULL;
6835 if (reversed) {
6836 deferred = new DeferredInlineSmiAddReversed(operand->reg(),
6837 smi_value,
6838 overwrite_mode);
6839 } else {
6840 deferred = new DeferredInlineSmiAdd(operand->reg(),
6841 smi_value,
6842 overwrite_mode);
6843 }
ager@chromium.org4af710e2009-09-15 12:20:11 +00006844 __ JumpIfNotSmi(operand->reg(), deferred->entry_label());
6845 __ SmiAddConstant(operand->reg(),
6846 operand->reg(),
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00006847 smi_value,
ager@chromium.org4af710e2009-09-15 12:20:11 +00006848 deferred->entry_label());
ager@chromium.org3e875802009-06-29 08:26:34 +00006849 deferred->BindExit();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006850 answer = *operand;
ager@chromium.org3e875802009-06-29 08:26:34 +00006851 break;
6852 }
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00006853
6854 case Token::SUB: {
6855 if (reversed) {
6856 Result constant_operand(value);
ager@chromium.orgac091b72010-05-05 07:34:42 +00006857 answer = LikelySmiBinaryOperation(expr, &constant_operand, operand,
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006858 overwrite_mode);
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00006859 } else {
6860 operand->ToRegister();
6861 frame_->Spill(operand->reg());
6862 DeferredCode* deferred = new DeferredInlineSmiSub(operand->reg(),
6863 smi_value,
6864 overwrite_mode);
ager@chromium.org4af710e2009-09-15 12:20:11 +00006865 __ JumpIfNotSmi(operand->reg(), deferred->entry_label());
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00006866 // A smi currently fits in a 32-bit Immediate.
ager@chromium.org4af710e2009-09-15 12:20:11 +00006867 __ SmiSubConstant(operand->reg(),
6868 operand->reg(),
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00006869 smi_value,
ager@chromium.org4af710e2009-09-15 12:20:11 +00006870 deferred->entry_label());
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00006871 deferred->BindExit();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006872 answer = *operand;
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00006873 }
6874 break;
6875 }
6876
6877 case Token::SAR:
6878 if (reversed) {
6879 Result constant_operand(value);
ager@chromium.orgac091b72010-05-05 07:34:42 +00006880 answer = LikelySmiBinaryOperation(expr, &constant_operand, operand,
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006881 overwrite_mode);
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00006882 } else {
6883 // Only the least significant 5 bits of the shift value are used.
6884 // In the slow case, this masking is done inside the runtime call.
6885 int shift_value = int_value & 0x1f;
6886 operand->ToRegister();
6887 frame_->Spill(operand->reg());
6888 DeferredInlineSmiOperation* deferred =
6889 new DeferredInlineSmiOperation(op,
6890 operand->reg(),
6891 operand->reg(),
6892 smi_value,
6893 overwrite_mode);
ager@chromium.org4af710e2009-09-15 12:20:11 +00006894 __ JumpIfNotSmi(operand->reg(), deferred->entry_label());
6895 __ SmiShiftArithmeticRightConstant(operand->reg(),
6896 operand->reg(),
6897 shift_value);
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00006898 deferred->BindExit();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006899 answer = *operand;
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00006900 }
6901 break;
6902
6903 case Token::SHR:
6904 if (reversed) {
6905 Result constant_operand(value);
ager@chromium.orgac091b72010-05-05 07:34:42 +00006906 answer = LikelySmiBinaryOperation(expr, &constant_operand, operand,
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006907 overwrite_mode);
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00006908 } else {
6909 // Only the least significant 5 bits of the shift value are used.
6910 // In the slow case, this masking is done inside the runtime call.
6911 int shift_value = int_value & 0x1f;
6912 operand->ToRegister();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006913 answer = allocator()->Allocate();
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00006914 ASSERT(answer.is_valid());
6915 DeferredInlineSmiOperation* deferred =
6916 new DeferredInlineSmiOperation(op,
6917 answer.reg(),
6918 operand->reg(),
6919 smi_value,
6920 overwrite_mode);
ager@chromium.org4af710e2009-09-15 12:20:11 +00006921 __ JumpIfNotSmi(operand->reg(), deferred->entry_label());
6922 __ SmiShiftLogicalRightConstant(answer.reg(),
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00006923 operand->reg(),
6924 shift_value,
6925 deferred->entry_label());
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00006926 deferred->BindExit();
ager@chromium.org4af710e2009-09-15 12:20:11 +00006927 operand->Unuse();
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00006928 }
6929 break;
6930
ager@chromium.orgadd848f2009-08-13 12:44:13 +00006931 case Token::SHL:
6932 if (reversed) {
ager@chromium.orgac091b72010-05-05 07:34:42 +00006933 operand->ToRegister();
ager@chromium.orgac091b72010-05-05 07:34:42 +00006934
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00006935 // We need rcx to be available to hold operand, and to be spilled.
6936 // SmiShiftLeft implicitly modifies rcx.
6937 if (operand->reg().is(rcx)) {
6938 frame_->Spill(operand->reg());
6939 answer = allocator()->Allocate();
6940 } else {
6941 Result rcx_reg = allocator()->Allocate(rcx);
6942 // answer must not be rcx.
6943 answer = allocator()->Allocate();
6944 // rcx_reg goes out of scope.
6945 }
6946
ager@chromium.orgac091b72010-05-05 07:34:42 +00006947 DeferredInlineSmiOperationReversed* deferred =
6948 new DeferredInlineSmiOperationReversed(op,
6949 answer.reg(),
6950 smi_value,
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00006951 operand->reg(),
ager@chromium.orgac091b72010-05-05 07:34:42 +00006952 overwrite_mode);
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00006953 if (!operand->type_info().IsSmi()) {
6954 Condition is_smi = masm_->CheckSmi(operand->reg());
ager@chromium.orgac091b72010-05-05 07:34:42 +00006955 deferred->Branch(NegateCondition(is_smi));
6956 } else if (FLAG_debug_code) {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006957 __ AbortIfNotSmi(operand->reg());
ager@chromium.orgac091b72010-05-05 07:34:42 +00006958 }
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00006959
6960 __ Move(answer.reg(), smi_value);
6961 __ SmiShiftLeft(answer.reg(), answer.reg(), operand->reg());
6962 operand->Unuse();
ager@chromium.orgac091b72010-05-05 07:34:42 +00006963
6964 deferred->BindExit();
ager@chromium.orgadd848f2009-08-13 12:44:13 +00006965 } else {
6966 // Only the least significant 5 bits of the shift value are used.
6967 // In the slow case, this masking is done inside the runtime call.
6968 int shift_value = int_value & 0x1f;
6969 operand->ToRegister();
6970 if (shift_value == 0) {
6971 // Spill operand so it can be overwritten in the slow case.
6972 frame_->Spill(operand->reg());
6973 DeferredInlineSmiOperation* deferred =
6974 new DeferredInlineSmiOperation(op,
6975 operand->reg(),
6976 operand->reg(),
6977 smi_value,
6978 overwrite_mode);
ager@chromium.org4af710e2009-09-15 12:20:11 +00006979 __ JumpIfNotSmi(operand->reg(), deferred->entry_label());
ager@chromium.orgadd848f2009-08-13 12:44:13 +00006980 deferred->BindExit();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006981 answer = *operand;
ager@chromium.orgadd848f2009-08-13 12:44:13 +00006982 } else {
6983 // Use a fresh temporary for nonzero shift values.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006984 answer = allocator()->Allocate();
ager@chromium.orgadd848f2009-08-13 12:44:13 +00006985 ASSERT(answer.is_valid());
6986 DeferredInlineSmiOperation* deferred =
6987 new DeferredInlineSmiOperation(op,
6988 answer.reg(),
6989 operand->reg(),
6990 smi_value,
6991 overwrite_mode);
ager@chromium.org4af710e2009-09-15 12:20:11 +00006992 __ JumpIfNotSmi(operand->reg(), deferred->entry_label());
6993 __ SmiShiftLeftConstant(answer.reg(),
6994 operand->reg(),
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00006995 shift_value);
ager@chromium.orgadd848f2009-08-13 12:44:13 +00006996 deferred->BindExit();
6997 operand->Unuse();
ager@chromium.orgadd848f2009-08-13 12:44:13 +00006998 }
6999 }
7000 break;
7001
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00007002 case Token::BIT_OR:
7003 case Token::BIT_XOR:
7004 case Token::BIT_AND: {
7005 operand->ToRegister();
7006 frame_->Spill(operand->reg());
7007 if (reversed) {
7008 // Bit operations with a constant smi are commutative.
7009 // We can swap left and right operands with no problem.
7010 // Swap left and right overwrite modes. 0->0, 1->2, 2->1.
7011 overwrite_mode = static_cast<OverwriteMode>((2 * overwrite_mode) % 3);
7012 }
7013 DeferredCode* deferred = new DeferredInlineSmiOperation(op,
7014 operand->reg(),
7015 operand->reg(),
7016 smi_value,
7017 overwrite_mode);
ager@chromium.org4af710e2009-09-15 12:20:11 +00007018 __ JumpIfNotSmi(operand->reg(), deferred->entry_label());
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00007019 if (op == Token::BIT_AND) {
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00007020 __ SmiAndConstant(operand->reg(), operand->reg(), smi_value);
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00007021 } else if (op == Token::BIT_XOR) {
7022 if (int_value != 0) {
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00007023 __ SmiXorConstant(operand->reg(), operand->reg(), smi_value);
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00007024 }
7025 } else {
7026 ASSERT(op == Token::BIT_OR);
7027 if (int_value != 0) {
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00007028 __ SmiOrConstant(operand->reg(), operand->reg(), smi_value);
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00007029 }
7030 }
7031 deferred->BindExit();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007032 answer = *operand;
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00007033 break;
7034 }
7035
7036 // Generate inline code for mod of powers of 2 and negative powers of 2.
7037 case Token::MOD:
7038 if (!reversed &&
7039 int_value != 0 &&
7040 (IsPowerOf2(int_value) || IsPowerOf2(-int_value))) {
7041 operand->ToRegister();
7042 frame_->Spill(operand->reg());
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00007043 DeferredCode* deferred =
7044 new DeferredInlineSmiOperation(op,
7045 operand->reg(),
7046 operand->reg(),
7047 smi_value,
7048 overwrite_mode);
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00007049 // Check for negative or non-Smi left hand side.
ager@chromium.org4af710e2009-09-15 12:20:11 +00007050 __ JumpIfNotPositiveSmi(operand->reg(), deferred->entry_label());
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00007051 if (int_value < 0) int_value = -int_value;
7052 if (int_value == 1) {
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00007053 __ Move(operand->reg(), Smi::FromInt(0));
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00007054 } else {
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00007055 __ SmiAndConstant(operand->reg(),
7056 operand->reg(),
7057 Smi::FromInt(int_value - 1));
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00007058 }
7059 deferred->BindExit();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007060 answer = *operand;
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00007061 break; // This break only applies if we generated code for MOD.
7062 }
7063 // Fall through if we did not find a power of 2 on the right hand side!
7064 // The next case must be the default.
7065
ager@chromium.org3e875802009-06-29 08:26:34 +00007066 default: {
7067 Result constant_operand(value);
7068 if (reversed) {
ager@chromium.orgac091b72010-05-05 07:34:42 +00007069 answer = LikelySmiBinaryOperation(expr, &constant_operand, operand,
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007070 overwrite_mode);
ager@chromium.org3e875802009-06-29 08:26:34 +00007071 } else {
ager@chromium.orgac091b72010-05-05 07:34:42 +00007072 answer = LikelySmiBinaryOperation(expr, operand, &constant_operand,
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007073 overwrite_mode);
ager@chromium.org3e875802009-06-29 08:26:34 +00007074 }
7075 break;
7076 }
7077 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007078 ASSERT(answer.is_valid());
7079 return answer;
ager@chromium.org3e875802009-06-29 08:26:34 +00007080}
7081
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00007082
7083// Implements a binary operation using a deferred code object and some
7084// inline code to operate on smis quickly.
ager@chromium.orgac091b72010-05-05 07:34:42 +00007085Result CodeGenerator::LikelySmiBinaryOperation(BinaryOperation* expr,
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007086 Result* left,
7087 Result* right,
7088 OverwriteMode overwrite_mode) {
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00007089 // Copy the type info because left and right may be overwritten.
7090 TypeInfo left_type_info = left->type_info();
7091 TypeInfo right_type_info = right->type_info();
7092 USE(left_type_info);
7093 USE(right_type_info);
7094 // TODO(X64): Use type information in calculations.
ager@chromium.orgac091b72010-05-05 07:34:42 +00007095 Token::Value op = expr->op();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007096 Result answer;
ager@chromium.org3e875802009-06-29 08:26:34 +00007097 // Special handling of div and mod because they use fixed registers.
7098 if (op == Token::DIV || op == Token::MOD) {
7099 // We need rax as the quotient register, rdx as the remainder
7100 // register, neither left nor right in rax or rdx, and left copied
7101 // to rax.
7102 Result quotient;
7103 Result remainder;
7104 bool left_is_in_rax = false;
7105 // Step 1: get rax for quotient.
7106 if ((left->is_register() && left->reg().is(rax)) ||
7107 (right->is_register() && right->reg().is(rax))) {
7108 // One or both is in rax. Use a fresh non-rdx register for
7109 // them.
7110 Result fresh = allocator_->Allocate();
7111 ASSERT(fresh.is_valid());
7112 if (fresh.reg().is(rdx)) {
7113 remainder = fresh;
7114 fresh = allocator_->Allocate();
7115 ASSERT(fresh.is_valid());
7116 }
7117 if (left->is_register() && left->reg().is(rax)) {
7118 quotient = *left;
7119 *left = fresh;
7120 left_is_in_rax = true;
7121 }
7122 if (right->is_register() && right->reg().is(rax)) {
7123 quotient = *right;
7124 *right = fresh;
7125 }
7126 __ movq(fresh.reg(), rax);
7127 } else {
7128 // Neither left nor right is in rax.
7129 quotient = allocator_->Allocate(rax);
7130 }
7131 ASSERT(quotient.is_register() && quotient.reg().is(rax));
7132 ASSERT(!(left->is_register() && left->reg().is(rax)));
7133 ASSERT(!(right->is_register() && right->reg().is(rax)));
7134
7135 // Step 2: get rdx for remainder if necessary.
7136 if (!remainder.is_valid()) {
7137 if ((left->is_register() && left->reg().is(rdx)) ||
7138 (right->is_register() && right->reg().is(rdx))) {
7139 Result fresh = allocator_->Allocate();
7140 ASSERT(fresh.is_valid());
7141 if (left->is_register() && left->reg().is(rdx)) {
7142 remainder = *left;
7143 *left = fresh;
7144 }
7145 if (right->is_register() && right->reg().is(rdx)) {
7146 remainder = *right;
7147 *right = fresh;
7148 }
7149 __ movq(fresh.reg(), rdx);
7150 } else {
7151 // Neither left nor right is in rdx.
7152 remainder = allocator_->Allocate(rdx);
7153 }
7154 }
7155 ASSERT(remainder.is_register() && remainder.reg().is(rdx));
7156 ASSERT(!(left->is_register() && left->reg().is(rdx)));
7157 ASSERT(!(right->is_register() && right->reg().is(rdx)));
7158
7159 left->ToRegister();
7160 right->ToRegister();
7161 frame_->Spill(rax);
7162 frame_->Spill(rdx);
7163
7164 // Check that left and right are smi tagged.
7165 DeferredInlineBinaryOperation* deferred =
7166 new DeferredInlineBinaryOperation(op,
7167 (op == Token::DIV) ? rax : rdx,
7168 left->reg(),
7169 right->reg(),
7170 overwrite_mode);
ager@chromium.org4af710e2009-09-15 12:20:11 +00007171 __ JumpIfNotBothSmi(left->reg(), right->reg(), deferred->entry_label());
ager@chromium.org3e875802009-06-29 08:26:34 +00007172
ager@chromium.org3e875802009-06-29 08:26:34 +00007173 if (op == Token::DIV) {
ager@chromium.org4af710e2009-09-15 12:20:11 +00007174 __ SmiDiv(rax, left->reg(), right->reg(), deferred->entry_label());
ager@chromium.org3e875802009-06-29 08:26:34 +00007175 deferred->BindExit();
7176 left->Unuse();
7177 right->Unuse();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007178 answer = quotient;
ager@chromium.org3e875802009-06-29 08:26:34 +00007179 } else {
7180 ASSERT(op == Token::MOD);
ager@chromium.org4af710e2009-09-15 12:20:11 +00007181 __ SmiMod(rdx, left->reg(), right->reg(), deferred->entry_label());
ager@chromium.org3e875802009-06-29 08:26:34 +00007182 deferred->BindExit();
7183 left->Unuse();
7184 right->Unuse();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007185 answer = remainder;
ager@chromium.org3e875802009-06-29 08:26:34 +00007186 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007187 ASSERT(answer.is_valid());
7188 return answer;
ager@chromium.org3e875802009-06-29 08:26:34 +00007189 }
7190
7191 // Special handling of shift operations because they use fixed
7192 // registers.
7193 if (op == Token::SHL || op == Token::SHR || op == Token::SAR) {
7194 // Move left out of rcx if necessary.
7195 if (left->is_register() && left->reg().is(rcx)) {
7196 *left = allocator_->Allocate();
7197 ASSERT(left->is_valid());
7198 __ movq(left->reg(), rcx);
7199 }
7200 right->ToRegister(rcx);
7201 left->ToRegister();
7202 ASSERT(left->is_register() && !left->reg().is(rcx));
7203 ASSERT(right->is_register() && right->reg().is(rcx));
7204
7205 // We will modify right, it must be spilled.
7206 frame_->Spill(rcx);
7207
7208 // Use a fresh answer register to avoid spilling the left operand.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007209 answer = allocator_->Allocate();
ager@chromium.org3e875802009-06-29 08:26:34 +00007210 ASSERT(answer.is_valid());
7211 // Check that both operands are smis using the answer register as a
7212 // temporary.
7213 DeferredInlineBinaryOperation* deferred =
7214 new DeferredInlineBinaryOperation(op,
7215 answer.reg(),
7216 left->reg(),
7217 rcx,
7218 overwrite_mode);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00007219
7220 Label do_op;
7221 if (right_type_info.IsSmi()) {
7222 if (FLAG_debug_code) {
7223 __ AbortIfNotSmi(right->reg());
7224 }
7225 __ movq(answer.reg(), left->reg());
7226 // If left is not known to be a smi, check if it is.
7227 // If left is not known to be a number, and it isn't a smi, check if
7228 // it is a HeapNumber.
7229 if (!left_type_info.IsSmi()) {
7230 __ JumpIfSmi(answer.reg(), &do_op);
7231 if (!left_type_info.IsNumber()) {
7232 // Branch if not a heapnumber.
7233 __ Cmp(FieldOperand(answer.reg(), HeapObject::kMapOffset),
7234 Factory::heap_number_map());
7235 deferred->Branch(not_equal);
7236 }
7237 // Load integer value into answer register using truncation.
7238 __ cvttsd2si(answer.reg(),
7239 FieldOperand(answer.reg(), HeapNumber::kValueOffset));
7240 // Branch if we might have overflowed.
7241 // (False negative for Smi::kMinValue)
7242 __ cmpq(answer.reg(), Immediate(0x80000000));
7243 deferred->Branch(equal);
7244 // TODO(lrn): Inline shifts on int32 here instead of first smi-tagging.
7245 __ Integer32ToSmi(answer.reg(), answer.reg());
7246 } else {
7247 // Fast case - both are actually smis.
7248 if (FLAG_debug_code) {
7249 __ AbortIfNotSmi(left->reg());
7250 }
7251 }
7252 } else {
7253 __ JumpIfNotBothSmi(left->reg(), rcx, deferred->entry_label());
7254 }
7255 __ bind(&do_op);
ager@chromium.org3e875802009-06-29 08:26:34 +00007256
ager@chromium.org3e875802009-06-29 08:26:34 +00007257 // Perform the operation.
7258 switch (op) {
7259 case Token::SAR:
ager@chromium.org4af710e2009-09-15 12:20:11 +00007260 __ SmiShiftArithmeticRight(answer.reg(), left->reg(), rcx);
ager@chromium.org3e875802009-06-29 08:26:34 +00007261 break;
7262 case Token::SHR: {
ager@chromium.org4af710e2009-09-15 12:20:11 +00007263 __ SmiShiftLogicalRight(answer.reg(),
7264 left->reg(),
7265 rcx,
7266 deferred->entry_label());
ager@chromium.org3e875802009-06-29 08:26:34 +00007267 break;
7268 }
7269 case Token::SHL: {
ager@chromium.org4af710e2009-09-15 12:20:11 +00007270 __ SmiShiftLeft(answer.reg(),
7271 left->reg(),
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00007272 rcx);
ager@chromium.org3e875802009-06-29 08:26:34 +00007273 break;
7274 }
7275 default:
7276 UNREACHABLE();
7277 }
ager@chromium.org3e875802009-06-29 08:26:34 +00007278 deferred->BindExit();
7279 left->Unuse();
7280 right->Unuse();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007281 ASSERT(answer.is_valid());
7282 return answer;
ager@chromium.org3e875802009-06-29 08:26:34 +00007283 }
7284
7285 // Handle the other binary operations.
7286 left->ToRegister();
7287 right->ToRegister();
7288 // A newly allocated register answer is used to hold the answer. The
7289 // registers containing left and right are not modified so they don't
7290 // need to be spilled in the fast case.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007291 answer = allocator_->Allocate();
ager@chromium.org3e875802009-06-29 08:26:34 +00007292 ASSERT(answer.is_valid());
7293
7294 // Perform the smi tag check.
7295 DeferredInlineBinaryOperation* deferred =
7296 new DeferredInlineBinaryOperation(op,
7297 answer.reg(),
7298 left->reg(),
7299 right->reg(),
7300 overwrite_mode);
ager@chromium.org4af710e2009-09-15 12:20:11 +00007301 __ JumpIfNotBothSmi(left->reg(), right->reg(), deferred->entry_label());
7302
ager@chromium.org3e875802009-06-29 08:26:34 +00007303 switch (op) {
7304 case Token::ADD:
ager@chromium.org4af710e2009-09-15 12:20:11 +00007305 __ SmiAdd(answer.reg(),
7306 left->reg(),
7307 right->reg(),
7308 deferred->entry_label());
ager@chromium.org3e875802009-06-29 08:26:34 +00007309 break;
7310
7311 case Token::SUB:
ager@chromium.org4af710e2009-09-15 12:20:11 +00007312 __ SmiSub(answer.reg(),
7313 left->reg(),
7314 right->reg(),
7315 deferred->entry_label());
ager@chromium.org3e875802009-06-29 08:26:34 +00007316 break;
7317
7318 case Token::MUL: {
ager@chromium.org4af710e2009-09-15 12:20:11 +00007319 __ SmiMul(answer.reg(),
7320 left->reg(),
7321 right->reg(),
7322 deferred->entry_label());
ager@chromium.org3e875802009-06-29 08:26:34 +00007323 break;
7324 }
7325
7326 case Token::BIT_OR:
ager@chromium.org4af710e2009-09-15 12:20:11 +00007327 __ SmiOr(answer.reg(), left->reg(), right->reg());
ager@chromium.org3e875802009-06-29 08:26:34 +00007328 break;
7329
7330 case Token::BIT_AND:
ager@chromium.org4af710e2009-09-15 12:20:11 +00007331 __ SmiAnd(answer.reg(), left->reg(), right->reg());
ager@chromium.org3e875802009-06-29 08:26:34 +00007332 break;
7333
7334 case Token::BIT_XOR:
ager@chromium.org4af710e2009-09-15 12:20:11 +00007335 __ SmiXor(answer.reg(), left->reg(), right->reg());
ager@chromium.org3e875802009-06-29 08:26:34 +00007336 break;
7337
7338 default:
7339 UNREACHABLE();
7340 break;
7341 }
7342 deferred->BindExit();
7343 left->Unuse();
7344 right->Unuse();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007345 ASSERT(answer.is_valid());
7346 return answer;
7347}
7348
7349
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00007350Result CodeGenerator::EmitNamedLoad(Handle<String> name, bool is_contextual) {
7351#ifdef DEBUG
7352 int original_height = frame()->height();
7353#endif
7354 Result result;
7355 // Do not inline the inobject property case for loads from the global
7356 // object. Also do not inline for unoptimized code. This saves time
7357 // in the code generator. Unoptimized code is toplevel code or code
7358 // that is not in a loop.
7359 if (is_contextual || scope()->is_global_scope() || loop_nesting() == 0) {
7360 Comment cmnt(masm(), "[ Load from named Property");
7361 frame()->Push(name);
7362
7363 RelocInfo::Mode mode = is_contextual
7364 ? RelocInfo::CODE_TARGET_CONTEXT
7365 : RelocInfo::CODE_TARGET;
7366 result = frame()->CallLoadIC(mode);
7367 // A test rax instruction following the call signals that the
7368 // inobject property case was inlined. Ensure that there is not
7369 // a test rax instruction here.
7370 __ nop();
7371 } else {
7372 // Inline the inobject property case.
7373 Comment cmnt(masm(), "[ Inlined named property load");
7374 Result receiver = frame()->Pop();
7375 receiver.ToRegister();
7376 result = allocator()->Allocate();
7377 ASSERT(result.is_valid());
7378
7379 // Cannot use r12 for receiver, because that changes
7380 // the distance between a call and a fixup location,
7381 // due to a special encoding of r12 as r/m in a ModR/M byte.
7382 if (receiver.reg().is(r12)) {
7383 frame()->Spill(receiver.reg()); // It will be overwritten with result.
7384 // Swap receiver and value.
7385 __ movq(result.reg(), receiver.reg());
7386 Result temp = receiver;
7387 receiver = result;
7388 result = temp;
7389 }
7390
7391 DeferredReferenceGetNamedValue* deferred =
7392 new DeferredReferenceGetNamedValue(result.reg(), receiver.reg(), name);
7393
7394 // Check that the receiver is a heap object.
7395 __ JumpIfSmi(receiver.reg(), deferred->entry_label());
7396
7397 __ bind(deferred->patch_site());
7398 // This is the map check instruction that will be patched (so we can't
7399 // use the double underscore macro that may insert instructions).
7400 // Initially use an invalid map to force a failure.
7401 masm()->Move(kScratchRegister, Factory::null_value());
7402 masm()->cmpq(FieldOperand(receiver.reg(), HeapObject::kMapOffset),
7403 kScratchRegister);
7404 // This branch is always a forwards branch so it's always a fixed
7405 // size which allows the assert below to succeed and patching to work.
7406 // Don't use deferred->Branch(...), since that might add coverage code.
7407 masm()->j(not_equal, deferred->entry_label());
7408
7409 // The delta from the patch label to the load offset must be
7410 // statically known.
7411 ASSERT(masm()->SizeOfCodeGeneratedSince(deferred->patch_site()) ==
7412 LoadIC::kOffsetToLoadInstruction);
7413 // The initial (invalid) offset has to be large enough to force
7414 // a 32-bit instruction encoding to allow patching with an
7415 // arbitrary offset. Use kMaxInt (minus kHeapObjectTag).
7416 int offset = kMaxInt;
7417 masm()->movq(result.reg(), FieldOperand(receiver.reg(), offset));
7418
7419 __ IncrementCounter(&Counters::named_load_inline, 1);
7420 deferred->BindExit();
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00007421 }
sgjesse@chromium.org82dbbab2010-06-02 08:57:44 +00007422 ASSERT(frame()->height() == original_height - 1);
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00007423 return result;
7424}
7425
7426
7427Result CodeGenerator::EmitKeyedLoad() {
7428#ifdef DEBUG
7429 int original_height = frame()->height();
7430#endif
7431 Result result;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007432 // Inline array load code if inside of a loop. We do not know
7433 // the receiver map yet, so we initially generate the code with
7434 // a check against an invalid map. In the inline cache code, we
7435 // patch the map check if appropriate.
7436 if (loop_nesting() > 0) {
7437 Comment cmnt(masm_, "[ Inlined load from keyed Property");
7438
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00007439 // Use a fresh temporary to load the elements without destroying
7440 // the receiver which is needed for the deferred slow case.
7441 // Allocate the temporary early so that we use rax if it is free.
7442 Result elements = allocator()->Allocate();
7443 ASSERT(elements.is_valid());
7444
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007445 Result key = frame_->Pop();
7446 Result receiver = frame_->Pop();
7447 key.ToRegister();
7448 receiver.ToRegister();
7449
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007450 DeferredReferenceGetKeyedValue* deferred =
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00007451 new DeferredReferenceGetKeyedValue(elements.reg(),
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007452 receiver.reg(),
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00007453 key.reg());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007454
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00007455 __ JumpIfSmi(receiver.reg(), deferred->entry_label());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007456
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00007457 // Check that the receiver has the expected map.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007458 // Initially, use an invalid map. The map is patched in the IC
7459 // initialization code.
7460 __ bind(deferred->patch_site());
7461 // Use masm-> here instead of the double underscore macro since extra
7462 // coverage code can interfere with the patching. Do not use
7463 // root array to load null_value, since it must be patched with
7464 // the expected receiver map.
7465 masm_->movq(kScratchRegister, Factory::null_value(),
7466 RelocInfo::EMBEDDED_OBJECT);
7467 masm_->cmpq(FieldOperand(receiver.reg(), HeapObject::kMapOffset),
7468 kScratchRegister);
7469 deferred->Branch(not_equal);
7470
7471 // Check that the key is a non-negative smi.
7472 __ JumpIfNotPositiveSmi(key.reg(), deferred->entry_label());
7473
7474 // Get the elements array from the receiver and check that it
7475 // is not a dictionary.
7476 __ movq(elements.reg(),
7477 FieldOperand(receiver.reg(), JSObject::kElementsOffset));
7478 __ Cmp(FieldOperand(elements.reg(), HeapObject::kMapOffset),
7479 Factory::fixed_array_map());
7480 deferred->Branch(not_equal);
7481
ricow@chromium.org30ce4112010-05-31 10:38:25 +00007482 // Check that key is within bounds.
7483 __ SmiCompare(key.reg(),
7484 FieldOperand(elements.reg(), FixedArray::kLengthOffset));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007485 deferred->Branch(above_equal);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00007486
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007487 // Load and check that the result is not the hole. We could
7488 // reuse the index or elements register for the value.
7489 //
7490 // TODO(206): Consider whether it makes sense to try some
7491 // heuristic about which register to reuse. For example, if
7492 // one is rax, the we can reuse that one because the value
7493 // coming from the deferred code will be in rax.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00007494 SmiIndex index =
7495 masm_->SmiToIndex(kScratchRegister, key.reg(), kPointerSizeLog2);
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00007496 __ movq(elements.reg(),
ricow@chromium.org30ce4112010-05-31 10:38:25 +00007497 FieldOperand(elements.reg(),
7498 index.reg,
7499 index.scale,
7500 FixedArray::kHeaderSize));
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00007501 result = elements;
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00007502 __ CompareRoot(result.reg(), Heap::kTheHoleValueRootIndex);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007503 deferred->Branch(equal);
7504 __ IncrementCounter(&Counters::keyed_load_inline, 1);
7505
7506 deferred->BindExit();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007507 frame_->Push(&receiver);
7508 frame_->Push(&key);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007509 } else {
7510 Comment cmnt(masm_, "[ Load from keyed Property");
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00007511 result = frame_->CallKeyedLoadIC(RelocInfo::CODE_TARGET);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007512 // Make sure that we do not have a test instruction after the
7513 // call. A test instruction after the call is used to
7514 // indicate that we have generated an inline version of the
7515 // keyed load. The explicit nop instruction is here because
7516 // the push that follows might be peep-hole optimized away.
7517 __ nop();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007518 }
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00007519 ASSERT(frame()->height() == original_height);
7520 return result;
ager@chromium.org3e875802009-06-29 08:26:34 +00007521}
7522
7523
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007524#undef __
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007525#define __ ACCESS_MASM(masm)
7526
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007527
7528Handle<String> Reference::GetName() {
7529 ASSERT(type_ == NAMED);
7530 Property* property = expression_->AsProperty();
7531 if (property == NULL) {
7532 // Global variable reference treated as a named property reference.
7533 VariableProxy* proxy = expression_->AsVariableProxy();
7534 ASSERT(proxy->AsVariable() != NULL);
7535 ASSERT(proxy->AsVariable()->is_global());
7536 return proxy->name();
7537 } else {
7538 Literal* raw_name = property->key()->AsLiteral();
7539 ASSERT(raw_name != NULL);
7540 return Handle<String>(String::cast(*raw_name->handle()));
7541 }
7542}
7543
7544
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007545void Reference::GetValue() {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007546 ASSERT(!cgen_->in_spilled_code());
7547 ASSERT(cgen_->HasValidEntryRegisters());
7548 ASSERT(!is_illegal());
7549 MacroAssembler* masm = cgen_->masm();
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00007550
7551 // Record the source position for the property load.
7552 Property* property = expression_->AsProperty();
7553 if (property != NULL) {
7554 cgen_->CodeForSourcePosition(property->position());
7555 }
7556
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007557 switch (type_) {
7558 case SLOT: {
7559 Comment cmnt(masm, "[ Load from Slot");
7560 Slot* slot = expression_->AsVariableProxy()->AsVariable()->slot();
7561 ASSERT(slot != NULL);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007562 cgen_->LoadFromSlotCheckForArguments(slot, NOT_INSIDE_TYPEOF);
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00007563 if (!persist_after_get_) set_unloaded();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007564 break;
7565 }
7566
7567 case NAMED: {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007568 Variable* var = expression_->AsVariableProxy()->AsVariable();
7569 bool is_global = var != NULL;
7570 ASSERT(!is_global || var->is_global());
sgjesse@chromium.org82dbbab2010-06-02 08:57:44 +00007571 if (persist_after_get_) {
7572 cgen_->frame()->Dup();
7573 }
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00007574 Result result = cgen_->EmitNamedLoad(GetName(), is_global);
7575 cgen_->frame()->Push(&result);
7576 if (!persist_after_get_) {
sgjesse@chromium.org82dbbab2010-06-02 08:57:44 +00007577 set_unloaded();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007578 }
7579 break;
7580 }
7581
7582 case KEYED: {
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00007583 // A load of a bare identifier (load from global) cannot be keyed.
7584 ASSERT(expression_->AsVariableProxy()->AsVariable() == NULL);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007585
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00007586 Result value = cgen_->EmitKeyedLoad();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007587 cgen_->frame()->Push(&value);
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00007588 if (!persist_after_get_) {
7589 cgen_->UnloadReference(this);
7590 }
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007591 break;
7592 }
7593
7594 default:
7595 UNREACHABLE();
7596 }
7597}
7598
7599
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007600void Reference::TakeValue() {
ager@chromium.org3e875802009-06-29 08:26:34 +00007601 // TODO(X64): This function is completely architecture independent. Move
7602 // it somewhere shared.
7603
7604 // For non-constant frame-allocated slots, we invalidate the value in the
7605 // slot. For all others, we fall back on GetValue.
7606 ASSERT(!cgen_->in_spilled_code());
7607 ASSERT(!is_illegal());
7608 if (type_ != SLOT) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007609 GetValue();
ager@chromium.org3e875802009-06-29 08:26:34 +00007610 return;
7611 }
7612
7613 Slot* slot = expression_->AsVariableProxy()->AsVariable()->slot();
7614 ASSERT(slot != NULL);
7615 if (slot->type() == Slot::LOOKUP ||
7616 slot->type() == Slot::CONTEXT ||
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +00007617 slot->var()->mode() == Variable::CONST ||
7618 slot->is_arguments()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007619 GetValue();
ager@chromium.org3e875802009-06-29 08:26:34 +00007620 return;
7621 }
7622
7623 // Only non-constant, frame-allocated parameters and locals can reach
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +00007624 // here. Be careful not to use the optimizations for arguments
7625 // object access since it may not have been initialized yet.
7626 ASSERT(!slot->is_arguments());
ager@chromium.org3e875802009-06-29 08:26:34 +00007627 if (slot->type() == Slot::PARAMETER) {
7628 cgen_->frame()->TakeParameterAt(slot->index());
7629 } else {
7630 ASSERT(slot->type() == Slot::LOCAL);
7631 cgen_->frame()->TakeLocalAt(slot->index());
7632 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007633
7634 ASSERT(persist_after_get_);
7635 // Do not unload the reference, because it is used in SetValue.
ager@chromium.org3e875802009-06-29 08:26:34 +00007636}
7637
7638
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007639void Reference::SetValue(InitState init_state) {
7640 ASSERT(cgen_->HasValidEntryRegisters());
7641 ASSERT(!is_illegal());
7642 MacroAssembler* masm = cgen_->masm();
7643 switch (type_) {
7644 case SLOT: {
7645 Comment cmnt(masm, "[ Store to Slot");
7646 Slot* slot = expression_->AsVariableProxy()->AsVariable()->slot();
7647 ASSERT(slot != NULL);
7648 cgen_->StoreToSlot(slot, init_state);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007649 cgen_->UnloadReference(this);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007650 break;
7651 }
7652
7653 case NAMED: {
7654 Comment cmnt(masm, "[ Store to named Property");
7655 cgen_->frame()->Push(GetName());
7656 Result answer = cgen_->frame()->CallStoreIC();
7657 cgen_->frame()->Push(&answer);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007658 set_unloaded();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007659 break;
7660 }
7661
7662 case KEYED: {
7663 Comment cmnt(masm, "[ Store to keyed Property");
7664
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007665 // Generate inlined version of the keyed store if the code is in
7666 // a loop and the key is likely to be a smi.
7667 Property* property = expression()->AsProperty();
7668 ASSERT(property != NULL);
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00007669 StaticType* key_smi_analysis = property->key()->type();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007670
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007671 if (cgen_->loop_nesting() > 0 && key_smi_analysis->IsLikelySmi()) {
7672 Comment cmnt(masm, "[ Inlined store to keyed Property");
7673
7674 // Get the receiver, key and value into registers.
7675 Result value = cgen_->frame()->Pop();
7676 Result key = cgen_->frame()->Pop();
7677 Result receiver = cgen_->frame()->Pop();
7678
7679 Result tmp = cgen_->allocator_->Allocate();
7680 ASSERT(tmp.is_valid());
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00007681 Result tmp2 = cgen_->allocator_->Allocate();
7682 ASSERT(tmp2.is_valid());
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007683
7684 // Determine whether the value is a constant before putting it
7685 // in a register.
7686 bool value_is_constant = value.is_constant();
7687
7688 // Make sure that value, key and receiver are in registers.
7689 value.ToRegister();
7690 key.ToRegister();
7691 receiver.ToRegister();
7692
7693 DeferredReferenceSetKeyedValue* deferred =
7694 new DeferredReferenceSetKeyedValue(value.reg(),
7695 key.reg(),
7696 receiver.reg());
7697
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007698 // Check that the receiver is not a smi.
ager@chromium.org4af710e2009-09-15 12:20:11 +00007699 __ JumpIfSmi(receiver.reg(), deferred->entry_label());
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007700
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00007701 // Check that the key is a smi.
7702 if (!key.is_smi()) {
7703 __ JumpIfNotSmi(key.reg(), deferred->entry_label());
ager@chromium.orgac091b72010-05-05 07:34:42 +00007704 } else if (FLAG_debug_code) {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00007705 __ AbortIfNotSmi(key.reg());
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00007706 }
7707
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007708 // Check that the receiver is a JSArray.
7709 __ CmpObjectType(receiver.reg(), JS_ARRAY_TYPE, kScratchRegister);
7710 deferred->Branch(not_equal);
7711
7712 // Check that the key is within bounds. Both the key and the
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00007713 // length of the JSArray are smis. Use unsigned comparison to handle
7714 // negative keys.
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00007715 __ SmiCompare(FieldOperand(receiver.reg(), JSArray::kLengthOffset),
7716 key.reg());
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00007717 deferred->Branch(below_equal);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007718
7719 // Get the elements array from the receiver and check that it
7720 // is a flat array (not a dictionary).
7721 __ movq(tmp.reg(),
7722 FieldOperand(receiver.reg(), JSObject::kElementsOffset));
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00007723
7724 // Check whether it is possible to omit the write barrier. If the
7725 // elements array is in new space or the value written is a smi we can
ricow@chromium.org30ce4112010-05-31 10:38:25 +00007726 // safely update the elements array without write barrier.
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00007727 Label in_new_space;
7728 __ InNewSpace(tmp.reg(), tmp2.reg(), equal, &in_new_space);
7729 if (!value_is_constant) {
7730 __ JumpIfNotSmi(value.reg(), deferred->entry_label());
7731 }
7732
7733 __ bind(&in_new_space);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007734 // Bind the deferred code patch site to be able to locate the
7735 // fixed array map comparison. When debugging, we patch this
7736 // comparison to always fail so that we will hit the IC call
7737 // in the deferred code which will allow the debugger to
7738 // break for fast case stores.
7739 __ bind(deferred->patch_site());
7740 // Avoid using __ to ensure the distance from patch_site
7741 // to the map address is always the same.
7742 masm->movq(kScratchRegister, Factory::fixed_array_map(),
7743 RelocInfo::EMBEDDED_OBJECT);
7744 __ cmpq(FieldOperand(tmp.reg(), HeapObject::kMapOffset),
7745 kScratchRegister);
7746 deferred->Branch(not_equal);
7747
7748 // Store the value.
ager@chromium.org4af710e2009-09-15 12:20:11 +00007749 SmiIndex index =
7750 masm->SmiToIndex(kScratchRegister, key.reg(), kPointerSizeLog2);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00007751 __ movq(FieldOperand(tmp.reg(),
7752 index.reg,
7753 index.scale,
7754 FixedArray::kHeaderSize),
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007755 value.reg());
7756 __ IncrementCounter(&Counters::keyed_store_inline, 1);
7757
7758 deferred->BindExit();
7759
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007760 cgen_->frame()->Push(&value);
7761 } else {
7762 Result answer = cgen_->frame()->CallKeyedStoreIC();
7763 // Make sure that we do not have a test instruction after the
7764 // call. A test instruction after the call is used to
7765 // indicate that we have generated an inline version of the
7766 // keyed store.
7767 masm->nop();
7768 cgen_->frame()->Push(&answer);
7769 }
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00007770 set_unloaded();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007771 break;
7772 }
7773
7774 default:
7775 UNREACHABLE();
7776 }
7777}
7778
7779
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007780void FastNewClosureStub::Generate(MacroAssembler* masm) {
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007781 // Create a new closure from the given function info in new
7782 // space. Set the context to the current context in rsi.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007783 Label gc;
7784 __ AllocateInNewSpace(JSFunction::kSize, rax, rbx, rcx, &gc, TAG_OBJECT);
7785
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007786 // Get the function info from the stack.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007787 __ movq(rdx, Operand(rsp, 1 * kPointerSize));
7788
7789 // Compute the function map in the current global context and set that
7790 // as the map of the allocated object.
7791 __ movq(rcx, Operand(rsi, Context::SlotOffset(Context::GLOBAL_INDEX)));
7792 __ movq(rcx, FieldOperand(rcx, GlobalObject::kGlobalContextOffset));
7793 __ movq(rcx, Operand(rcx, Context::SlotOffset(Context::FUNCTION_MAP_INDEX)));
7794 __ movq(FieldOperand(rax, JSObject::kMapOffset), rcx);
7795
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007796 // Initialize the rest of the function. We don't have to update the
7797 // write barrier because the allocated object is in new space.
7798 __ LoadRoot(rbx, Heap::kEmptyFixedArrayRootIndex);
7799 __ LoadRoot(rcx, Heap::kTheHoleValueRootIndex);
7800 __ movq(FieldOperand(rax, JSObject::kPropertiesOffset), rbx);
7801 __ movq(FieldOperand(rax, JSObject::kElementsOffset), rbx);
7802 __ movq(FieldOperand(rax, JSFunction::kPrototypeOrInitialMapOffset), rcx);
7803 __ movq(FieldOperand(rax, JSFunction::kSharedFunctionInfoOffset), rdx);
7804 __ movq(FieldOperand(rax, JSFunction::kContextOffset), rsi);
7805 __ movq(FieldOperand(rax, JSFunction::kLiteralsOffset), rbx);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007806
7807 // Return and remove the on-stack parameter.
7808 __ ret(1 * kPointerSize);
7809
7810 // Create a new closure through the slower runtime call.
7811 __ bind(&gc);
7812 __ pop(rcx); // Temporarily remove return address.
7813 __ pop(rdx);
7814 __ push(rsi);
7815 __ push(rdx);
7816 __ push(rcx); // Restore return address.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00007817 __ TailCallRuntime(Runtime::kNewClosure, 2, 1);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007818}
7819
7820
7821void FastNewContextStub::Generate(MacroAssembler* masm) {
7822 // Try to allocate the context in new space.
7823 Label gc;
7824 int length = slots_ + Context::MIN_CONTEXT_SLOTS;
7825 __ AllocateInNewSpace((length * kPointerSize) + FixedArray::kHeaderSize,
7826 rax, rbx, rcx, &gc, TAG_OBJECT);
7827
7828 // Get the function from the stack.
7829 __ movq(rcx, Operand(rsp, 1 * kPointerSize));
7830
7831 // Setup the object header.
7832 __ LoadRoot(kScratchRegister, Heap::kContextMapRootIndex);
7833 __ movq(FieldOperand(rax, HeapObject::kMapOffset), kScratchRegister);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00007834 __ Move(FieldOperand(rax, FixedArray::kLengthOffset), Smi::FromInt(length));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007835
7836 // Setup the fixed slots.
7837 __ xor_(rbx, rbx); // Set to NULL.
7838 __ movq(Operand(rax, Context::SlotOffset(Context::CLOSURE_INDEX)), rcx);
7839 __ movq(Operand(rax, Context::SlotOffset(Context::FCONTEXT_INDEX)), rax);
7840 __ movq(Operand(rax, Context::SlotOffset(Context::PREVIOUS_INDEX)), rbx);
7841 __ movq(Operand(rax, Context::SlotOffset(Context::EXTENSION_INDEX)), rbx);
7842
7843 // Copy the global object from the surrounding context.
7844 __ movq(rbx, Operand(rsi, Context::SlotOffset(Context::GLOBAL_INDEX)));
7845 __ movq(Operand(rax, Context::SlotOffset(Context::GLOBAL_INDEX)), rbx);
7846
7847 // Initialize the rest of the slots to undefined.
7848 __ LoadRoot(rbx, Heap::kUndefinedValueRootIndex);
7849 for (int i = Context::MIN_CONTEXT_SLOTS; i < length; i++) {
7850 __ movq(Operand(rax, Context::SlotOffset(i)), rbx);
7851 }
7852
7853 // Return and remove the on-stack parameter.
7854 __ movq(rsi, rax);
7855 __ ret(1 * kPointerSize);
7856
7857 // Need to collect. Call into runtime system.
7858 __ bind(&gc);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00007859 __ TailCallRuntime(Runtime::kNewContext, 1, 1);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007860}
7861
7862
ager@chromium.org5c838252010-02-19 08:53:10 +00007863void FastCloneShallowArrayStub::Generate(MacroAssembler* masm) {
7864 // Stack layout on entry:
7865 //
7866 // [rsp + kPointerSize]: constant elements.
7867 // [rsp + (2 * kPointerSize)]: literal index.
7868 // [rsp + (3 * kPointerSize)]: literals array.
7869
7870 // All sizes here are multiples of kPointerSize.
7871 int elements_size = (length_ > 0) ? FixedArray::SizeFor(length_) : 0;
7872 int size = JSArray::kSize + elements_size;
7873
7874 // Load boilerplate object into rcx and check if we need to create a
7875 // boilerplate.
7876 Label slow_case;
7877 __ movq(rcx, Operand(rsp, 3 * kPointerSize));
7878 __ movq(rax, Operand(rsp, 2 * kPointerSize));
7879 SmiIndex index = masm->SmiToIndex(rax, rax, kPointerSizeLog2);
7880 __ movq(rcx,
7881 FieldOperand(rcx, index.reg, index.scale, FixedArray::kHeaderSize));
7882 __ CompareRoot(rcx, Heap::kUndefinedValueRootIndex);
7883 __ j(equal, &slow_case);
7884
7885 // Allocate both the JS array and the elements array in one big
7886 // allocation. This avoids multiple limit checks.
7887 __ AllocateInNewSpace(size, rax, rbx, rdx, &slow_case, TAG_OBJECT);
7888
7889 // Copy the JS array part.
7890 for (int i = 0; i < JSArray::kSize; i += kPointerSize) {
7891 if ((i != JSArray::kElementsOffset) || (length_ == 0)) {
7892 __ movq(rbx, FieldOperand(rcx, i));
7893 __ movq(FieldOperand(rax, i), rbx);
7894 }
7895 }
7896
7897 if (length_ > 0) {
7898 // Get hold of the elements array of the boilerplate and setup the
7899 // elements pointer in the resulting object.
7900 __ movq(rcx, FieldOperand(rcx, JSArray::kElementsOffset));
7901 __ lea(rdx, Operand(rax, JSArray::kSize));
7902 __ movq(FieldOperand(rax, JSArray::kElementsOffset), rdx);
7903
7904 // Copy the elements array.
7905 for (int i = 0; i < elements_size; i += kPointerSize) {
7906 __ movq(rbx, FieldOperand(rcx, i));
7907 __ movq(FieldOperand(rdx, i), rbx);
7908 }
7909 }
7910
7911 // Return and remove the on-stack parameters.
7912 __ ret(3 * kPointerSize);
7913
7914 __ bind(&slow_case);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00007915 __ TailCallRuntime(Runtime::kCreateArrayLiteralShallow, 3, 1);
ager@chromium.org5c838252010-02-19 08:53:10 +00007916}
7917
7918
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007919void ToBooleanStub::Generate(MacroAssembler* masm) {
7920 Label false_result, true_result, not_string;
7921 __ movq(rax, Operand(rsp, 1 * kPointerSize));
7922
7923 // 'null' => false.
ager@chromium.org18ad94b2009-09-02 08:22:29 +00007924 __ CompareRoot(rax, Heap::kNullValueRootIndex);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007925 __ j(equal, &false_result);
7926
7927 // Get the map and type of the heap object.
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00007928 // We don't use CmpObjectType because we manipulate the type field.
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007929 __ movq(rdx, FieldOperand(rax, HeapObject::kMapOffset));
7930 __ movzxbq(rcx, FieldOperand(rdx, Map::kInstanceTypeOffset));
7931
7932 // Undetectable => false.
7933 __ movzxbq(rbx, FieldOperand(rdx, Map::kBitFieldOffset));
7934 __ and_(rbx, Immediate(1 << Map::kIsUndetectable));
7935 __ j(not_zero, &false_result);
7936
7937 // JavaScript object => true.
7938 __ cmpq(rcx, Immediate(FIRST_JS_OBJECT_TYPE));
7939 __ j(above_equal, &true_result);
7940
7941 // String value => false iff empty.
7942 __ cmpq(rcx, Immediate(FIRST_NONSTRING_TYPE));
7943 __ j(above_equal, &not_string);
ager@chromium.orgac091b72010-05-05 07:34:42 +00007944 __ movq(rdx, FieldOperand(rax, String::kLengthOffset));
7945 __ SmiTest(rdx);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007946 __ j(zero, &false_result);
7947 __ jmp(&true_result);
7948
7949 __ bind(&not_string);
7950 // HeapNumber => false iff +0, -0, or NaN.
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00007951 // These three cases set C3 when compared to zero in the FPU.
ager@chromium.org18ad94b2009-09-02 08:22:29 +00007952 __ CompareRoot(rdx, Heap::kHeapNumberMapRootIndex);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007953 __ j(not_equal, &true_result);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007954 __ fldz(); // Load zero onto fp stack
7955 // Load heap-number double value onto fp stack
7956 __ fld_d(FieldOperand(rax, HeapNumber::kValueOffset));
ager@chromium.org3811b432009-10-28 14:53:37 +00007957 __ FCmp();
7958 __ j(zero, &false_result);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007959 // Fall through to |true_result|.
7960
7961 // Return 1/0 for true/false in rax.
7962 __ bind(&true_result);
7963 __ movq(rax, Immediate(1));
7964 __ ret(1 * kPointerSize);
7965 __ bind(&false_result);
7966 __ xor_(rax, rax);
7967 __ ret(1 * kPointerSize);
7968}
7969
7970
ager@chromium.org3e875802009-06-29 08:26:34 +00007971bool CodeGenerator::FoldConstantSmis(Token::Value op, int left, int right) {
ager@chromium.org3e875802009-06-29 08:26:34 +00007972 Object* answer_object = Heap::undefined_value();
7973 switch (op) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007974 case Token::ADD:
kasperl@chromium.org74e4e5e2010-01-25 10:15:52 +00007975 // Use intptr_t to detect overflow of 32-bit int.
7976 if (Smi::IsValid(static_cast<intptr_t>(left) + right)) {
ager@chromium.org3e875802009-06-29 08:26:34 +00007977 answer_object = Smi::FromInt(left + right);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007978 }
ager@chromium.org3e875802009-06-29 08:26:34 +00007979 break;
7980 case Token::SUB:
kasperl@chromium.org74e4e5e2010-01-25 10:15:52 +00007981 // Use intptr_t to detect overflow of 32-bit int.
7982 if (Smi::IsValid(static_cast<intptr_t>(left) - right)) {
ager@chromium.org3e875802009-06-29 08:26:34 +00007983 answer_object = Smi::FromInt(left - right);
7984 }
7985 break;
7986 case Token::MUL: {
7987 double answer = static_cast<double>(left) * right;
7988 if (answer >= Smi::kMinValue && answer <= Smi::kMaxValue) {
7989 // If the product is zero and the non-zero factor is negative,
7990 // the spec requires us to return floating point negative zero.
ager@chromium.org18ad94b2009-09-02 08:22:29 +00007991 if (answer != 0 || (left + right) >= 0) {
ager@chromium.org3e875802009-06-29 08:26:34 +00007992 answer_object = Smi::FromInt(static_cast<int>(answer));
7993 }
7994 }
7995 }
7996 break;
7997 case Token::DIV:
7998 case Token::MOD:
7999 break;
8000 case Token::BIT_OR:
8001 answer_object = Smi::FromInt(left | right);
8002 break;
8003 case Token::BIT_AND:
8004 answer_object = Smi::FromInt(left & right);
8005 break;
8006 case Token::BIT_XOR:
8007 answer_object = Smi::FromInt(left ^ right);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00008008 break;
8009
ager@chromium.org3e875802009-06-29 08:26:34 +00008010 case Token::SHL: {
8011 int shift_amount = right & 0x1F;
8012 if (Smi::IsValid(left << shift_amount)) {
8013 answer_object = Smi::FromInt(left << shift_amount);
8014 }
8015 break;
8016 }
8017 case Token::SHR: {
8018 int shift_amount = right & 0x1F;
8019 unsigned int unsigned_left = left;
8020 unsigned_left >>= shift_amount;
8021 if (unsigned_left <= static_cast<unsigned int>(Smi::kMaxValue)) {
8022 answer_object = Smi::FromInt(unsigned_left);
8023 }
8024 break;
8025 }
8026 case Token::SAR: {
8027 int shift_amount = right & 0x1F;
8028 unsigned int unsigned_left = left;
8029 if (left < 0) {
8030 // Perform arithmetic shift of a negative number by
8031 // complementing number, logical shifting, complementing again.
8032 unsigned_left = ~unsigned_left;
8033 unsigned_left >>= shift_amount;
8034 unsigned_left = ~unsigned_left;
8035 } else {
8036 unsigned_left >>= shift_amount;
8037 }
ager@chromium.orgab99eea2009-08-25 07:05:41 +00008038 ASSERT(Smi::IsValid(static_cast<int32_t>(unsigned_left)));
8039 answer_object = Smi::FromInt(static_cast<int32_t>(unsigned_left));
ager@chromium.org3e875802009-06-29 08:26:34 +00008040 break;
8041 }
ager@chromium.org5aa501c2009-06-23 07:57:28 +00008042 default:
8043 UNREACHABLE();
8044 break;
8045 }
ager@chromium.org3e875802009-06-29 08:26:34 +00008046 if (answer_object == Heap::undefined_value()) {
8047 return false;
8048 }
8049 frame_->Push(Handle<Object>(answer_object));
8050 return true;
ager@chromium.org5aa501c2009-06-23 07:57:28 +00008051}
8052
8053
ager@chromium.org3e875802009-06-29 08:26:34 +00008054// End of CodeGenerator implementation.
8055
ager@chromium.orgac091b72010-05-05 07:34:42 +00008056void TranscendentalCacheStub::Generate(MacroAssembler* masm) {
8057 // Input on stack:
8058 // rsp[8]: argument (should be number).
8059 // rsp[0]: return address.
8060 Label runtime_call;
8061 Label runtime_call_clear_stack;
8062 Label input_not_smi;
8063 Label loaded;
8064 // Test that rax is a number.
8065 __ movq(rax, Operand(rsp, kPointerSize));
8066 __ JumpIfNotSmi(rax, &input_not_smi);
8067 // Input is a smi. Untag and load it onto the FPU stack.
8068 // Then load the bits of the double into rbx.
8069 __ SmiToInteger32(rax, rax);
8070 __ subq(rsp, Immediate(kPointerSize));
8071 __ cvtlsi2sd(xmm1, rax);
8072 __ movsd(Operand(rsp, 0), xmm1);
8073 __ movq(rbx, xmm1);
8074 __ movq(rdx, xmm1);
8075 __ fld_d(Operand(rsp, 0));
8076 __ addq(rsp, Immediate(kPointerSize));
8077 __ jmp(&loaded);
8078
8079 __ bind(&input_not_smi);
8080 // Check if input is a HeapNumber.
8081 __ Move(rbx, Factory::heap_number_map());
8082 __ cmpq(rbx, FieldOperand(rax, HeapObject::kMapOffset));
8083 __ j(not_equal, &runtime_call);
8084 // Input is a HeapNumber. Push it on the FPU stack and load its
8085 // bits into rbx.
8086 __ fld_d(FieldOperand(rax, HeapNumber::kValueOffset));
8087 __ movq(rbx, FieldOperand(rax, HeapNumber::kValueOffset));
8088 __ movq(rdx, rbx);
8089 __ bind(&loaded);
8090 // ST[0] == double value
8091 // rbx = bits of double value.
8092 // rdx = also bits of double value.
8093 // Compute hash (h is 32 bits, bits are 64):
8094 // h = h0 = bits ^ (bits >> 32);
8095 // h ^= h >> 16;
8096 // h ^= h >> 8;
8097 // h = h & (cacheSize - 1);
8098 // or h = (h0 ^ (h0 >> 8) ^ (h0 >> 16) ^ (h0 >> 24)) & (cacheSize - 1)
8099 __ sar(rdx, Immediate(32));
8100 __ xorl(rdx, rbx);
8101 __ movl(rcx, rdx);
8102 __ movl(rax, rdx);
8103 __ movl(rdi, rdx);
8104 __ sarl(rdx, Immediate(8));
8105 __ sarl(rcx, Immediate(16));
8106 __ sarl(rax, Immediate(24));
8107 __ xorl(rcx, rdx);
8108 __ xorl(rax, rdi);
8109 __ xorl(rcx, rax);
8110 ASSERT(IsPowerOf2(TranscendentalCache::kCacheSize));
8111 __ andl(rcx, Immediate(TranscendentalCache::kCacheSize - 1));
8112 // ST[0] == double value.
8113 // rbx = bits of double value.
8114 // rcx = TranscendentalCache::hash(double value).
8115 __ movq(rax, ExternalReference::transcendental_cache_array_address());
8116 // rax points to cache array.
8117 __ movq(rax, Operand(rax, type_ * sizeof(TranscendentalCache::caches_[0])));
8118 // rax points to the cache for the type type_.
8119 // If NULL, the cache hasn't been initialized yet, so go through runtime.
8120 __ testq(rax, rax);
8121 __ j(zero, &runtime_call_clear_stack);
8122#ifdef DEBUG
8123 // Check that the layout of cache elements match expectations.
8124 { // NOLINT - doesn't like a single brace on a line.
8125 TranscendentalCache::Element test_elem[2];
8126 char* elem_start = reinterpret_cast<char*>(&test_elem[0]);
8127 char* elem2_start = reinterpret_cast<char*>(&test_elem[1]);
8128 char* elem_in0 = reinterpret_cast<char*>(&(test_elem[0].in[0]));
8129 char* elem_in1 = reinterpret_cast<char*>(&(test_elem[0].in[1]));
8130 char* elem_out = reinterpret_cast<char*>(&(test_elem[0].output));
8131 // Two uint_32's and a pointer per element.
8132 CHECK_EQ(16, static_cast<int>(elem2_start - elem_start));
8133 CHECK_EQ(0, static_cast<int>(elem_in0 - elem_start));
8134 CHECK_EQ(kIntSize, static_cast<int>(elem_in1 - elem_start));
8135 CHECK_EQ(2 * kIntSize, static_cast<int>(elem_out - elem_start));
8136 }
8137#endif
8138 // Find the address of the rcx'th entry in the cache, i.e., &rax[rcx*16].
8139 __ addl(rcx, rcx);
8140 __ lea(rcx, Operand(rax, rcx, times_8, 0));
8141 // Check if cache matches: Double value is stored in uint32_t[2] array.
8142 Label cache_miss;
8143 __ cmpq(rbx, Operand(rcx, 0));
8144 __ j(not_equal, &cache_miss);
8145 // Cache hit!
8146 __ movq(rax, Operand(rcx, 2 * kIntSize));
8147 __ fstp(0); // Clear FPU stack.
8148 __ ret(kPointerSize);
8149
8150 __ bind(&cache_miss);
8151 // Update cache with new value.
8152 Label nan_result;
8153 GenerateOperation(masm, &nan_result);
8154 __ AllocateHeapNumber(rax, rdi, &runtime_call_clear_stack);
8155 __ movq(Operand(rcx, 0), rbx);
8156 __ movq(Operand(rcx, 2 * kIntSize), rax);
8157 __ fstp_d(FieldOperand(rax, HeapNumber::kValueOffset));
8158 __ ret(kPointerSize);
8159
8160 __ bind(&runtime_call_clear_stack);
8161 __ fstp(0);
8162 __ bind(&runtime_call);
8163 __ TailCallExternalReference(ExternalReference(RuntimeFunction()), 1, 1);
8164
8165 __ bind(&nan_result);
8166 __ fstp(0); // Remove argument from FPU stack.
8167 __ LoadRoot(rax, Heap::kNanValueRootIndex);
8168 __ movq(Operand(rcx, 0), rbx);
8169 __ movq(Operand(rcx, 2 * kIntSize), rax);
8170 __ ret(kPointerSize);
8171}
8172
8173
8174Runtime::FunctionId TranscendentalCacheStub::RuntimeFunction() {
8175 switch (type_) {
8176 // Add more cases when necessary.
8177 case TranscendentalCache::SIN: return Runtime::kMath_sin;
8178 case TranscendentalCache::COS: return Runtime::kMath_cos;
8179 default:
8180 UNIMPLEMENTED();
8181 return Runtime::kAbort;
8182 }
8183}
8184
8185
8186void TranscendentalCacheStub::GenerateOperation(MacroAssembler* masm,
8187 Label* on_nan_result) {
8188 // Registers:
8189 // rbx: Bits of input double. Must be preserved.
8190 // rcx: Pointer to cache entry. Must be preserved.
8191 // st(0): Input double
8192 Label done;
8193 ASSERT(type_ == TranscendentalCache::SIN ||
8194 type_ == TranscendentalCache::COS);
8195 // More transcendental types can be added later.
8196
8197 // Both fsin and fcos require arguments in the range +/-2^63 and
8198 // return NaN for infinities and NaN. They can share all code except
8199 // the actual fsin/fcos operation.
8200 Label in_range;
8201 // If argument is outside the range -2^63..2^63, fsin/cos doesn't
8202 // work. We must reduce it to the appropriate range.
8203 __ movq(rdi, rbx);
8204 // Move exponent and sign bits to low bits.
8205 __ shr(rdi, Immediate(HeapNumber::kMantissaBits));
8206 // Remove sign bit.
8207 __ andl(rdi, Immediate((1 << HeapNumber::KExponentBits) - 1));
8208 int supported_exponent_limit = (63 + HeapNumber::kExponentBias);
8209 __ cmpl(rdi, Immediate(supported_exponent_limit));
8210 __ j(below, &in_range);
8211 // Check for infinity and NaN. Both return NaN for sin.
8212 __ cmpl(rdi, Immediate(0x7ff));
8213 __ j(equal, on_nan_result);
8214
8215 // Use fpmod to restrict argument to the range +/-2*PI.
8216 __ fldpi();
8217 __ fadd(0);
8218 __ fld(1);
8219 // FPU Stack: input, 2*pi, input.
8220 {
8221 Label no_exceptions;
8222 __ fwait();
8223 __ fnstsw_ax();
8224 // Clear if Illegal Operand or Zero Division exceptions are set.
8225 __ testl(rax, Immediate(5)); // #IO and #ZD flags of FPU status word.
8226 __ j(zero, &no_exceptions);
8227 __ fnclex();
8228 __ bind(&no_exceptions);
8229 }
8230
8231 // Compute st(0) % st(1)
8232 {
8233 Label partial_remainder_loop;
8234 __ bind(&partial_remainder_loop);
8235 __ fprem1();
8236 __ fwait();
8237 __ fnstsw_ax();
8238 __ testl(rax, Immediate(0x400)); // Check C2 bit of FPU status word.
8239 // If C2 is set, computation only has partial result. Loop to
8240 // continue computation.
8241 __ j(not_zero, &partial_remainder_loop);
8242 }
8243 // FPU Stack: input, 2*pi, input % 2*pi
8244 __ fstp(2);
8245 // FPU Stack: input % 2*pi, 2*pi,
8246 __ fstp(0);
8247 // FPU Stack: input % 2*pi
8248 __ bind(&in_range);
8249 switch (type_) {
8250 case TranscendentalCache::SIN:
8251 __ fsin();
8252 break;
8253 case TranscendentalCache::COS:
8254 __ fcos();
8255 break;
8256 default:
8257 UNREACHABLE();
8258 }
8259 __ bind(&done);
8260}
8261
8262
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00008263// Get the integer part of a heap number.
8264// Overwrites the contents of rdi, rbx and rcx. Result cannot be rdi or rbx.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008265void IntegerConvert(MacroAssembler* masm,
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00008266 Register result,
8267 Register source) {
8268 // Result may be rcx. If result and source are the same register, source will
8269 // be overwritten.
8270 ASSERT(!result.is(rdi) && !result.is(rbx));
8271 // TODO(lrn): When type info reaches here, if value is a 32-bit integer, use
8272 // cvttsd2si (32-bit version) directly.
8273 Register double_exponent = rbx;
8274 Register double_value = rdi;
8275 Label done, exponent_63_plus;
8276 // Get double and extract exponent.
8277 __ movq(double_value, FieldOperand(source, HeapNumber::kValueOffset));
8278 // Clear result preemptively, in case we need to return zero.
8279 __ xorl(result, result);
8280 __ movq(xmm0, double_value); // Save copy in xmm0 in case we need it there.
8281 // Double to remove sign bit, shift exponent down to least significant bits.
8282 // and subtract bias to get the unshifted, unbiased exponent.
8283 __ lea(double_exponent, Operand(double_value, double_value, times_1, 0));
8284 __ shr(double_exponent, Immediate(64 - HeapNumber::KExponentBits));
8285 __ subl(double_exponent, Immediate(HeapNumber::kExponentBias));
8286 // Check whether the exponent is too big for a 63 bit unsigned integer.
8287 __ cmpl(double_exponent, Immediate(63));
8288 __ j(above_equal, &exponent_63_plus);
8289 // Handle exponent range 0..62.
8290 __ cvttsd2siq(result, xmm0);
8291 __ jmp(&done);
8292
8293 __ bind(&exponent_63_plus);
8294 // Exponent negative or 63+.
8295 __ cmpl(double_exponent, Immediate(83));
8296 // If exponent negative or above 83, number contains no significant bits in
8297 // the range 0..2^31, so result is zero, and rcx already holds zero.
8298 __ j(above, &done);
8299
8300 // Exponent in rage 63..83.
8301 // Mantissa * 2^exponent contains bits in the range 2^0..2^31, namely
8302 // the least significant exponent-52 bits.
8303
8304 // Negate low bits of mantissa if value is negative.
8305 __ addq(double_value, double_value); // Move sign bit to carry.
8306 __ sbbl(result, result); // And convert carry to -1 in result register.
8307 // if scratch2 is negative, do (scratch2-1)^-1, otherwise (scratch2-0)^0.
8308 __ addl(double_value, result);
8309 // Do xor in opposite directions depending on where we want the result
8310 // (depending on whether result is rcx or not).
8311
8312 if (result.is(rcx)) {
8313 __ xorl(double_value, result);
8314 // Left shift mantissa by (exponent - mantissabits - 1) to save the
8315 // bits that have positional values below 2^32 (the extra -1 comes from the
8316 // doubling done above to move the sign bit into the carry flag).
8317 __ leal(rcx, Operand(double_exponent, -HeapNumber::kMantissaBits - 1));
8318 __ shll_cl(double_value);
8319 __ movl(result, double_value);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008320 } else {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00008321 // As the then-branch, but move double-value to result before shifting.
8322 __ xorl(result, double_value);
8323 __ leal(rcx, Operand(double_exponent, -HeapNumber::kMantissaBits - 1));
8324 __ shll_cl(result);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008325 }
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00008326
8327 __ bind(&done);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008328}
8329
8330
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00008331void GenericUnaryOpStub::Generate(MacroAssembler* masm) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008332 Label slow, done;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00008333
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008334 if (op_ == Token::SUB) {
8335 // Check whether the value is a smi.
8336 Label try_float;
8337 __ JumpIfNotSmi(rax, &try_float);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00008338
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008339 // Enter runtime system if the value of the smi is zero
8340 // to make sure that we switch between 0 and -0.
8341 // Also enter it if the value of the smi is Smi::kMinValue.
8342 __ SmiNeg(rax, rax, &done);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00008343
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008344 // Either zero or Smi::kMinValue, neither of which become a smi when
8345 // negated.
8346 __ SmiCompare(rax, Smi::FromInt(0));
8347 __ j(not_equal, &slow);
8348 __ Move(rax, Factory::minus_zero_value());
8349 __ jmp(&done);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00008350
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008351 // Try floating point case.
8352 __ bind(&try_float);
8353 __ movq(rdx, FieldOperand(rax, HeapObject::kMapOffset));
8354 __ CompareRoot(rdx, Heap::kHeapNumberMapRootIndex);
8355 __ j(not_equal, &slow);
8356 // Operand is a float, negate its value by flipping sign bit.
8357 __ movq(rdx, FieldOperand(rax, HeapNumber::kValueOffset));
8358 __ movq(kScratchRegister, Immediate(0x01));
8359 __ shl(kScratchRegister, Immediate(63));
8360 __ xor_(rdx, kScratchRegister); // Flip sign.
8361 // rdx is value to store.
8362 if (overwrite_) {
8363 __ movq(FieldOperand(rax, HeapNumber::kValueOffset), rdx);
8364 } else {
8365 __ AllocateHeapNumber(rcx, rbx, &slow);
8366 // rcx: allocated 'empty' number
8367 __ movq(FieldOperand(rcx, HeapNumber::kValueOffset), rdx);
8368 __ movq(rax, rcx);
8369 }
8370 } else if (op_ == Token::BIT_NOT) {
8371 // Check if the operand is a heap number.
8372 __ movq(rdx, FieldOperand(rax, HeapObject::kMapOffset));
8373 __ CompareRoot(rdx, Heap::kHeapNumberMapRootIndex);
8374 __ j(not_equal, &slow);
8375
8376 // Convert the heap number in rax to an untagged integer in rcx.
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00008377 IntegerConvert(masm, rax, rax);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008378
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00008379 // Do the bitwise operation and smi tag the result.
8380 __ notl(rax);
8381 __ Integer32ToSmi(rax, rax);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008382 }
8383
8384 // Return from the stub.
8385 __ bind(&done);
8386 __ StubReturn(1);
8387
8388 // Handle the slow case by jumping to the JavaScript builtin.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00008389 __ bind(&slow);
8390 __ pop(rcx); // pop return address
8391 __ push(rax);
8392 __ push(rcx); // push return address
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008393 switch (op_) {
8394 case Token::SUB:
8395 __ InvokeBuiltin(Builtins::UNARY_MINUS, JUMP_FUNCTION);
8396 break;
8397 case Token::BIT_NOT:
8398 __ InvokeBuiltin(Builtins::BIT_NOT, JUMP_FUNCTION);
8399 break;
8400 default:
8401 UNREACHABLE();
8402 }
8403}
ager@chromium.org18ad94b2009-09-02 08:22:29 +00008404
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008405
8406void RegExpExecStub::Generate(MacroAssembler* masm) {
8407 // Just jump directly to runtime if native RegExp is not selected at compile
8408 // time or if regexp entry in generated code is turned off runtime switch or
8409 // at compilation.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00008410#ifdef V8_INTERPRETED_REGEXP
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00008411 __ TailCallRuntime(Runtime::kRegExpExec, 4, 1);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00008412#else // V8_INTERPRETED_REGEXP
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008413 if (!FLAG_regexp_entry_native) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00008414 __ TailCallRuntime(Runtime::kRegExpExec, 4, 1);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008415 return;
ager@chromium.org18ad94b2009-09-02 08:22:29 +00008416 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00008417
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008418 // Stack frame on entry.
8419 // esp[0]: return address
8420 // esp[8]: last_match_info (expected JSArray)
8421 // esp[16]: previous index
8422 // esp[24]: subject string
8423 // esp[32]: JSRegExp object
8424
8425 static const int kLastMatchInfoOffset = 1 * kPointerSize;
8426 static const int kPreviousIndexOffset = 2 * kPointerSize;
8427 static const int kSubjectOffset = 3 * kPointerSize;
8428 static const int kJSRegExpOffset = 4 * kPointerSize;
8429
8430 Label runtime;
8431
8432 // Ensure that a RegExp stack is allocated.
8433 ExternalReference address_of_regexp_stack_memory_address =
8434 ExternalReference::address_of_regexp_stack_memory_address();
8435 ExternalReference address_of_regexp_stack_memory_size =
8436 ExternalReference::address_of_regexp_stack_memory_size();
8437 __ movq(kScratchRegister, address_of_regexp_stack_memory_size);
8438 __ movq(kScratchRegister, Operand(kScratchRegister, 0));
8439 __ testq(kScratchRegister, kScratchRegister);
8440 __ j(zero, &runtime);
8441
8442
8443 // Check that the first argument is a JSRegExp object.
8444 __ movq(rax, Operand(rsp, kJSRegExpOffset));
8445 __ JumpIfSmi(rax, &runtime);
8446 __ CmpObjectType(rax, JS_REGEXP_TYPE, kScratchRegister);
8447 __ j(not_equal, &runtime);
8448 // Check that the RegExp has been compiled (data contains a fixed array).
8449 __ movq(rcx, FieldOperand(rax, JSRegExp::kDataOffset));
8450 if (FLAG_debug_code) {
8451 Condition is_smi = masm->CheckSmi(rcx);
8452 __ Check(NegateCondition(is_smi),
8453 "Unexpected type for RegExp data, FixedArray expected");
8454 __ CmpObjectType(rcx, FIXED_ARRAY_TYPE, kScratchRegister);
8455 __ Check(equal, "Unexpected type for RegExp data, FixedArray expected");
8456 }
8457
8458 // rcx: RegExp data (FixedArray)
8459 // Check the type of the RegExp. Only continue if type is JSRegExp::IRREGEXP.
8460 __ movq(rbx, FieldOperand(rcx, JSRegExp::kDataTagOffset));
8461 __ SmiCompare(rbx, Smi::FromInt(JSRegExp::IRREGEXP));
8462 __ j(not_equal, &runtime);
8463
8464 // rcx: RegExp data (FixedArray)
8465 // Check that the number of captures fit in the static offsets vector buffer.
8466 __ movq(rdx, FieldOperand(rcx, JSRegExp::kIrregexpCaptureCountOffset));
8467 // Calculate number of capture registers (number_of_captures + 1) * 2.
8468 __ PositiveSmiTimesPowerOfTwoToInteger64(rdx, rdx, 1);
8469 __ addq(rdx, Immediate(2)); // rdx was number_of_captures * 2.
8470 // Check that the static offsets vector buffer is large enough.
8471 __ cmpq(rdx, Immediate(OffsetsVector::kStaticOffsetsVectorSize));
8472 __ j(above, &runtime);
8473
8474 // rcx: RegExp data (FixedArray)
8475 // rdx: Number of capture registers
8476 // Check that the second argument is a string.
8477 __ movq(rax, Operand(rsp, kSubjectOffset));
8478 __ JumpIfSmi(rax, &runtime);
8479 Condition is_string = masm->IsObjectStringType(rax, rbx, rbx);
8480 __ j(NegateCondition(is_string), &runtime);
8481 // Get the length of the string to rbx.
ager@chromium.orgac091b72010-05-05 07:34:42 +00008482 __ movq(rbx, FieldOperand(rax, String::kLengthOffset));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008483
ager@chromium.orgac091b72010-05-05 07:34:42 +00008484 // rbx: Length of subject string as smi
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008485 // rcx: RegExp data (FixedArray)
8486 // rdx: Number of capture registers
8487 // Check that the third argument is a positive smi less than the string
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00008488 // length. A negative value will be greater (unsigned comparison).
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008489 __ movq(rax, Operand(rsp, kPreviousIndexOffset));
ager@chromium.orgac091b72010-05-05 07:34:42 +00008490 __ JumpIfNotSmi(rax, &runtime);
8491 __ SmiCompare(rax, rbx);
8492 __ j(above_equal, &runtime);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008493
8494 // rcx: RegExp data (FixedArray)
8495 // rdx: Number of capture registers
8496 // Check that the fourth object is a JSArray object.
8497 __ movq(rax, Operand(rsp, kLastMatchInfoOffset));
8498 __ JumpIfSmi(rax, &runtime);
8499 __ CmpObjectType(rax, JS_ARRAY_TYPE, kScratchRegister);
8500 __ j(not_equal, &runtime);
8501 // Check that the JSArray is in fast case.
8502 __ movq(rbx, FieldOperand(rax, JSArray::kElementsOffset));
8503 __ movq(rax, FieldOperand(rbx, HeapObject::kMapOffset));
8504 __ Cmp(rax, Factory::fixed_array_map());
8505 __ j(not_equal, &runtime);
8506 // Check that the last match info has space for the capture registers and the
8507 // additional information. Ensure no overflow in add.
8508 ASSERT(FixedArray::kMaxLength < kMaxInt - FixedArray::kLengthOffset);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00008509 __ movq(rax, FieldOperand(rbx, FixedArray::kLengthOffset));
8510 __ SmiToInteger32(rax, rax);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008511 __ addl(rdx, Immediate(RegExpImpl::kLastMatchOverhead));
8512 __ cmpl(rdx, rax);
8513 __ j(greater, &runtime);
8514
8515 // ecx: RegExp data (FixedArray)
8516 // Check the representation and encoding of the subject string.
8517 Label seq_string, seq_two_byte_string, check_code;
8518 const int kStringRepresentationEncodingMask =
8519 kIsNotStringMask | kStringRepresentationMask | kStringEncodingMask;
8520 __ movq(rax, Operand(rsp, kSubjectOffset));
8521 __ movq(rbx, FieldOperand(rax, HeapObject::kMapOffset));
8522 __ movzxbl(rbx, FieldOperand(rbx, Map::kInstanceTypeOffset));
8523 __ andb(rbx, Immediate(kStringRepresentationEncodingMask));
8524 // First check for sequential string.
8525 ASSERT_EQ(0, kStringTag);
8526 ASSERT_EQ(0, kSeqStringTag);
8527 __ testb(rbx, Immediate(kIsNotStringMask | kStringRepresentationMask));
8528 __ j(zero, &seq_string);
8529
8530 // Check for flat cons string.
8531 // A flat cons string is a cons string where the second part is the empty
8532 // string. In that case the subject string is just the first part of the cons
8533 // string. Also in this case the first part of the cons string is known to be
8534 // a sequential string or an external string.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00008535 __ andb(rbx, Immediate(kStringRepresentationMask));
8536 __ cmpb(rbx, Immediate(kConsStringTag));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008537 __ j(not_equal, &runtime);
8538 __ movq(rdx, FieldOperand(rax, ConsString::kSecondOffset));
8539 __ Cmp(rdx, Factory::empty_string());
8540 __ j(not_equal, &runtime);
8541 __ movq(rax, FieldOperand(rax, ConsString::kFirstOffset));
8542 __ movq(rbx, FieldOperand(rax, HeapObject::kMapOffset));
8543 __ movzxbl(rbx, FieldOperand(rbx, Map::kInstanceTypeOffset));
8544 ASSERT_EQ(0, kSeqStringTag);
8545 __ testb(rbx, Immediate(kStringRepresentationMask));
8546 __ j(not_zero, &runtime);
8547 __ andb(rbx, Immediate(kStringRepresentationEncodingMask));
8548
8549 __ bind(&seq_string);
8550 // rax: subject string (sequential either ascii to two byte)
8551 // rbx: suject string type & kStringRepresentationEncodingMask
8552 // rcx: RegExp data (FixedArray)
8553 // Check that the irregexp code has been generated for an ascii string. If
8554 // it has, the field contains a code object otherwise it contains the hole.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00008555 const int kSeqTwoByteString = kStringTag | kSeqStringTag | kTwoByteStringTag;
8556 __ cmpb(rbx, Immediate(kSeqTwoByteString));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008557 __ j(equal, &seq_two_byte_string);
8558 if (FLAG_debug_code) {
8559 __ cmpb(rbx, Immediate(kStringTag | kSeqStringTag | kAsciiStringTag));
8560 __ Check(equal, "Expected sequential ascii string");
8561 }
8562 __ movq(r12, FieldOperand(rcx, JSRegExp::kDataAsciiCodeOffset));
8563 __ Set(rdi, 1); // Type is ascii.
8564 __ jmp(&check_code);
8565
8566 __ bind(&seq_two_byte_string);
8567 // rax: subject string
8568 // rcx: RegExp data (FixedArray)
8569 __ movq(r12, FieldOperand(rcx, JSRegExp::kDataUC16CodeOffset));
8570 __ Set(rdi, 0); // Type is two byte.
8571
8572 __ bind(&check_code);
8573 // Check that the irregexp code has been generated for the actual string
8574 // encoding. If it has, the field contains a code object otherwise it contains
8575 // the hole.
8576 __ CmpObjectType(r12, CODE_TYPE, kScratchRegister);
8577 __ j(not_equal, &runtime);
8578
8579 // rax: subject string
8580 // rdi: encoding of subject string (1 if ascii, 0 if two_byte);
8581 // r12: code
8582 // Load used arguments before starting to push arguments for call to native
8583 // RegExp code to avoid handling changing stack height.
8584 __ movq(rbx, Operand(rsp, kPreviousIndexOffset));
8585 __ SmiToInteger64(rbx, rbx); // Previous index from smi.
8586
8587 // rax: subject string
8588 // rbx: previous index
8589 // rdi: encoding of subject string (1 if ascii 0 if two_byte);
8590 // r12: code
8591 // All checks done. Now push arguments for native regexp code.
8592 __ IncrementCounter(&Counters::regexp_entry_native, 1);
8593
8594 // rsi is caller save on Windows and used to pass parameter on Linux.
8595 __ push(rsi);
8596
8597 static const int kRegExpExecuteArguments = 7;
8598 __ PrepareCallCFunction(kRegExpExecuteArguments);
8599 int argument_slots_on_stack =
8600 masm->ArgumentStackSlotsForCFunctionCall(kRegExpExecuteArguments);
8601
8602 // Argument 7: Indicate that this is a direct call from JavaScript.
8603 __ movq(Operand(rsp, (argument_slots_on_stack - 1) * kPointerSize),
8604 Immediate(1));
8605
8606 // Argument 6: Start (high end) of backtracking stack memory area.
8607 __ movq(kScratchRegister, address_of_regexp_stack_memory_address);
8608 __ movq(r9, Operand(kScratchRegister, 0));
8609 __ movq(kScratchRegister, address_of_regexp_stack_memory_size);
8610 __ addq(r9, Operand(kScratchRegister, 0));
8611 // Argument 6 passed in r9 on Linux and on the stack on Windows.
8612#ifdef _WIN64
8613 __ movq(Operand(rsp, (argument_slots_on_stack - 2) * kPointerSize), r9);
8614#endif
8615
8616 // Argument 5: static offsets vector buffer.
8617 __ movq(r8, ExternalReference::address_of_static_offsets_vector());
8618 // Argument 5 passed in r8 on Linux and on the stack on Windows.
8619#ifdef _WIN64
8620 __ movq(Operand(rsp, (argument_slots_on_stack - 3) * kPointerSize), r8);
8621#endif
8622
8623 // First four arguments are passed in registers on both Linux and Windows.
8624#ifdef _WIN64
8625 Register arg4 = r9;
8626 Register arg3 = r8;
8627 Register arg2 = rdx;
8628 Register arg1 = rcx;
8629#else
8630 Register arg4 = rcx;
8631 Register arg3 = rdx;
8632 Register arg2 = rsi;
8633 Register arg1 = rdi;
8634#endif
8635
8636 // Keep track on aliasing between argX defined above and the registers used.
8637 // rax: subject string
8638 // rbx: previous index
8639 // rdi: encoding of subject string (1 if ascii 0 if two_byte);
8640 // r12: code
8641
8642 // Argument 4: End of string data
8643 // Argument 3: Start of string data
8644 Label setup_two_byte, setup_rest;
8645 __ testb(rdi, rdi);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008646 __ j(zero, &setup_two_byte);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00008647 __ SmiToInteger32(rdi, FieldOperand(rax, String::kLengthOffset));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008648 __ lea(arg4, FieldOperand(rax, rdi, times_1, SeqAsciiString::kHeaderSize));
8649 __ lea(arg3, FieldOperand(rax, rbx, times_1, SeqAsciiString::kHeaderSize));
8650 __ jmp(&setup_rest);
8651 __ bind(&setup_two_byte);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00008652 __ SmiToInteger32(rdi, FieldOperand(rax, String::kLengthOffset));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008653 __ lea(arg4, FieldOperand(rax, rdi, times_2, SeqTwoByteString::kHeaderSize));
8654 __ lea(arg3, FieldOperand(rax, rbx, times_2, SeqTwoByteString::kHeaderSize));
8655
8656 __ bind(&setup_rest);
8657 // Argument 2: Previous index.
8658 __ movq(arg2, rbx);
8659
8660 // Argument 1: Subject string.
8661 __ movq(arg1, rax);
8662
8663 // Locate the code entry and call it.
8664 __ addq(r12, Immediate(Code::kHeaderSize - kHeapObjectTag));
8665 __ CallCFunction(r12, kRegExpExecuteArguments);
8666
8667 // rsi is caller save, as it is used to pass parameter.
8668 __ pop(rsi);
8669
8670 // Check the result.
8671 Label success;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00008672 __ cmpl(rax, Immediate(NativeRegExpMacroAssembler::SUCCESS));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008673 __ j(equal, &success);
8674 Label failure;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00008675 __ cmpl(rax, Immediate(NativeRegExpMacroAssembler::FAILURE));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008676 __ j(equal, &failure);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00008677 __ cmpl(rax, Immediate(NativeRegExpMacroAssembler::EXCEPTION));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008678 // If not exception it can only be retry. Handle that in the runtime system.
8679 __ j(not_equal, &runtime);
8680 // Result must now be exception. If there is no pending exception already a
8681 // stack overflow (on the backtrack stack) was detected in RegExp code but
8682 // haven't created the exception yet. Handle that in the runtime system.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00008683 // TODO(592): Rerunning the RegExp to get the stack overflow exception.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008684 ExternalReference pending_exception_address(Top::k_pending_exception_address);
8685 __ movq(kScratchRegister, pending_exception_address);
8686 __ Cmp(kScratchRegister, Factory::the_hole_value());
8687 __ j(equal, &runtime);
8688 __ bind(&failure);
8689 // For failure and exception return null.
8690 __ Move(rax, Factory::null_value());
8691 __ ret(4 * kPointerSize);
8692
8693 // Load RegExp data.
8694 __ bind(&success);
8695 __ movq(rax, Operand(rsp, kJSRegExpOffset));
8696 __ movq(rcx, FieldOperand(rax, JSRegExp::kDataOffset));
8697 __ movq(rdx, FieldOperand(rcx, JSRegExp::kIrregexpCaptureCountOffset));
8698 // Calculate number of capture registers (number_of_captures + 1) * 2.
8699 __ PositiveSmiTimesPowerOfTwoToInteger64(rdx, rdx, 1);
8700 __ addq(rdx, Immediate(2)); // rdx was number_of_captures * 2.
8701
8702 // rdx: Number of capture registers
8703 // Load last_match_info which is still known to be a fast case JSArray.
8704 __ movq(rax, Operand(rsp, kLastMatchInfoOffset));
8705 __ movq(rbx, FieldOperand(rax, JSArray::kElementsOffset));
8706
8707 // rbx: last_match_info backing store (FixedArray)
8708 // rdx: number of capture registers
8709 // Store the capture count.
8710 __ Integer32ToSmi(kScratchRegister, rdx);
8711 __ movq(FieldOperand(rbx, RegExpImpl::kLastCaptureCountOffset),
8712 kScratchRegister);
8713 // Store last subject and last input.
8714 __ movq(rax, Operand(rsp, kSubjectOffset));
8715 __ movq(FieldOperand(rbx, RegExpImpl::kLastSubjectOffset), rax);
8716 __ movq(rcx, rbx);
8717 __ RecordWrite(rcx, RegExpImpl::kLastSubjectOffset, rax, rdi);
8718 __ movq(rax, Operand(rsp, kSubjectOffset));
8719 __ movq(FieldOperand(rbx, RegExpImpl::kLastInputOffset), rax);
8720 __ movq(rcx, rbx);
8721 __ RecordWrite(rcx, RegExpImpl::kLastInputOffset, rax, rdi);
8722
8723 // Get the static offsets vector filled by the native regexp code.
8724 __ movq(rcx, ExternalReference::address_of_static_offsets_vector());
8725
8726 // rbx: last_match_info backing store (FixedArray)
8727 // rcx: offsets vector
8728 // rdx: number of capture registers
8729 Label next_capture, done;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008730 // Capture register counter starts from number of capture registers and
8731 // counts down until wraping after zero.
8732 __ bind(&next_capture);
8733 __ subq(rdx, Immediate(1));
8734 __ j(negative, &done);
8735 // Read the value from the static offsets vector buffer and make it a smi.
8736 __ movl(rdi, Operand(rcx, rdx, times_int_size, 0));
8737 __ Integer32ToSmi(rdi, rdi, &runtime);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008738 // Store the smi value in the last match info.
8739 __ movq(FieldOperand(rbx,
8740 rdx,
8741 times_pointer_size,
8742 RegExpImpl::kFirstCaptureOffset),
8743 rdi);
8744 __ jmp(&next_capture);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00008745 __ bind(&done);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008746
8747 // Return last match info.
8748 __ movq(rax, Operand(rsp, kLastMatchInfoOffset));
8749 __ ret(4 * kPointerSize);
8750
8751 // Do the runtime call to execute the regexp.
8752 __ bind(&runtime);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00008753 __ TailCallRuntime(Runtime::kRegExpExec, 4, 1);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00008754#endif // V8_INTERPRETED_REGEXP
ager@chromium.org5aa501c2009-06-23 07:57:28 +00008755}
8756
ager@chromium.org5aa501c2009-06-23 07:57:28 +00008757
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00008758void NumberToStringStub::GenerateConvertHashCodeToIndex(MacroAssembler* masm,
8759 Register hash,
8760 Register mask) {
8761 __ and_(hash, mask);
8762 // Each entry in string cache consists of two pointer sized fields,
8763 // but times_twice_pointer_size (multiplication by 16) scale factor
8764 // is not supported by addrmode on x64 platform.
8765 // So we have to premultiply entry index before lookup.
8766 __ shl(hash, Immediate(kPointerSizeLog2 + 1));
8767}
8768
8769
fschneider@chromium.org086aac62010-03-17 13:18:24 +00008770void NumberToStringStub::GenerateLookupNumberStringCache(MacroAssembler* masm,
8771 Register object,
8772 Register result,
8773 Register scratch1,
8774 Register scratch2,
8775 bool object_is_smi,
8776 Label* not_found) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00008777 // Use of registers. Register result is used as a temporary.
8778 Register number_string_cache = result;
8779 Register mask = scratch1;
8780 Register scratch = scratch2;
8781
8782 // Load the number string cache.
8783 __ LoadRoot(number_string_cache, Heap::kNumberStringCacheRootIndex);
8784
8785 // Make the hash mask from the length of the number string cache. It
8786 // contains two elements (number and string) for each cache entry.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00008787 __ movq(mask, FieldOperand(number_string_cache, FixedArray::kLengthOffset));
8788 // Divide smi tagged length by two.
8789 __ PositiveSmiDivPowerOfTwoToInteger32(mask, mask, 1);
8790 __ subq(mask, Immediate(1)); // Make mask.
fschneider@chromium.org086aac62010-03-17 13:18:24 +00008791
8792 // Calculate the entry in the number string cache. The hash value in the
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00008793 // number string cache for smis is just the smi value, and the hash for
8794 // doubles is the xor of the upper and lower words. See
8795 // Heap::GetNumberStringCache.
8796 Label is_smi;
8797 Label load_result_from_cache;
8798 if (!object_is_smi) {
8799 __ JumpIfSmi(object, &is_smi);
8800 __ CheckMap(object, Factory::heap_number_map(), not_found, true);
8801
8802 ASSERT_EQ(8, kDoubleSize);
8803 __ movl(scratch, FieldOperand(object, HeapNumber::kValueOffset + 4));
8804 __ xor_(scratch, FieldOperand(object, HeapNumber::kValueOffset));
8805 GenerateConvertHashCodeToIndex(masm, scratch, mask);
8806
8807 Register index = scratch;
8808 Register probe = mask;
8809 __ movq(probe,
8810 FieldOperand(number_string_cache,
8811 index,
8812 times_1,
8813 FixedArray::kHeaderSize));
8814 __ JumpIfSmi(probe, not_found);
8815 ASSERT(CpuFeatures::IsSupported(SSE2));
8816 CpuFeatures::Scope fscope(SSE2);
8817 __ movsd(xmm0, FieldOperand(object, HeapNumber::kValueOffset));
8818 __ movsd(xmm1, FieldOperand(probe, HeapNumber::kValueOffset));
8819 __ comisd(xmm0, xmm1);
8820 __ j(parity_even, not_found); // Bail out if NaN is involved.
8821 __ j(not_equal, not_found); // The cache did not contain this value.
8822 __ jmp(&load_result_from_cache);
8823 }
8824
8825 __ bind(&is_smi);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00008826 __ movq(scratch, object);
8827 __ SmiToInteger32(scratch, scratch);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00008828 GenerateConvertHashCodeToIndex(masm, scratch, mask);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00008829
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00008830 Register index = scratch;
fschneider@chromium.org086aac62010-03-17 13:18:24 +00008831 // Check if the entry is the smi we are looking for.
8832 __ cmpq(object,
8833 FieldOperand(number_string_cache,
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00008834 index,
fschneider@chromium.org086aac62010-03-17 13:18:24 +00008835 times_1,
8836 FixedArray::kHeaderSize));
8837 __ j(not_equal, not_found);
8838
8839 // Get the result from the cache.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00008840 __ bind(&load_result_from_cache);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00008841 __ movq(result,
8842 FieldOperand(number_string_cache,
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00008843 index,
fschneider@chromium.org086aac62010-03-17 13:18:24 +00008844 times_1,
8845 FixedArray::kHeaderSize + kPointerSize));
8846 __ IncrementCounter(&Counters::number_to_string_native, 1);
8847}
8848
8849
8850void NumberToStringStub::Generate(MacroAssembler* masm) {
8851 Label runtime;
8852
8853 __ movq(rbx, Operand(rsp, kPointerSize));
8854
8855 // Generate code to lookup number in the number string cache.
8856 GenerateLookupNumberStringCache(masm, rbx, rax, r8, r9, false, &runtime);
8857 __ ret(1 * kPointerSize);
8858
8859 __ bind(&runtime);
8860 // Handle number to string in the runtime system if not found in the cache.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00008861 __ TailCallRuntime(Runtime::kNumberToStringSkipCache, 1, 1);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00008862}
8863
8864
ager@chromium.orgac091b72010-05-05 07:34:42 +00008865void RecordWriteStub::Generate(MacroAssembler* masm) {
8866 masm->RecordWriteHelper(object_, addr_, scratch_);
8867 masm->ret(0);
8868}
8869
8870
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00008871static int NegativeComparisonResult(Condition cc) {
8872 ASSERT(cc != equal);
8873 ASSERT((cc == less) || (cc == less_equal)
8874 || (cc == greater) || (cc == greater_equal));
8875 return (cc == greater || cc == greater_equal) ? LESS : GREATER;
8876}
8877
ager@chromium.orgac091b72010-05-05 07:34:42 +00008878
ager@chromium.org5aa501c2009-06-23 07:57:28 +00008879void CompareStub::Generate(MacroAssembler* masm) {
ager@chromium.org3e875802009-06-29 08:26:34 +00008880 Label call_builtin, done;
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00008881 // The compare stub returns a positive, negative, or zero 64-bit integer
8882 // value in rax, corresponding to result of comparing the two inputs.
ager@chromium.org3e875802009-06-29 08:26:34 +00008883 // NOTICE! This code is only reached after a smi-fast-case check, so
8884 // it is certain that at least one operand isn't a smi.
8885
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00008886 // Two identical objects are equal unless they are both NaN or undefined.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00008887 {
8888 Label not_identical;
8889 __ cmpq(rax, rdx);
8890 __ j(not_equal, &not_identical);
ager@chromium.org3e875802009-06-29 08:26:34 +00008891
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00008892 if (cc_ != equal) {
8893 // Check for undefined. undefined OP undefined is false even though
8894 // undefined == undefined.
8895 Label check_for_nan;
8896 __ CompareRoot(rdx, Heap::kUndefinedValueRootIndex);
8897 __ j(not_equal, &check_for_nan);
8898 __ Set(rax, NegativeComparisonResult(cc_));
8899 __ ret(0);
8900 __ bind(&check_for_nan);
8901 }
ager@chromium.org3e875802009-06-29 08:26:34 +00008902
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00008903 // Test for NaN. Sadly, we can't just compare to Factory::nan_value(),
8904 // so we do the second best thing - test it ourselves.
8905 // Note: if cc_ != equal, never_nan_nan_ is not used.
8906 if (never_nan_nan_ && (cc_ == equal)) {
8907 __ Set(rax, EQUAL);
8908 __ ret(0);
8909 } else {
8910 Label return_equal;
8911 Label heap_number;
8912 // If it's not a heap number, then return equal.
8913 __ Cmp(FieldOperand(rdx, HeapObject::kMapOffset),
8914 Factory::heap_number_map());
8915 __ j(equal, &heap_number);
8916 __ bind(&return_equal);
8917 __ Set(rax, EQUAL);
8918 __ ret(0);
ager@chromium.org3811b432009-10-28 14:53:37 +00008919
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00008920 __ bind(&heap_number);
8921 // It is a heap number, so return non-equal if it's NaN and equal if
8922 // it's not NaN.
8923 // The representation of NaN values has all exponent bits (52..62) set,
8924 // and not all mantissa bits (0..51) clear.
8925 // We only allow QNaNs, which have bit 51 set (which also rules out
8926 // the value being Infinity).
8927
8928 // Value is a QNaN if value & kQuietNaNMask == kQuietNaNMask, i.e.,
8929 // all bits in the mask are set. We only need to check the word
8930 // that contains the exponent and high bit of the mantissa.
8931 ASSERT_NE(0, (kQuietNaNHighBitsMask << 1) & 0x80000000u);
8932 __ movl(rdx, FieldOperand(rdx, HeapNumber::kExponentOffset));
8933 __ xorl(rax, rax);
8934 __ addl(rdx, rdx); // Shift value and mask so mask applies to top bits.
8935 __ cmpl(rdx, Immediate(kQuietNaNHighBitsMask << 1));
8936 if (cc_ == equal) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00008937 __ setcc(above_equal, rax);
8938 __ ret(0);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00008939 } else {
8940 Label nan;
8941 __ j(above_equal, &nan);
8942 __ Set(rax, EQUAL);
8943 __ ret(0);
8944 __ bind(&nan);
8945 __ Set(rax, NegativeComparisonResult(cc_));
8946 __ ret(0);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00008947 }
ager@chromium.org3e875802009-06-29 08:26:34 +00008948 }
8949
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00008950 __ bind(&not_identical);
8951 }
8952
8953 if (cc_ == equal) { // Both strict and non-strict.
8954 Label slow; // Fallthrough label.
8955
ager@chromium.org3e875802009-06-29 08:26:34 +00008956 // If we're doing a strict equality comparison, we don't have to do
8957 // type conversion, so we generate code to do fast comparison for objects
8958 // and oddballs. Non-smi numbers and strings still go through the usual
8959 // slow-case code.
8960 if (strict_) {
8961 // If either is a Smi (we know that not both are), then they can only
8962 // be equal if the other is a HeapNumber. If so, use the slow case.
8963 {
8964 Label not_smis;
ager@chromium.org4af710e2009-09-15 12:20:11 +00008965 __ SelectNonSmi(rbx, rax, rdx, &not_smis);
ager@chromium.org3e875802009-06-29 08:26:34 +00008966
8967 // Check if the non-smi operand is a heap number.
8968 __ Cmp(FieldOperand(rbx, HeapObject::kMapOffset),
8969 Factory::heap_number_map());
8970 // If heap number, handle it in the slow case.
8971 __ j(equal, &slow);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00008972 // Return non-equal. ebx (the lower half of rbx) is not zero.
ager@chromium.org3e875802009-06-29 08:26:34 +00008973 __ movq(rax, rbx);
8974 __ ret(0);
8975
8976 __ bind(&not_smis);
8977 }
8978
8979 // If either operand is a JSObject or an oddball value, then they are not
8980 // equal since their pointers are different
8981 // There is no test for undetectability in strict equality.
8982
8983 // If the first object is a JS object, we have done pointer comparison.
8984 ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
8985 Label first_non_object;
8986 __ CmpObjectType(rax, FIRST_JS_OBJECT_TYPE, rcx);
8987 __ j(below, &first_non_object);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00008988 // Return non-zero (eax (not rax) is not zero)
ager@chromium.org3e875802009-06-29 08:26:34 +00008989 Label return_not_equal;
8990 ASSERT(kHeapObjectTag != 0);
8991 __ bind(&return_not_equal);
8992 __ ret(0);
8993
8994 __ bind(&first_non_object);
8995 // Check for oddballs: true, false, null, undefined.
8996 __ CmpInstanceType(rcx, ODDBALL_TYPE);
8997 __ j(equal, &return_not_equal);
8998
8999 __ CmpObjectType(rdx, FIRST_JS_OBJECT_TYPE, rcx);
9000 __ j(above_equal, &return_not_equal);
9001
9002 // Check for oddballs: true, false, null, undefined.
9003 __ CmpInstanceType(rcx, ODDBALL_TYPE);
9004 __ j(equal, &return_not_equal);
9005
9006 // Fall through to the general case.
9007 }
9008 __ bind(&slow);
9009 }
9010
9011 // Push arguments below the return address to prepare jump to builtin.
9012 __ pop(rcx);
9013 __ push(rax);
9014 __ push(rdx);
9015 __ push(rcx);
9016
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00009017 // Generate the number comparison code.
9018 if (include_number_compare_) {
9019 Label non_number_comparison;
9020 Label unordered;
9021 FloatingPointHelper::LoadFloatOperand(masm, rdx, xmm0,
9022 &non_number_comparison);
9023 FloatingPointHelper::LoadFloatOperand(masm, rax, xmm1,
9024 &non_number_comparison);
ager@chromium.org3e875802009-06-29 08:26:34 +00009025
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00009026 __ comisd(xmm0, xmm1);
ager@chromium.org3e875802009-06-29 08:26:34 +00009027
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00009028 // Don't base result on EFLAGS when a NaN is involved.
9029 __ j(parity_even, &unordered);
9030 // Return a result of -1, 0, or 1, based on EFLAGS.
9031 __ movq(rax, Immediate(0)); // equal
9032 __ movq(rcx, Immediate(1));
9033 __ cmovq(above, rax, rcx);
9034 __ movq(rcx, Immediate(-1));
9035 __ cmovq(below, rax, rcx);
9036 __ ret(2 * kPointerSize); // rax, rdx were pushed
ager@chromium.org3e875802009-06-29 08:26:34 +00009037
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00009038 // If one of the numbers was NaN, then the result is always false.
9039 // The cc is never not-equal.
9040 __ bind(&unordered);
9041 ASSERT(cc_ != not_equal);
9042 if (cc_ == less || cc_ == less_equal) {
9043 __ Set(rax, 1);
9044 } else {
9045 __ Set(rax, -1);
9046 }
9047 __ ret(2 * kPointerSize); // rax, rdx were pushed
ager@chromium.org3e875802009-06-29 08:26:34 +00009048
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00009049 // The number comparison code did not provide a valid result.
9050 __ bind(&non_number_comparison);
9051 }
ager@chromium.org3e875802009-06-29 08:26:34 +00009052
9053 // Fast negative check for symbol-to-symbol equality.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009054 Label check_for_strings;
ager@chromium.org3e875802009-06-29 08:26:34 +00009055 if (cc_ == equal) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009056 BranchIfNonSymbol(masm, &check_for_strings, rax, kScratchRegister);
9057 BranchIfNonSymbol(masm, &check_for_strings, rdx, kScratchRegister);
ager@chromium.org3e875802009-06-29 08:26:34 +00009058
9059 // We've already checked for object identity, so if both operands
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00009060 // are symbols they aren't equal. Register eax (not rax) already holds a
ager@chromium.org3e875802009-06-29 08:26:34 +00009061 // non-zero value, which indicates not equal, so just return.
9062 __ ret(2 * kPointerSize);
9063 }
9064
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009065 __ bind(&check_for_strings);
9066
9067 __ JumpIfNotBothSequentialAsciiStrings(rdx, rax, rcx, rbx, &call_builtin);
9068
9069 // Inline comparison of ascii strings.
9070 StringCompareStub::GenerateCompareFlatAsciiStrings(masm,
9071 rdx,
9072 rax,
9073 rcx,
9074 rbx,
9075 rdi,
9076 r8);
9077
9078#ifdef DEBUG
9079 __ Abort("Unexpected fall-through from string comparison");
9080#endif
9081
ager@chromium.org3e875802009-06-29 08:26:34 +00009082 __ bind(&call_builtin);
9083 // must swap argument order
9084 __ pop(rcx);
9085 __ pop(rdx);
9086 __ pop(rax);
9087 __ push(rdx);
9088 __ push(rax);
9089
9090 // Figure out which native to call and setup the arguments.
9091 Builtins::JavaScript builtin;
9092 if (cc_ == equal) {
9093 builtin = strict_ ? Builtins::STRICT_EQUALS : Builtins::EQUALS;
9094 } else {
9095 builtin = Builtins::COMPARE;
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00009096 __ Push(Smi::FromInt(NegativeComparisonResult(cc_)));
ager@chromium.org3e875802009-06-29 08:26:34 +00009097 }
9098
9099 // Restore return address on the stack.
9100 __ push(rcx);
9101
9102 // Call the native; it returns -1 (less), 0 (equal), or 1 (greater)
9103 // tagged as a small integer.
9104 __ InvokeBuiltin(builtin, JUMP_FUNCTION);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00009105}
9106
9107
ager@chromium.org3e875802009-06-29 08:26:34 +00009108void CompareStub::BranchIfNonSymbol(MacroAssembler* masm,
9109 Label* label,
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009110 Register object,
9111 Register scratch) {
ager@chromium.org4af710e2009-09-15 12:20:11 +00009112 __ JumpIfSmi(object, label);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009113 __ movq(scratch, FieldOperand(object, HeapObject::kMapOffset));
9114 __ movzxbq(scratch,
9115 FieldOperand(scratch, Map::kInstanceTypeOffset));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00009116 // Ensure that no non-strings have the symbol bit set.
9117 ASSERT(kNotStringTag + kIsSymbolMask > LAST_TYPE);
9118 ASSERT(kSymbolTag != 0);
9119 __ testb(scratch, Immediate(kIsSymbolMask));
9120 __ j(zero, label);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00009121}
9122
9123
9124// Call the function just below TOS on the stack with the given
9125// arguments. The receiver is the TOS.
9126void CodeGenerator::CallWithArguments(ZoneList<Expression*>* args,
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009127 CallFunctionFlags flags,
ager@chromium.org5aa501c2009-06-23 07:57:28 +00009128 int position) {
9129 // Push the arguments ("left-to-right") on the stack.
9130 int arg_count = args->length();
9131 for (int i = 0; i < arg_count; i++) {
9132 Load(args->at(i));
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00009133 frame_->SpillTop();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00009134 }
9135
9136 // Record the position for debugging purposes.
9137 CodeForSourcePosition(position);
9138
9139 // Use the shared code stub to call the function.
9140 InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009141 CallFunctionStub call_function(arg_count, in_loop, flags);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00009142 Result answer = frame_->CallStub(&call_function, arg_count + 1);
9143 // Restore context and replace function on the stack with the
9144 // result of the stub invocation.
9145 frame_->RestoreContextRegister();
9146 frame_->SetElementAt(0, &answer);
9147}
9148
9149
9150void InstanceofStub::Generate(MacroAssembler* masm) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009151 // Implements "value instanceof function" operator.
9152 // Expected input state:
9153 // rsp[0] : return address
9154 // rsp[1] : function pointer
9155 // rsp[2] : value
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00009156 // Returns a bitwise zero to indicate that the value
9157 // is and instance of the function and anything else to
9158 // indicate that the value is not an instance.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009159
9160 // Get the object - go slow case if it's a smi.
9161 Label slow;
9162 __ movq(rax, Operand(rsp, 2 * kPointerSize));
ager@chromium.org4af710e2009-09-15 12:20:11 +00009163 __ JumpIfSmi(rax, &slow);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009164
9165 // Check that the left hand is a JS object. Leave its map in rax.
9166 __ CmpObjectType(rax, FIRST_JS_OBJECT_TYPE, rax);
9167 __ j(below, &slow);
9168 __ CmpInstanceType(rax, LAST_JS_OBJECT_TYPE);
9169 __ j(above, &slow);
9170
9171 // Get the prototype of the function.
9172 __ movq(rdx, Operand(rsp, 1 * kPointerSize));
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00009173 // rdx is function, rax is map.
9174
9175 // Look up the function and the map in the instanceof cache.
9176 Label miss;
9177 __ CompareRoot(rdx, Heap::kInstanceofCacheFunctionRootIndex);
9178 __ j(not_equal, &miss);
9179 __ CompareRoot(rax, Heap::kInstanceofCacheMapRootIndex);
9180 __ j(not_equal, &miss);
9181 __ LoadRoot(rax, Heap::kInstanceofCacheAnswerRootIndex);
9182 __ ret(2 * kPointerSize);
9183
9184 __ bind(&miss);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009185 __ TryGetFunctionPrototype(rdx, rbx, &slow);
9186
9187 // Check that the function prototype is a JS object.
ager@chromium.org4af710e2009-09-15 12:20:11 +00009188 __ JumpIfSmi(rbx, &slow);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009189 __ CmpObjectType(rbx, FIRST_JS_OBJECT_TYPE, kScratchRegister);
9190 __ j(below, &slow);
9191 __ CmpInstanceType(kScratchRegister, LAST_JS_OBJECT_TYPE);
9192 __ j(above, &slow);
9193
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00009194 // Register mapping:
9195 // rax is object map.
9196 // rdx is function.
9197 // rbx is function prototype.
9198 __ StoreRoot(rdx, Heap::kInstanceofCacheFunctionRootIndex);
9199 __ StoreRoot(rax, Heap::kInstanceofCacheMapRootIndex);
9200
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009201 __ movq(rcx, FieldOperand(rax, Map::kPrototypeOffset));
9202
9203 // Loop through the prototype chain looking for the function prototype.
9204 Label loop, is_instance, is_not_instance;
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009205 __ LoadRoot(kScratchRegister, Heap::kNullValueRootIndex);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009206 __ bind(&loop);
9207 __ cmpq(rcx, rbx);
9208 __ j(equal, &is_instance);
9209 __ cmpq(rcx, kScratchRegister);
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00009210 // The code at is_not_instance assumes that kScratchRegister contains a
9211 // non-zero GCable value (the null object in this case).
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009212 __ j(equal, &is_not_instance);
9213 __ movq(rcx, FieldOperand(rcx, HeapObject::kMapOffset));
9214 __ movq(rcx, FieldOperand(rcx, Map::kPrototypeOffset));
9215 __ jmp(&loop);
9216
9217 __ bind(&is_instance);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00009218 __ xorl(rax, rax);
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00009219 // Store bitwise zero in the cache. This is a Smi in GC terms.
9220 ASSERT_EQ(0, kSmiTag);
9221 __ StoreRoot(rax, Heap::kInstanceofCacheAnswerRootIndex);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009222 __ ret(2 * kPointerSize);
9223
9224 __ bind(&is_not_instance);
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00009225 // We have to store a non-zero value in the cache.
9226 __ StoreRoot(kScratchRegister, Heap::kInstanceofCacheAnswerRootIndex);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009227 __ ret(2 * kPointerSize);
9228
9229 // Slow-case: Go through the JavaScript implementation.
9230 __ bind(&slow);
9231 __ InvokeBuiltin(Builtins::INSTANCE_OF, JUMP_FUNCTION);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00009232}
9233
9234
9235void ArgumentsAccessStub::GenerateNewObject(MacroAssembler* masm) {
ager@chromium.org5c838252010-02-19 08:53:10 +00009236 // rsp[0] : return address
9237 // rsp[8] : number of parameters
9238 // rsp[16] : receiver displacement
9239 // rsp[24] : function
9240
ager@chromium.org5aa501c2009-06-23 07:57:28 +00009241 // The displacement is used for skipping the return address and the
9242 // frame pointer on the stack. It is the offset of the last
9243 // parameter (if any) relative to the frame pointer.
9244 static const int kDisplacement = 2 * kPointerSize;
9245
9246 // Check if the calling frame is an arguments adaptor frame.
ager@chromium.org5c838252010-02-19 08:53:10 +00009247 Label adaptor_frame, try_allocate, runtime;
ager@chromium.org5aa501c2009-06-23 07:57:28 +00009248 __ movq(rdx, Operand(rbp, StandardFrameConstants::kCallerFPOffset));
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00009249 __ SmiCompare(Operand(rdx, StandardFrameConstants::kContextOffset),
9250 Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR));
ager@chromium.org5c838252010-02-19 08:53:10 +00009251 __ j(equal, &adaptor_frame);
9252
9253 // Get the length from the frame.
9254 __ movq(rcx, Operand(rsp, 1 * kPointerSize));
9255 __ jmp(&try_allocate);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00009256
9257 // Patch the arguments.length and the parameters pointer.
ager@chromium.org5c838252010-02-19 08:53:10 +00009258 __ bind(&adaptor_frame);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00009259 __ movq(rcx, Operand(rdx, ArgumentsAdaptorFrameConstants::kLengthOffset));
9260 __ movq(Operand(rsp, 1 * kPointerSize), rcx);
ager@chromium.org5c838252010-02-19 08:53:10 +00009261 // Do not clobber the length index for the indexing operation since
9262 // it is used compute the size for allocation later.
9263 SmiIndex index = masm->SmiToIndex(rbx, rcx, kPointerSizeLog2);
ager@chromium.org4af710e2009-09-15 12:20:11 +00009264 __ lea(rdx, Operand(rdx, index.reg, index.scale, kDisplacement));
ager@chromium.org5aa501c2009-06-23 07:57:28 +00009265 __ movq(Operand(rsp, 2 * kPointerSize), rdx);
9266
ager@chromium.org5c838252010-02-19 08:53:10 +00009267 // Try the new space allocation. Start out with computing the size of
9268 // the arguments object and the elements array.
9269 Label add_arguments_object;
9270 __ bind(&try_allocate);
9271 __ testq(rcx, rcx);
9272 __ j(zero, &add_arguments_object);
9273 index = masm->SmiToIndex(rcx, rcx, kPointerSizeLog2);
9274 __ lea(rcx, Operand(index.reg, index.scale, FixedArray::kHeaderSize));
9275 __ bind(&add_arguments_object);
9276 __ addq(rcx, Immediate(Heap::kArgumentsObjectSize));
9277
9278 // Do the allocation of both objects in one go.
9279 __ AllocateInNewSpace(rcx, rax, rdx, rbx, &runtime, TAG_OBJECT);
9280
9281 // Get the arguments boilerplate from the current (global) context.
9282 int offset = Context::SlotOffset(Context::ARGUMENTS_BOILERPLATE_INDEX);
9283 __ movq(rdi, Operand(rsi, Context::SlotOffset(Context::GLOBAL_INDEX)));
9284 __ movq(rdi, FieldOperand(rdi, GlobalObject::kGlobalContextOffset));
9285 __ movq(rdi, Operand(rdi, offset));
9286
9287 // Copy the JS object part.
9288 for (int i = 0; i < JSObject::kHeaderSize; i += kPointerSize) {
9289 __ movq(kScratchRegister, FieldOperand(rdi, i));
9290 __ movq(FieldOperand(rax, i), kScratchRegister);
9291 }
9292
9293 // Setup the callee in-object property.
9294 ASSERT(Heap::arguments_callee_index == 0);
9295 __ movq(kScratchRegister, Operand(rsp, 3 * kPointerSize));
9296 __ movq(FieldOperand(rax, JSObject::kHeaderSize), kScratchRegister);
9297
9298 // Get the length (smi tagged) and set that as an in-object property too.
9299 ASSERT(Heap::arguments_length_index == 1);
9300 __ movq(rcx, Operand(rsp, 1 * kPointerSize));
9301 __ movq(FieldOperand(rax, JSObject::kHeaderSize + kPointerSize), rcx);
9302
9303 // If there are no actual arguments, we're done.
9304 Label done;
9305 __ testq(rcx, rcx);
9306 __ j(zero, &done);
9307
9308 // Get the parameters pointer from the stack and untag the length.
9309 __ movq(rdx, Operand(rsp, 2 * kPointerSize));
ager@chromium.org5c838252010-02-19 08:53:10 +00009310
9311 // Setup the elements pointer in the allocated arguments object and
9312 // initialize the header in the elements fixed array.
9313 __ lea(rdi, Operand(rax, Heap::kArgumentsObjectSize));
9314 __ movq(FieldOperand(rax, JSObject::kElementsOffset), rdi);
9315 __ LoadRoot(kScratchRegister, Heap::kFixedArrayMapRootIndex);
9316 __ movq(FieldOperand(rdi, FixedArray::kMapOffset), kScratchRegister);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009317 __ movq(FieldOperand(rdi, FixedArray::kLengthOffset), rcx);
9318 __ SmiToInteger32(rcx, rcx); // Untag length for the loop below.
ager@chromium.org5c838252010-02-19 08:53:10 +00009319
9320 // Copy the fixed array slots.
9321 Label loop;
9322 __ bind(&loop);
9323 __ movq(kScratchRegister, Operand(rdx, -1 * kPointerSize)); // Skip receiver.
9324 __ movq(FieldOperand(rdi, FixedArray::kHeaderSize), kScratchRegister);
9325 __ addq(rdi, Immediate(kPointerSize));
9326 __ subq(rdx, Immediate(kPointerSize));
9327 __ decq(rcx);
9328 __ j(not_zero, &loop);
9329
9330 // Return and remove the on-stack parameters.
9331 __ bind(&done);
9332 __ ret(3 * kPointerSize);
9333
ager@chromium.org5aa501c2009-06-23 07:57:28 +00009334 // Do the runtime call to allocate the arguments object.
9335 __ bind(&runtime);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009336 __ TailCallRuntime(Runtime::kNewArgumentsFast, 3, 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00009337}
9338
ager@chromium.org3e875802009-06-29 08:26:34 +00009339
ager@chromium.org5aa501c2009-06-23 07:57:28 +00009340void ArgumentsAccessStub::GenerateReadElement(MacroAssembler* masm) {
9341 // The key is in rdx and the parameter count is in rax.
9342
9343 // The displacement is used for skipping the frame pointer on the
9344 // stack. It is the offset of the last parameter (if any) relative
9345 // to the frame pointer.
9346 static const int kDisplacement = 1 * kPointerSize;
9347
9348 // Check that the key is a smi.
9349 Label slow;
ager@chromium.org4af710e2009-09-15 12:20:11 +00009350 __ JumpIfNotSmi(rdx, &slow);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00009351
9352 // Check if the calling frame is an arguments adaptor frame.
9353 Label adaptor;
9354 __ movq(rbx, Operand(rbp, StandardFrameConstants::kCallerFPOffset));
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00009355 __ SmiCompare(Operand(rbx, StandardFrameConstants::kContextOffset),
9356 Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR));
ager@chromium.org5aa501c2009-06-23 07:57:28 +00009357 __ j(equal, &adaptor);
9358
9359 // Check index against formal parameters count limit passed in
9360 // through register rax. Use unsigned comparison to get negative
9361 // check for free.
9362 __ cmpq(rdx, rax);
9363 __ j(above_equal, &slow);
9364
9365 // Read the argument from the stack and return it.
ager@chromium.org4af710e2009-09-15 12:20:11 +00009366 SmiIndex index = masm->SmiToIndex(rax, rax, kPointerSizeLog2);
9367 __ lea(rbx, Operand(rbp, index.reg, index.scale, 0));
9368 index = masm->SmiToNegativeIndex(rdx, rdx, kPointerSizeLog2);
9369 __ movq(rax, Operand(rbx, index.reg, index.scale, kDisplacement));
ager@chromium.org5aa501c2009-06-23 07:57:28 +00009370 __ Ret();
9371
9372 // Arguments adaptor case: Check index against actual arguments
9373 // limit found in the arguments adaptor frame. Use unsigned
9374 // comparison to get negative check for free.
9375 __ bind(&adaptor);
9376 __ movq(rcx, Operand(rbx, ArgumentsAdaptorFrameConstants::kLengthOffset));
9377 __ cmpq(rdx, rcx);
9378 __ j(above_equal, &slow);
9379
9380 // Read the argument from the stack and return it.
ager@chromium.org4af710e2009-09-15 12:20:11 +00009381 index = masm->SmiToIndex(rax, rcx, kPointerSizeLog2);
9382 __ lea(rbx, Operand(rbx, index.reg, index.scale, 0));
9383 index = masm->SmiToNegativeIndex(rdx, rdx, kPointerSizeLog2);
9384 __ movq(rax, Operand(rbx, index.reg, index.scale, kDisplacement));
ager@chromium.org5aa501c2009-06-23 07:57:28 +00009385 __ Ret();
9386
9387 // Slow-case: Handle non-smi or out-of-bounds access to arguments
9388 // by calling the runtime system.
9389 __ bind(&slow);
9390 __ pop(rbx); // Return address.
9391 __ push(rdx);
9392 __ push(rbx);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009393 __ TailCallRuntime(Runtime::kGetArgumentsProperty, 1, 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00009394}
9395
9396
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009397void CEntryStub::GenerateThrowTOS(MacroAssembler* masm) {
ager@chromium.org3e875802009-06-29 08:26:34 +00009398 // Check that stack should contain next handler, frame pointer, state and
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009399 // return address in that order.
9400 ASSERT_EQ(StackHandlerConstants::kFPOffset + kPointerSize,
9401 StackHandlerConstants::kStateOffset);
9402 ASSERT_EQ(StackHandlerConstants::kStateOffset + kPointerSize,
9403 StackHandlerConstants::kPCOffset);
9404
9405 ExternalReference handler_address(Top::k_handler_address);
9406 __ movq(kScratchRegister, handler_address);
ager@chromium.org3e875802009-06-29 08:26:34 +00009407 __ movq(rsp, Operand(kScratchRegister, 0));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009408 // get next in chain
ager@chromium.org3e875802009-06-29 08:26:34 +00009409 __ pop(rcx);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009410 __ movq(Operand(kScratchRegister, 0), rcx);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009411 __ pop(rbp); // pop frame pointer
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009412 __ pop(rdx); // remove state
9413
9414 // Before returning we restore the context from the frame pointer if not NULL.
9415 // The frame pointer is NULL in the exception handler of a JS entry frame.
9416 __ xor_(rsi, rsi); // tentatively set context pointer to NULL
9417 Label skip;
9418 __ cmpq(rbp, Immediate(0));
9419 __ j(equal, &skip);
9420 __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
9421 __ bind(&skip);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009422 __ ret(0);
9423}
9424
9425
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009426void CEntryStub::GenerateCore(MacroAssembler* masm,
9427 Label* throw_normal_exception,
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00009428 Label* throw_termination_exception,
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009429 Label* throw_out_of_memory_exception,
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009430 bool do_gc,
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009431 bool always_allocate_scope,
9432 int /* alignment_skew */) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009433 // rax: result parameter for PerformGC, if any.
9434 // rbx: pointer to C function (C callee-saved).
9435 // rbp: frame pointer (restored after C call).
9436 // rsp: stack pointer (restored after C call).
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009437 // r14: number of arguments including receiver (C callee-saved).
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009438 // r15: pointer to the first argument (C callee-saved).
9439 // This pointer is reused in LeaveExitFrame(), so it is stored in a
9440 // callee-saved register.
9441
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009442 // Simple results returned in rax (both AMD64 and Win64 calling conventions).
9443 // Complex results must be written to address passed as first argument.
9444 // AMD64 calling convention: a struct of two pointers in rax+rdx
9445
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009446 // Check stack alignment.
9447 if (FLAG_debug_code) {
9448 __ CheckStackAlignment();
9449 }
9450
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009451 if (do_gc) {
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009452 // Pass failure code returned from last attempt as first argument to
9453 // PerformGC. No need to use PrepareCallCFunction/CallCFunction here as the
9454 // stack is known to be aligned. This function takes one argument which is
9455 // passed in register.
ager@chromium.org96c75b52009-08-26 09:13:16 +00009456#ifdef _WIN64
9457 __ movq(rcx, rax);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009458#else // _WIN64
ager@chromium.org96c75b52009-08-26 09:13:16 +00009459 __ movq(rdi, rax);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009460#endif
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009461 __ movq(kScratchRegister,
9462 FUNCTION_ADDR(Runtime::PerformGC),
9463 RelocInfo::RUNTIME_ENTRY);
9464 __ call(kScratchRegister);
9465 }
9466
9467 ExternalReference scope_depth =
9468 ExternalReference::heap_always_allocate_scope_depth();
9469 if (always_allocate_scope) {
9470 __ movq(kScratchRegister, scope_depth);
9471 __ incl(Operand(kScratchRegister, 0));
9472 }
9473
9474 // Call C function.
ager@chromium.org96c75b52009-08-26 09:13:16 +00009475#ifdef _WIN64
9476 // Windows 64-bit ABI passes arguments in rcx, rdx, r8, r9
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009477 // Store Arguments object on stack, below the 4 WIN64 ABI parameter slots.
9478 __ movq(Operand(rsp, 4 * kPointerSize), r14); // argc.
9479 __ movq(Operand(rsp, 5 * kPointerSize), r15); // argv.
ager@chromium.orga1645e22009-09-09 19:27:10 +00009480 if (result_size_ < 2) {
9481 // Pass a pointer to the Arguments object as the first argument.
9482 // Return result in single register (rax).
9483 __ lea(rcx, Operand(rsp, 4 * kPointerSize));
9484 } else {
9485 ASSERT_EQ(2, result_size_);
9486 // Pass a pointer to the result location as the first argument.
9487 __ lea(rcx, Operand(rsp, 6 * kPointerSize));
9488 // Pass a pointer to the Arguments object as the second argument.
9489 __ lea(rdx, Operand(rsp, 4 * kPointerSize));
9490 }
9491
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009492#else // _WIN64
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009493 // GCC passes arguments in rdi, rsi, rdx, rcx, r8, r9.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009494 __ movq(rdi, r14); // argc.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009495 __ movq(rsi, r15); // argv.
9496#endif
9497 __ call(rbx);
9498 // Result is in rax - do not destroy this register!
9499
9500 if (always_allocate_scope) {
9501 __ movq(kScratchRegister, scope_depth);
9502 __ decl(Operand(kScratchRegister, 0));
9503 }
9504
9505 // Check for failure result.
9506 Label failure_returned;
9507 ASSERT(((kFailureTag + 1) & kFailureTagMask) == 0);
ager@chromium.org3811b432009-10-28 14:53:37 +00009508#ifdef _WIN64
9509 // If return value is on the stack, pop it to registers.
9510 if (result_size_ > 1) {
9511 ASSERT_EQ(2, result_size_);
9512 // Read result values stored on stack. Result is stored
9513 // above the four argument mirror slots and the two
9514 // Arguments object slots.
9515 __ movq(rax, Operand(rsp, 6 * kPointerSize));
9516 __ movq(rdx, Operand(rsp, 7 * kPointerSize));
9517 }
9518#endif
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009519 __ lea(rcx, Operand(rax, 1));
9520 // Lower 2 bits of rcx are 0 iff rax has failure tag.
9521 __ testl(rcx, Immediate(kFailureTagMask));
9522 __ j(zero, &failure_returned);
9523
9524 // Exit the JavaScript to C++ exit frame.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009525 __ LeaveExitFrame(mode_, result_size_);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009526 __ ret(0);
9527
9528 // Handling of failure.
9529 __ bind(&failure_returned);
9530
9531 Label retry;
9532 // If the returned exception is RETRY_AFTER_GC continue at retry label
9533 ASSERT(Failure::RETRY_AFTER_GC == 0);
ager@chromium.org3e875802009-06-29 08:26:34 +00009534 __ testl(rax, Immediate(((1 << kFailureTypeTagSize) - 1) << kFailureTagSize));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009535 __ j(zero, &retry);
9536
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00009537 // Special handling of out of memory exceptions.
9538 __ movq(kScratchRegister, Failure::OutOfMemoryException(), RelocInfo::NONE);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009539 __ cmpq(rax, kScratchRegister);
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00009540 __ j(equal, throw_out_of_memory_exception);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009541
9542 // Retrieve the pending exception and clear the variable.
9543 ExternalReference pending_exception_address(Top::k_pending_exception_address);
9544 __ movq(kScratchRegister, pending_exception_address);
9545 __ movq(rax, Operand(kScratchRegister, 0));
9546 __ movq(rdx, ExternalReference::the_hole_value_location());
9547 __ movq(rdx, Operand(rdx, 0));
9548 __ movq(Operand(kScratchRegister, 0), rdx);
9549
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00009550 // Special handling of termination exceptions which are uncatchable
9551 // by javascript code.
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009552 __ CompareRoot(rax, Heap::kTerminationExceptionRootIndex);
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00009553 __ j(equal, throw_termination_exception);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009554
9555 // Handle normal exception.
9556 __ jmp(throw_normal_exception);
9557
9558 // Retry.
9559 __ bind(&retry);
9560}
9561
9562
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00009563void CEntryStub::GenerateThrowUncatchable(MacroAssembler* masm,
9564 UncatchableExceptionType type) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009565 // Fetch top stack handler.
9566 ExternalReference handler_address(Top::k_handler_address);
9567 __ movq(kScratchRegister, handler_address);
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +00009568 __ movq(rsp, Operand(kScratchRegister, 0));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009569
9570 // Unwind the handlers until the ENTRY handler is found.
9571 Label loop, done;
9572 __ bind(&loop);
9573 // Load the type of the current stack handler.
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00009574 const int kStateOffset = StackHandlerConstants::kStateOffset;
9575 __ cmpq(Operand(rsp, kStateOffset), Immediate(StackHandler::ENTRY));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009576 __ j(equal, &done);
9577 // Fetch the next handler in the list.
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00009578 const int kNextOffset = StackHandlerConstants::kNextOffset;
9579 __ movq(rsp, Operand(rsp, kNextOffset));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009580 __ jmp(&loop);
9581 __ bind(&done);
9582
9583 // Set the top handler address to next handler past the current ENTRY handler.
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00009584 __ movq(kScratchRegister, handler_address);
9585 __ pop(Operand(kScratchRegister, 0));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009586
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00009587 if (type == OUT_OF_MEMORY) {
9588 // Set external caught exception to false.
9589 ExternalReference external_caught(Top::k_external_caught_exception_address);
9590 __ movq(rax, Immediate(false));
9591 __ store_rax(external_caught);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009592
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00009593 // Set pending exception and rax to out of memory exception.
9594 ExternalReference pending_exception(Top::k_pending_exception_address);
9595 __ movq(rax, Failure::OutOfMemoryException(), RelocInfo::NONE);
9596 __ store_rax(pending_exception);
9597 }
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009598
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00009599 // Clear the context pointer.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009600 __ xor_(rsi, rsi);
9601
9602 // Restore registers from handler.
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +00009603 ASSERT_EQ(StackHandlerConstants::kNextOffset + kPointerSize,
9604 StackHandlerConstants::kFPOffset);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009605 __ pop(rbp); // FP
9606 ASSERT_EQ(StackHandlerConstants::kFPOffset + kPointerSize,
9607 StackHandlerConstants::kStateOffset);
9608 __ pop(rdx); // State
9609
9610 ASSERT_EQ(StackHandlerConstants::kStateOffset + kPointerSize,
9611 StackHandlerConstants::kPCOffset);
9612 __ ret(0);
9613}
9614
kasperl@chromium.org71affb52009-05-26 05:44:31 +00009615
ager@chromium.org3e875802009-06-29 08:26:34 +00009616void CallFunctionStub::Generate(MacroAssembler* masm) {
9617 Label slow;
9618
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009619 // If the receiver might be a value (string, number or boolean) check for this
9620 // and box it if it is.
9621 if (ReceiverMightBeValue()) {
9622 // Get the receiver from the stack.
9623 // +1 ~ return address
9624 Label receiver_is_value, receiver_is_js_object;
9625 __ movq(rax, Operand(rsp, (argc_ + 1) * kPointerSize));
9626
9627 // Check if receiver is a smi (which is a number value).
9628 __ JumpIfSmi(rax, &receiver_is_value);
9629
9630 // Check if the receiver is a valid JS object.
9631 __ CmpObjectType(rax, FIRST_JS_OBJECT_TYPE, rdi);
9632 __ j(above_equal, &receiver_is_js_object);
9633
9634 // Call the runtime to box the value.
9635 __ bind(&receiver_is_value);
9636 __ EnterInternalFrame();
9637 __ push(rax);
9638 __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION);
9639 __ LeaveInternalFrame();
9640 __ movq(Operand(rsp, (argc_ + 1) * kPointerSize), rax);
9641
9642 __ bind(&receiver_is_js_object);
9643 }
9644
ager@chromium.org3e875802009-06-29 08:26:34 +00009645 // Get the function to call from the stack.
9646 // +2 ~ receiver, return address
9647 __ movq(rdi, Operand(rsp, (argc_ + 2) * kPointerSize));
9648
9649 // Check that the function really is a JavaScript function.
ager@chromium.org4af710e2009-09-15 12:20:11 +00009650 __ JumpIfSmi(rdi, &slow);
ager@chromium.org3e875802009-06-29 08:26:34 +00009651 // Goto slow case if we do not have a function.
9652 __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rcx);
9653 __ j(not_equal, &slow);
9654
9655 // Fast-case: Just invoke the function.
9656 ParameterCount actual(argc_);
9657 __ InvokeFunction(rdi, actual, JUMP_FUNCTION);
9658
9659 // Slow-case: Non-function called.
9660 __ bind(&slow);
ager@chromium.org5c838252010-02-19 08:53:10 +00009661 // CALL_NON_FUNCTION expects the non-function callee as receiver (instead
9662 // of the original receiver from the call site).
9663 __ movq(Operand(rsp, (argc_ + 1) * kPointerSize), rdi);
ager@chromium.org3e875802009-06-29 08:26:34 +00009664 __ Set(rax, argc_);
9665 __ Set(rbx, 0);
9666 __ GetBuiltinEntry(rdx, Builtins::CALL_NON_FUNCTION);
9667 Handle<Code> adaptor(Builtins::builtin(Builtins::ArgumentsAdaptorTrampoline));
9668 __ Jump(adaptor, RelocInfo::CODE_TARGET);
9669}
9670
9671
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009672void CEntryStub::Generate(MacroAssembler* masm) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009673 // rax: number of arguments including receiver
9674 // rbx: pointer to C function (C callee-saved)
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009675 // rbp: frame pointer of calling JS frame (restored after C call)
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009676 // rsp: stack pointer (restored after C call)
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009677 // rsi: current context (restored)
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009678
9679 // NOTE: Invocations of builtins may return failure objects
9680 // instead of a proper result. The builtin entry handles
9681 // this by performing a garbage collection and retrying the
9682 // builtin once.
9683
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009684 // Enter the exit frame that transitions from JavaScript to C++.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009685 __ EnterExitFrame(mode_, result_size_);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009686
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009687 // rax: Holds the context at this point, but should not be used.
9688 // On entry to code generated by GenerateCore, it must hold
9689 // a failure result if the collect_garbage argument to GenerateCore
9690 // is true. This failure result can be the result of code
9691 // generated by a previous call to GenerateCore. The value
9692 // of rax is then passed to Runtime::PerformGC.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009693 // rbx: pointer to builtin function (C callee-saved).
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009694 // rbp: frame pointer of exit frame (restored after C call).
9695 // rsp: stack pointer (restored after C call).
9696 // r14: number of arguments including receiver (C callee-saved).
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009697 // r15: argv pointer (C callee-saved).
9698
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009699 Label throw_normal_exception;
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00009700 Label throw_termination_exception;
9701 Label throw_out_of_memory_exception;
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009702
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00009703 // Call into the runtime system.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009704 GenerateCore(masm,
9705 &throw_normal_exception,
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00009706 &throw_termination_exception,
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009707 &throw_out_of_memory_exception,
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00009708 false,
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009709 false);
9710
9711 // Do space-specific GC and retry runtime call.
9712 GenerateCore(masm,
9713 &throw_normal_exception,
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00009714 &throw_termination_exception,
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009715 &throw_out_of_memory_exception,
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009716 true,
9717 false);
9718
9719 // Do full GC and retry runtime call one final time.
9720 Failure* failure = Failure::InternalError();
9721 __ movq(rax, failure, RelocInfo::NONE);
9722 GenerateCore(masm,
9723 &throw_normal_exception,
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00009724 &throw_termination_exception,
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009725 &throw_out_of_memory_exception,
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009726 true,
9727 true);
9728
9729 __ bind(&throw_out_of_memory_exception);
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00009730 GenerateThrowUncatchable(masm, OUT_OF_MEMORY);
9731
9732 __ bind(&throw_termination_exception);
9733 GenerateThrowUncatchable(masm, TERMINATION);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009734
9735 __ bind(&throw_normal_exception);
9736 GenerateThrowTOS(masm);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00009737}
9738
ager@chromium.orge2902be2009-06-08 12:21:35 +00009739
ager@chromium.orgc4c92722009-11-18 14:12:51 +00009740void ApiGetterEntryStub::Generate(MacroAssembler* masm) {
9741 UNREACHABLE();
9742}
9743
9744
kasperl@chromium.org71affb52009-05-26 05:44:31 +00009745void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) {
ager@chromium.orge2902be2009-06-08 12:21:35 +00009746 Label invoke, exit;
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +00009747#ifdef ENABLE_LOGGING_AND_PROFILING
9748 Label not_outermost_js, not_outermost_js_2;
9749#endif
ager@chromium.orge2902be2009-06-08 12:21:35 +00009750
9751 // Setup frame.
9752 __ push(rbp);
9753 __ movq(rbp, rsp);
9754
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009755 // Push the stack frame type marker twice.
ager@chromium.orge2902be2009-06-08 12:21:35 +00009756 int marker = is_construct ? StackFrame::ENTRY_CONSTRUCT : StackFrame::ENTRY;
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00009757 __ Push(Smi::FromInt(marker)); // context slot
9758 __ Push(Smi::FromInt(marker)); // function slot
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009759 // Save callee-saved registers (X64 calling conventions).
ager@chromium.orge2902be2009-06-08 12:21:35 +00009760 __ push(r12);
9761 __ push(r13);
9762 __ push(r14);
9763 __ push(r15);
9764 __ push(rdi);
9765 __ push(rsi);
9766 __ push(rbx);
9767 // TODO(X64): Push XMM6-XMM15 (low 64 bits) as well, or make them
9768 // callee-save in JS code as well.
9769
9770 // Save copies of the top frame descriptor on the stack.
9771 ExternalReference c_entry_fp(Top::k_c_entry_fp_address);
9772 __ load_rax(c_entry_fp);
9773 __ push(rax);
9774
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +00009775#ifdef ENABLE_LOGGING_AND_PROFILING
9776 // If this is the outermost JS call, set js_entry_sp value.
9777 ExternalReference js_entry_sp(Top::k_js_entry_sp_address);
9778 __ load_rax(js_entry_sp);
9779 __ testq(rax, rax);
9780 __ j(not_zero, &not_outermost_js);
9781 __ movq(rax, rbp);
9782 __ store_rax(js_entry_sp);
9783 __ bind(&not_outermost_js);
9784#endif
9785
ager@chromium.orge2902be2009-06-08 12:21:35 +00009786 // Call a faked try-block that does the invoke.
9787 __ call(&invoke);
9788
9789 // Caught exception: Store result (exception) in the pending
9790 // exception field in the JSEnv and return a failure sentinel.
9791 ExternalReference pending_exception(Top::k_pending_exception_address);
9792 __ store_rax(pending_exception);
9793 __ movq(rax, Failure::Exception(), RelocInfo::NONE);
9794 __ jmp(&exit);
9795
9796 // Invoke: Link this frame into the handler chain.
9797 __ bind(&invoke);
9798 __ PushTryHandler(IN_JS_ENTRY, JS_ENTRY_HANDLER);
ager@chromium.orge2902be2009-06-08 12:21:35 +00009799
9800 // Clear any pending exceptions.
9801 __ load_rax(ExternalReference::the_hole_value_location());
9802 __ store_rax(pending_exception);
9803
9804 // Fake a receiver (NULL).
9805 __ push(Immediate(0)); // receiver
9806
9807 // Invoke the function by calling through JS entry trampoline
9808 // builtin and pop the faked function when we return. We load the address
9809 // from an external reference instead of inlining the call target address
9810 // directly in the code, because the builtin stubs may not have been
9811 // generated yet at the time this code is generated.
9812 if (is_construct) {
9813 ExternalReference construct_entry(Builtins::JSConstructEntryTrampoline);
9814 __ load_rax(construct_entry);
9815 } else {
9816 ExternalReference entry(Builtins::JSEntryTrampoline);
9817 __ load_rax(entry);
9818 }
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009819 __ lea(kScratchRegister, FieldOperand(rax, Code::kHeaderSize));
9820 __ call(kScratchRegister);
ager@chromium.orge2902be2009-06-08 12:21:35 +00009821
9822 // Unlink this frame from the handler chain.
9823 __ movq(kScratchRegister, ExternalReference(Top::k_handler_address));
9824 __ pop(Operand(kScratchRegister, 0));
9825 // Pop next_sp.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009826 __ addq(rsp, Immediate(StackHandlerConstants::kSize - kPointerSize));
ager@chromium.orge2902be2009-06-08 12:21:35 +00009827
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +00009828#ifdef ENABLE_LOGGING_AND_PROFILING
9829 // If current EBP value is the same as js_entry_sp value, it means that
9830 // the current function is the outermost.
9831 __ movq(kScratchRegister, js_entry_sp);
9832 __ cmpq(rbp, Operand(kScratchRegister, 0));
9833 __ j(not_equal, &not_outermost_js_2);
9834 __ movq(Operand(kScratchRegister, 0), Immediate(0));
9835 __ bind(&not_outermost_js_2);
9836#endif
9837
ager@chromium.orge2902be2009-06-08 12:21:35 +00009838 // Restore the top frame descriptor from the stack.
9839 __ bind(&exit);
9840 __ movq(kScratchRegister, ExternalReference(Top::k_c_entry_fp_address));
9841 __ pop(Operand(kScratchRegister, 0));
9842
9843 // Restore callee-saved registers (X64 conventions).
9844 __ pop(rbx);
9845 __ pop(rsi);
9846 __ pop(rdi);
9847 __ pop(r15);
9848 __ pop(r14);
9849 __ pop(r13);
9850 __ pop(r12);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009851 __ addq(rsp, Immediate(2 * kPointerSize)); // remove markers
ager@chromium.orge2902be2009-06-08 12:21:35 +00009852
9853 // Restore frame pointer and return.
9854 __ pop(rbp);
9855 __ ret(0);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00009856}
9857
ager@chromium.org3e875802009-06-29 08:26:34 +00009858
9859// -----------------------------------------------------------------------------
9860// Implementation of stubs.
9861
9862// Stub classes have public member named masm, not masm_.
9863
9864void StackCheckStub::Generate(MacroAssembler* masm) {
9865 // Because builtins always remove the receiver from the stack, we
9866 // have to fake one to avoid underflowing the stack. The receiver
9867 // must be inserted below the return address on the stack so we
9868 // temporarily store that in a register.
9869 __ pop(rax);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00009870 __ Push(Smi::FromInt(0));
ager@chromium.org3e875802009-06-29 08:26:34 +00009871 __ push(rax);
9872
9873 // Do tail-call to runtime routine.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009874 __ TailCallRuntime(Runtime::kStackGuard, 1, 1);
ager@chromium.org3e875802009-06-29 08:26:34 +00009875}
9876
9877
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00009878void FloatingPointHelper::LoadFloatOperand(MacroAssembler* masm,
9879 Register number) {
9880 Label load_smi, done;
9881
ager@chromium.org4af710e2009-09-15 12:20:11 +00009882 __ JumpIfSmi(number, &load_smi);
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00009883 __ fld_d(FieldOperand(number, HeapNumber::kValueOffset));
9884 __ jmp(&done);
9885
9886 __ bind(&load_smi);
ager@chromium.org4af710e2009-09-15 12:20:11 +00009887 __ SmiToInteger32(number, number);
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00009888 __ push(number);
9889 __ fild_s(Operand(rsp, 0));
9890 __ pop(number);
9891
9892 __ bind(&done);
9893}
9894
ager@chromium.org3e875802009-06-29 08:26:34 +00009895
9896void FloatingPointHelper::LoadFloatOperand(MacroAssembler* masm,
9897 Register src,
9898 XMMRegister dst) {
9899 Label load_smi, done;
9900
ager@chromium.org4af710e2009-09-15 12:20:11 +00009901 __ JumpIfSmi(src, &load_smi);
ager@chromium.org3e875802009-06-29 08:26:34 +00009902 __ movsd(dst, FieldOperand(src, HeapNumber::kValueOffset));
9903 __ jmp(&done);
9904
9905 __ bind(&load_smi);
ager@chromium.org4af710e2009-09-15 12:20:11 +00009906 __ SmiToInteger32(src, src);
ager@chromium.org3e875802009-06-29 08:26:34 +00009907 __ cvtlsi2sd(dst, src);
9908
9909 __ bind(&done);
9910}
9911
9912
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00009913void FloatingPointHelper::LoadFloatOperand(MacroAssembler* masm,
9914 Register src,
9915 XMMRegister dst,
9916 Label* not_number) {
9917 Label load_smi, done;
9918 ASSERT(!src.is(kScratchRegister));
9919 __ JumpIfSmi(src, &load_smi);
9920 __ LoadRoot(kScratchRegister, Heap::kHeapNumberMapRootIndex);
9921 __ cmpq(FieldOperand(src, HeapObject::kMapOffset), kScratchRegister);
9922 __ j(not_equal, not_number);
9923 __ movsd(dst, FieldOperand(src, HeapNumber::kValueOffset));
9924 __ jmp(&done);
9925
9926 __ bind(&load_smi);
9927 __ SmiToInteger32(kScratchRegister, src);
9928 __ cvtlsi2sd(dst, kScratchRegister);
9929
9930 __ bind(&done);
9931}
9932
9933
ager@chromium.org3e875802009-06-29 08:26:34 +00009934void FloatingPointHelper::LoadFloatOperands(MacroAssembler* masm,
9935 XMMRegister dst1,
9936 XMMRegister dst2) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009937 __ movq(kScratchRegister, rdx);
ager@chromium.org3e875802009-06-29 08:26:34 +00009938 LoadFloatOperand(masm, kScratchRegister, dst1);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009939 __ movq(kScratchRegister, rax);
ager@chromium.org3e875802009-06-29 08:26:34 +00009940 LoadFloatOperand(masm, kScratchRegister, dst2);
9941}
9942
9943
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009944void FloatingPointHelper::LoadFloatOperandsFromSmis(MacroAssembler* masm,
9945 XMMRegister dst1,
9946 XMMRegister dst2) {
9947 __ SmiToInteger32(kScratchRegister, rdx);
9948 __ cvtlsi2sd(dst1, kScratchRegister);
9949 __ SmiToInteger32(kScratchRegister, rax);
9950 __ cvtlsi2sd(dst2, kScratchRegister);
ager@chromium.org3e875802009-06-29 08:26:34 +00009951}
9952
9953
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009954// Input: rdx, rax are the left and right objects of a bit op.
9955// Output: rax, rcx are left and right integers for a bit op.
9956void FloatingPointHelper::LoadAsIntegers(MacroAssembler* masm,
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009957 Label* conversion_failure) {
9958 // Check float operands.
9959 Label arg1_is_object, check_undefined_arg1;
9960 Label arg2_is_object, check_undefined_arg2;
9961 Label load_arg2, done;
ager@chromium.org3e875802009-06-29 08:26:34 +00009962
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009963 __ JumpIfNotSmi(rdx, &arg1_is_object);
9964 __ SmiToInteger32(rdx, rdx);
9965 __ jmp(&load_arg2);
9966
9967 // If the argument is undefined it converts to zero (ECMA-262, section 9.5).
9968 __ bind(&check_undefined_arg1);
9969 __ CompareRoot(rdx, Heap::kUndefinedValueRootIndex);
9970 __ j(not_equal, conversion_failure);
9971 __ movl(rdx, Immediate(0));
9972 __ jmp(&load_arg2);
9973
9974 __ bind(&arg1_is_object);
9975 __ movq(rbx, FieldOperand(rdx, HeapObject::kMapOffset));
9976 __ CompareRoot(rbx, Heap::kHeapNumberMapRootIndex);
9977 __ j(not_equal, &check_undefined_arg1);
9978 // Get the untagged integer version of the edx heap number in rcx.
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00009979 IntegerConvert(masm, rdx, rdx);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009980
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00009981 // Here rdx has the untagged integer, rax has a Smi or a heap number.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009982 __ bind(&load_arg2);
9983 // Test if arg2 is a Smi.
9984 __ JumpIfNotSmi(rax, &arg2_is_object);
9985 __ SmiToInteger32(rax, rax);
9986 __ movl(rcx, rax);
ager@chromium.org3e875802009-06-29 08:26:34 +00009987 __ jmp(&done);
9988
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009989 // If the argument is undefined it converts to zero (ECMA-262, section 9.5).
9990 __ bind(&check_undefined_arg2);
9991 __ CompareRoot(rax, Heap::kUndefinedValueRootIndex);
9992 __ j(not_equal, conversion_failure);
9993 __ movl(rcx, Immediate(0));
9994 __ jmp(&done);
ager@chromium.org3e875802009-06-29 08:26:34 +00009995
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009996 __ bind(&arg2_is_object);
9997 __ movq(rbx, FieldOperand(rax, HeapObject::kMapOffset));
9998 __ CompareRoot(rbx, Heap::kHeapNumberMapRootIndex);
9999 __ j(not_equal, &check_undefined_arg2);
10000 // Get the untagged integer version of the eax heap number in ecx.
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +000010001 IntegerConvert(masm, rcx, rax);
ager@chromium.org3e875802009-06-29 08:26:34 +000010002 __ bind(&done);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010003 __ movl(rax, rdx);
ager@chromium.org3e875802009-06-29 08:26:34 +000010004}
10005
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +000010006
ager@chromium.org3e875802009-06-29 08:26:34 +000010007void FloatingPointHelper::LoadFloatOperands(MacroAssembler* masm,
10008 Register lhs,
10009 Register rhs) {
10010 Label load_smi_lhs, load_smi_rhs, done_load_lhs, done;
ager@chromium.org4af710e2009-09-15 12:20:11 +000010011 __ JumpIfSmi(lhs, &load_smi_lhs);
ager@chromium.org3e875802009-06-29 08:26:34 +000010012 __ fld_d(FieldOperand(lhs, HeapNumber::kValueOffset));
10013 __ bind(&done_load_lhs);
10014
ager@chromium.org4af710e2009-09-15 12:20:11 +000010015 __ JumpIfSmi(rhs, &load_smi_rhs);
ager@chromium.org3e875802009-06-29 08:26:34 +000010016 __ fld_d(FieldOperand(rhs, HeapNumber::kValueOffset));
10017 __ jmp(&done);
10018
10019 __ bind(&load_smi_lhs);
ager@chromium.org4af710e2009-09-15 12:20:11 +000010020 __ SmiToInteger64(kScratchRegister, lhs);
ager@chromium.org3e875802009-06-29 08:26:34 +000010021 __ push(kScratchRegister);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010022 __ fild_d(Operand(rsp, 0));
ager@chromium.org3e875802009-06-29 08:26:34 +000010023 __ pop(kScratchRegister);
10024 __ jmp(&done_load_lhs);
10025
10026 __ bind(&load_smi_rhs);
ager@chromium.org4af710e2009-09-15 12:20:11 +000010027 __ SmiToInteger64(kScratchRegister, rhs);
ager@chromium.org3e875802009-06-29 08:26:34 +000010028 __ push(kScratchRegister);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010029 __ fild_d(Operand(rsp, 0));
ager@chromium.org3e875802009-06-29 08:26:34 +000010030 __ pop(kScratchRegister);
10031
10032 __ bind(&done);
10033}
10034
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +000010035
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +000010036void FloatingPointHelper::CheckNumberOperands(MacroAssembler* masm,
10037 Label* non_float) {
ager@chromium.org3e875802009-06-29 08:26:34 +000010038 Label test_other, done;
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010039 // Test if both operands are numbers (heap_numbers or smis).
10040 // If not, jump to label non_float.
ager@chromium.org4af710e2009-09-15 12:20:11 +000010041 __ JumpIfSmi(rdx, &test_other); // argument in rdx is OK
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010042 __ Cmp(FieldOperand(rdx, HeapObject::kMapOffset), Factory::heap_number_map());
10043 __ j(not_equal, non_float); // The argument in rdx is not a number.
ager@chromium.org3e875802009-06-29 08:26:34 +000010044
10045 __ bind(&test_other);
ager@chromium.org4af710e2009-09-15 12:20:11 +000010046 __ JumpIfSmi(rax, &done); // argument in rax is OK
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010047 __ Cmp(FieldOperand(rax, HeapObject::kMapOffset), Factory::heap_number_map());
10048 __ j(not_equal, non_float); // The argument in rax is not a number.
ager@chromium.org3e875802009-06-29 08:26:34 +000010049
10050 // Fall-through: Both operands are numbers.
10051 __ bind(&done);
10052}
10053
10054
10055const char* GenericBinaryOpStub::GetName() {
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +000010056 if (name_ != NULL) return name_;
10057 const int len = 100;
10058 name_ = Bootstrapper::AllocateAutoDeletedArray(len);
10059 if (name_ == NULL) return "OOM";
10060 const char* op_name = Token::Name(op_);
10061 const char* overwrite_name;
10062 switch (mode_) {
10063 case NO_OVERWRITE: overwrite_name = "Alloc"; break;
10064 case OVERWRITE_RIGHT: overwrite_name = "OverwriteRight"; break;
10065 case OVERWRITE_LEFT: overwrite_name = "OverwriteLeft"; break;
10066 default: overwrite_name = "UnknownOverwrite"; break;
ager@chromium.org3e875802009-06-29 08:26:34 +000010067 }
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +000010068
10069 OS::SNPrintF(Vector<char>(name_, len),
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +000010070 "GenericBinaryOpStub_%s_%s%s_%s%s_%s_%s",
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +000010071 op_name,
10072 overwrite_name,
10073 (flags_ & NO_SMI_CODE_IN_STUB) ? "_NoSmiInStub" : "",
10074 args_in_registers_ ? "RegArgs" : "StackArgs",
10075 args_reversed_ ? "_R" : "",
whesse@chromium.orgcec079d2010-03-22 14:44:04 +000010076 static_operands_type_.ToString(),
10077 BinaryOpIC::GetName(runtime_operands_type_));
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +000010078 return name_;
ager@chromium.org3e875802009-06-29 08:26:34 +000010079}
10080
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +000010081
ager@chromium.org3811b432009-10-28 14:53:37 +000010082void GenericBinaryOpStub::GenerateCall(
10083 MacroAssembler* masm,
10084 Register left,
10085 Register right) {
10086 if (!ArgsInRegistersSupported()) {
10087 // Pass arguments on the stack.
10088 __ push(left);
10089 __ push(right);
10090 } else {
10091 // The calling convention with registers is left in rdx and right in rax.
10092 Register left_arg = rdx;
10093 Register right_arg = rax;
10094 if (!(left.is(left_arg) && right.is(right_arg))) {
10095 if (left.is(right_arg) && right.is(left_arg)) {
10096 if (IsOperationCommutative()) {
10097 SetArgsReversed();
10098 } else {
10099 __ xchg(left, right);
10100 }
10101 } else if (left.is(left_arg)) {
10102 __ movq(right_arg, right);
ager@chromium.org951093b2010-02-11 12:59:04 +000010103 } else if (right.is(right_arg)) {
10104 __ movq(left_arg, left);
ager@chromium.org3811b432009-10-28 14:53:37 +000010105 } else if (left.is(right_arg)) {
10106 if (IsOperationCommutative()) {
10107 __ movq(left_arg, right);
10108 SetArgsReversed();
10109 } else {
10110 // Order of moves important to avoid destroying left argument.
10111 __ movq(left_arg, left);
10112 __ movq(right_arg, right);
10113 }
10114 } else if (right.is(left_arg)) {
10115 if (IsOperationCommutative()) {
10116 __ movq(right_arg, left);
10117 SetArgsReversed();
10118 } else {
10119 // Order of moves important to avoid destroying right argument.
10120 __ movq(right_arg, right);
10121 __ movq(left_arg, left);
10122 }
ager@chromium.org3811b432009-10-28 14:53:37 +000010123 } else {
10124 // Order of moves is not important.
10125 __ movq(left_arg, left);
10126 __ movq(right_arg, right);
10127 }
10128 }
10129
10130 // Update flags to indicate that arguments are in registers.
10131 SetArgsInRegisters();
10132 __ IncrementCounter(&Counters::generic_binary_stub_calls_regs, 1);
10133 }
10134
10135 // Call the stub.
10136 __ CallStub(this);
10137}
10138
10139
10140void GenericBinaryOpStub::GenerateCall(
10141 MacroAssembler* masm,
10142 Register left,
10143 Smi* right) {
10144 if (!ArgsInRegistersSupported()) {
10145 // Pass arguments on the stack.
10146 __ push(left);
10147 __ Push(right);
10148 } else {
10149 // The calling convention with registers is left in rdx and right in rax.
10150 Register left_arg = rdx;
10151 Register right_arg = rax;
10152 if (left.is(left_arg)) {
10153 __ Move(right_arg, right);
10154 } else if (left.is(right_arg) && IsOperationCommutative()) {
10155 __ Move(left_arg, right);
10156 SetArgsReversed();
10157 } else {
ager@chromium.org951093b2010-02-11 12:59:04 +000010158 // For non-commutative operations, left and right_arg might be
10159 // the same register. Therefore, the order of the moves is
10160 // important here in order to not overwrite left before moving
10161 // it to left_arg.
ager@chromium.org3811b432009-10-28 14:53:37 +000010162 __ movq(left_arg, left);
10163 __ Move(right_arg, right);
10164 }
10165
10166 // Update flags to indicate that arguments are in registers.
10167 SetArgsInRegisters();
10168 __ IncrementCounter(&Counters::generic_binary_stub_calls_regs, 1);
10169 }
10170
10171 // Call the stub.
10172 __ CallStub(this);
10173}
10174
10175
10176void GenericBinaryOpStub::GenerateCall(
10177 MacroAssembler* masm,
10178 Smi* left,
10179 Register right) {
10180 if (!ArgsInRegistersSupported()) {
10181 // Pass arguments on the stack.
10182 __ Push(left);
10183 __ push(right);
10184 } else {
10185 // The calling convention with registers is left in rdx and right in rax.
10186 Register left_arg = rdx;
10187 Register right_arg = rax;
10188 if (right.is(right_arg)) {
10189 __ Move(left_arg, left);
10190 } else if (right.is(left_arg) && IsOperationCommutative()) {
10191 __ Move(right_arg, left);
10192 SetArgsReversed();
10193 } else {
ager@chromium.org951093b2010-02-11 12:59:04 +000010194 // For non-commutative operations, right and left_arg might be
10195 // the same register. Therefore, the order of the moves is
10196 // important here in order to not overwrite right before moving
10197 // it to right_arg.
ager@chromium.org3811b432009-10-28 14:53:37 +000010198 __ movq(right_arg, right);
ager@chromium.org951093b2010-02-11 12:59:04 +000010199 __ Move(left_arg, left);
ager@chromium.org3811b432009-10-28 14:53:37 +000010200 }
10201 // Update flags to indicate that arguments are in registers.
10202 SetArgsInRegisters();
10203 __ IncrementCounter(&Counters::generic_binary_stub_calls_regs, 1);
10204 }
10205
10206 // Call the stub.
10207 __ CallStub(this);
10208}
10209
10210
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010211Result GenericBinaryOpStub::GenerateCall(MacroAssembler* masm,
10212 VirtualFrame* frame,
10213 Result* left,
10214 Result* right) {
10215 if (ArgsInRegistersSupported()) {
10216 SetArgsInRegisters();
10217 return frame->CallStub(this, left, right);
10218 } else {
10219 frame->Push(left);
10220 frame->Push(right);
10221 return frame->CallStub(this, 2);
10222 }
10223}
10224
10225
ager@chromium.org3e875802009-06-29 08:26:34 +000010226void GenericBinaryOpStub::GenerateSmiCode(MacroAssembler* masm, Label* slow) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +000010227 // 1. Move arguments into rdx, rax except for DIV and MOD, which need the
10228 // dividend in rax and rdx free for the division. Use rax, rbx for those.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010229 Comment load_comment(masm, "-- Load arguments");
10230 Register left = rdx;
10231 Register right = rax;
10232 if (op_ == Token::DIV || op_ == Token::MOD) {
10233 left = rax;
10234 right = rbx;
10235 if (HasArgsInRegisters()) {
10236 __ movq(rbx, rax);
10237 __ movq(rax, rdx);
10238 }
10239 }
10240 if (!HasArgsInRegisters()) {
10241 __ movq(right, Operand(rsp, 1 * kPointerSize));
10242 __ movq(left, Operand(rsp, 2 * kPointerSize));
10243 }
ager@chromium.org3e875802009-06-29 08:26:34 +000010244
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010245 Label not_smis;
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +000010246 // 2. Smi check both operands.
10247 if (static_operands_type_.IsSmi()) {
10248 // Skip smi check if we know that both arguments are smis.
10249 if (FLAG_debug_code) {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +000010250 __ AbortIfNotSmi(left);
10251 __ AbortIfNotSmi(right);
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +000010252 }
10253 if (op_ == Token::BIT_OR) {
10254 // Handle OR here, since we do extra smi-checking in the or code below.
10255 __ SmiOr(right, right, left);
10256 GenerateReturn(masm);
10257 return;
10258 }
10259 } else {
10260 if (op_ != Token::BIT_OR) {
10261 // Skip the check for OR as it is better combined with the
10262 // actual operation.
10263 Comment smi_check_comment(masm, "-- Smi check arguments");
10264 __ JumpIfNotBothSmi(left, right, &not_smis);
10265 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010266 }
ager@chromium.org3e875802009-06-29 08:26:34 +000010267
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010268 // 3. Operands are both smis (except for OR), perform the operation leaving
10269 // the result in rax and check the result if necessary.
10270 Comment perform_smi(masm, "-- Perform smi operation");
10271 Label use_fp_on_smis;
ager@chromium.org3e875802009-06-29 08:26:34 +000010272 switch (op_) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010273 case Token::ADD: {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010274 ASSERT(right.is(rax));
10275 __ SmiAdd(right, right, left, &use_fp_on_smis); // ADD is commutative.
ager@chromium.org3e875802009-06-29 08:26:34 +000010276 break;
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010277 }
10278
10279 case Token::SUB: {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010280 __ SmiSub(left, left, right, &use_fp_on_smis);
10281 __ movq(rax, left);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010282 break;
10283 }
ager@chromium.org3e875802009-06-29 08:26:34 +000010284
10285 case Token::MUL:
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010286 ASSERT(right.is(rax));
10287 __ SmiMul(right, right, left, &use_fp_on_smis); // MUL is commutative.
ager@chromium.org3e875802009-06-29 08:26:34 +000010288 break;
10289
10290 case Token::DIV:
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010291 ASSERT(left.is(rax));
10292 __ SmiDiv(left, left, right, &use_fp_on_smis);
ager@chromium.org3e875802009-06-29 08:26:34 +000010293 break;
10294
10295 case Token::MOD:
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010296 ASSERT(left.is(rax));
10297 __ SmiMod(left, left, right, slow);
ager@chromium.org3e875802009-06-29 08:26:34 +000010298 break;
10299
10300 case Token::BIT_OR:
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010301 ASSERT(right.is(rax));
10302 __ movq(rcx, right); // Save the right operand.
10303 __ SmiOr(right, right, left); // BIT_OR is commutative.
10304 __ testb(right, Immediate(kSmiTagMask));
10305 __ j(not_zero, &not_smis);
ager@chromium.org3e875802009-06-29 08:26:34 +000010306 break;
10307
10308 case Token::BIT_AND:
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010309 ASSERT(right.is(rax));
10310 __ SmiAnd(right, right, left); // BIT_AND is commutative.
ager@chromium.org3e875802009-06-29 08:26:34 +000010311 break;
10312
10313 case Token::BIT_XOR:
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010314 ASSERT(right.is(rax));
10315 __ SmiXor(right, right, left); // BIT_XOR is commutative.
ager@chromium.org3e875802009-06-29 08:26:34 +000010316 break;
10317
10318 case Token::SHL:
10319 case Token::SHR:
10320 case Token::SAR:
ager@chromium.org3e875802009-06-29 08:26:34 +000010321 switch (op_) {
10322 case Token::SAR:
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010323 __ SmiShiftArithmeticRight(left, left, right);
ager@chromium.org3e875802009-06-29 08:26:34 +000010324 break;
10325 case Token::SHR:
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010326 __ SmiShiftLogicalRight(left, left, right, slow);
ager@chromium.org3e875802009-06-29 08:26:34 +000010327 break;
10328 case Token::SHL:
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +000010329 __ SmiShiftLeft(left, left, right);
ager@chromium.org3e875802009-06-29 08:26:34 +000010330 break;
10331 default:
10332 UNREACHABLE();
10333 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010334 __ movq(rax, left);
ager@chromium.org3e875802009-06-29 08:26:34 +000010335 break;
10336
10337 default:
10338 UNREACHABLE();
10339 break;
10340 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010341
whesse@chromium.orgcec079d2010-03-22 14:44:04 +000010342 // 4. Emit return of result in rax.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010343 GenerateReturn(masm);
10344
10345 // 5. For some operations emit inline code to perform floating point
10346 // operations on known smis (e.g., if the result of the operation
10347 // overflowed the smi range).
10348 switch (op_) {
10349 case Token::ADD:
10350 case Token::SUB:
10351 case Token::MUL:
10352 case Token::DIV: {
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +000010353 ASSERT(use_fp_on_smis.is_linked());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010354 __ bind(&use_fp_on_smis);
10355 if (op_ == Token::DIV) {
10356 __ movq(rdx, rax);
10357 __ movq(rax, rbx);
10358 }
10359 // left is rdx, right is rax.
10360 __ AllocateHeapNumber(rbx, rcx, slow);
10361 FloatingPointHelper::LoadFloatOperandsFromSmis(masm, xmm4, xmm5);
10362 switch (op_) {
10363 case Token::ADD: __ addsd(xmm4, xmm5); break;
10364 case Token::SUB: __ subsd(xmm4, xmm5); break;
10365 case Token::MUL: __ mulsd(xmm4, xmm5); break;
10366 case Token::DIV: __ divsd(xmm4, xmm5); break;
10367 default: UNREACHABLE();
10368 }
10369 __ movsd(FieldOperand(rbx, HeapNumber::kValueOffset), xmm4);
10370 __ movq(rax, rbx);
10371 GenerateReturn(masm);
10372 }
10373 default:
10374 break;
10375 }
10376
10377 // 6. Non-smi operands, fall out to the non-smi code with the operands in
10378 // rdx and rax.
10379 Comment done_comment(masm, "-- Enter non-smi code");
10380 __ bind(&not_smis);
10381
10382 switch (op_) {
10383 case Token::DIV:
10384 case Token::MOD:
10385 // Operands are in rax, rbx at this point.
10386 __ movq(rdx, rax);
10387 __ movq(rax, rbx);
10388 break;
10389
10390 case Token::BIT_OR:
10391 // Right operand is saved in rcx and rax was destroyed by the smi
10392 // operation.
10393 __ movq(rax, rcx);
10394 break;
10395
10396 default:
10397 break;
10398 }
ager@chromium.org3e875802009-06-29 08:26:34 +000010399}
10400
10401
10402void GenericBinaryOpStub::Generate(MacroAssembler* masm) {
10403 Label call_runtime;
whesse@chromium.orgcec079d2010-03-22 14:44:04 +000010404
10405 if (ShouldGenerateSmiCode()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010406 GenerateSmiCode(masm, &call_runtime);
10407 } else if (op_ != Token::MOD) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +000010408 if (!HasArgsInRegisters()) {
10409 GenerateLoadArguments(masm);
10410 }
ager@chromium.org3e875802009-06-29 08:26:34 +000010411 }
ager@chromium.org3e875802009-06-29 08:26:34 +000010412 // Floating point case.
whesse@chromium.orgcec079d2010-03-22 14:44:04 +000010413 if (ShouldGenerateFPCode()) {
10414 switch (op_) {
10415 case Token::ADD:
10416 case Token::SUB:
10417 case Token::MUL:
10418 case Token::DIV: {
10419 if (runtime_operands_type_ == BinaryOpIC::DEFAULT &&
10420 HasSmiCodeInStub()) {
10421 // Execution reaches this point when the first non-smi argument occurs
10422 // (and only if smi code is generated). This is the right moment to
10423 // patch to HEAP_NUMBERS state. The transition is attempted only for
10424 // the four basic operations. The stub stays in the DEFAULT state
10425 // forever for all other operations (also if smi code is skipped).
10426 GenerateTypeTransition(masm);
10427 }
10428
10429 Label not_floats;
10430 // rax: y
10431 // rdx: x
ager@chromium.orgac091b72010-05-05 07:34:42 +000010432 if (static_operands_type_.IsNumber() && FLAG_debug_code) {
10433 // Assert at runtime that inputs are only numbers.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +000010434 __ AbortIfNotNumber(rdx);
10435 __ AbortIfNotNumber(rax);
ager@chromium.org5c838252010-02-19 08:53:10 +000010436 } else {
10437 FloatingPointHelper::CheckNumberOperands(masm, &call_runtime);
10438 }
whesse@chromium.orgcec079d2010-03-22 14:44:04 +000010439 // Fast-case: Both operands are numbers.
10440 // xmm4 and xmm5 are volatile XMM registers.
10441 FloatingPointHelper::LoadFloatOperands(masm, xmm4, xmm5);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010442
whesse@chromium.orgcec079d2010-03-22 14:44:04 +000010443 switch (op_) {
10444 case Token::ADD: __ addsd(xmm4, xmm5); break;
10445 case Token::SUB: __ subsd(xmm4, xmm5); break;
10446 case Token::MUL: __ mulsd(xmm4, xmm5); break;
10447 case Token::DIV: __ divsd(xmm4, xmm5); break;
10448 default: UNREACHABLE();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010449 }
whesse@chromium.orgcec079d2010-03-22 14:44:04 +000010450 // Allocate a heap number, if needed.
10451 Label skip_allocation;
10452 OverwriteMode mode = mode_;
10453 if (HasArgsReversed()) {
10454 if (mode == OVERWRITE_RIGHT) {
10455 mode = OVERWRITE_LEFT;
10456 } else if (mode == OVERWRITE_LEFT) {
10457 mode = OVERWRITE_RIGHT;
10458 }
10459 }
10460 switch (mode) {
ager@chromium.org3e875802009-06-29 08:26:34 +000010461 case OVERWRITE_LEFT:
whesse@chromium.orgcec079d2010-03-22 14:44:04 +000010462 __ JumpIfNotSmi(rdx, &skip_allocation);
10463 __ AllocateHeapNumber(rbx, rcx, &call_runtime);
10464 __ movq(rdx, rbx);
10465 __ bind(&skip_allocation);
10466 __ movq(rax, rdx);
10467 break;
ager@chromium.org3e875802009-06-29 08:26:34 +000010468 case OVERWRITE_RIGHT:
whesse@chromium.orgcec079d2010-03-22 14:44:04 +000010469 // If the argument in rax is already an object, we skip the
ager@chromium.org3e875802009-06-29 08:26:34 +000010470 // allocation of a heap number.
ager@chromium.org4af710e2009-09-15 12:20:11 +000010471 __ JumpIfNotSmi(rax, &skip_allocation);
ager@chromium.org3e875802009-06-29 08:26:34 +000010472 // Fall through!
10473 case NO_OVERWRITE:
whesse@chromium.orgcec079d2010-03-22 14:44:04 +000010474 // Allocate a heap number for the result. Keep rax and rdx intact
10475 // for the possible runtime call.
10476 __ AllocateHeapNumber(rbx, rcx, &call_runtime);
10477 __ movq(rax, rbx);
ager@chromium.org3e875802009-06-29 08:26:34 +000010478 __ bind(&skip_allocation);
10479 break;
10480 default: UNREACHABLE();
10481 }
whesse@chromium.orgcec079d2010-03-22 14:44:04 +000010482 __ movsd(FieldOperand(rax, HeapNumber::kValueOffset), xmm4);
ager@chromium.org3811b432009-10-28 14:53:37 +000010483 GenerateReturn(masm);
whesse@chromium.orgcec079d2010-03-22 14:44:04 +000010484 __ bind(&not_floats);
10485 if (runtime_operands_type_ == BinaryOpIC::DEFAULT &&
10486 !HasSmiCodeInStub()) {
10487 // Execution reaches this point when the first non-number argument
10488 // occurs (and only if smi code is skipped from the stub, otherwise
10489 // the patching has already been done earlier in this case branch).
10490 // A perfect moment to try patching to STRINGS for ADD operation.
10491 if (op_ == Token::ADD) {
10492 GenerateTypeTransition(masm);
10493 }
10494 }
10495 break;
ager@chromium.org3e875802009-06-29 08:26:34 +000010496 }
whesse@chromium.orgcec079d2010-03-22 14:44:04 +000010497 case Token::MOD: {
10498 // For MOD we go directly to runtime in the non-smi case.
10499 break;
10500 }
10501 case Token::BIT_OR:
10502 case Token::BIT_AND:
10503 case Token::BIT_XOR:
10504 case Token::SAR:
10505 case Token::SHL:
10506 case Token::SHR: {
10507 Label skip_allocation, non_smi_result;
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +000010508 FloatingPointHelper::LoadAsIntegers(masm, &call_runtime);
whesse@chromium.orgcec079d2010-03-22 14:44:04 +000010509 switch (op_) {
10510 case Token::BIT_OR: __ orl(rax, rcx); break;
10511 case Token::BIT_AND: __ andl(rax, rcx); break;
10512 case Token::BIT_XOR: __ xorl(rax, rcx); break;
10513 case Token::SAR: __ sarl_cl(rax); break;
10514 case Token::SHL: __ shll_cl(rax); break;
10515 case Token::SHR: __ shrl_cl(rax); break;
10516 default: UNREACHABLE();
10517 }
10518 if (op_ == Token::SHR) {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +000010519 // Check if result is negative. This can only happen for a shift
whesse@chromium.orgcec079d2010-03-22 14:44:04 +000010520 // by zero, which also doesn't update the sign flag.
10521 __ testl(rax, rax);
10522 __ j(negative, &non_smi_result);
10523 }
10524 __ JumpIfNotValidSmiValue(rax, &non_smi_result);
10525 // Tag smi result, if possible, and return.
10526 __ Integer32ToSmi(rax, rax);
10527 GenerateReturn(masm);
ager@chromium.org3e875802009-06-29 08:26:34 +000010528
whesse@chromium.orgcec079d2010-03-22 14:44:04 +000010529 // All ops except SHR return a signed int32 that we load in
10530 // a HeapNumber.
10531 if (op_ != Token::SHR && non_smi_result.is_linked()) {
10532 __ bind(&non_smi_result);
10533 // Allocate a heap number if needed.
10534 __ movsxlq(rbx, rax); // rbx: sign extended 32-bit result
10535 switch (mode_) {
10536 case OVERWRITE_LEFT:
10537 case OVERWRITE_RIGHT:
10538 // If the operand was an object, we skip the
10539 // allocation of a heap number.
10540 __ movq(rax, Operand(rsp, mode_ == OVERWRITE_RIGHT ?
10541 1 * kPointerSize : 2 * kPointerSize));
10542 __ JumpIfNotSmi(rax, &skip_allocation);
10543 // Fall through!
10544 case NO_OVERWRITE:
10545 __ AllocateHeapNumber(rax, rcx, &call_runtime);
10546 __ bind(&skip_allocation);
10547 break;
10548 default: UNREACHABLE();
10549 }
10550 // Store the result in the HeapNumber and return.
10551 __ movq(Operand(rsp, 1 * kPointerSize), rbx);
10552 __ fild_s(Operand(rsp, 1 * kPointerSize));
10553 __ fstp_d(FieldOperand(rax, HeapNumber::kValueOffset));
10554 GenerateReturn(masm);
10555 }
10556
10557 // SHR should return uint32 - go to runtime for non-smi/negative result.
10558 if (op_ == Token::SHR) {
10559 __ bind(&non_smi_result);
10560 }
10561 break;
ager@chromium.org3e875802009-06-29 08:26:34 +000010562 }
whesse@chromium.orgcec079d2010-03-22 14:44:04 +000010563 default: UNREACHABLE(); break;
ager@chromium.org3e875802009-06-29 08:26:34 +000010564 }
ager@chromium.org3e875802009-06-29 08:26:34 +000010565 }
10566
10567 // If all else fails, use the runtime system to get the correct
ager@chromium.org3811b432009-10-28 14:53:37 +000010568 // result. If arguments was passed in registers now place them on the
10569 // stack in the correct order below the return address.
ager@chromium.org3e875802009-06-29 08:26:34 +000010570 __ bind(&call_runtime);
fschneider@chromium.org086aac62010-03-17 13:18:24 +000010571
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010572 if (HasArgsInRegisters()) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +000010573 GenerateRegisterArgsPush(masm);
ager@chromium.org3811b432009-10-28 14:53:37 +000010574 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +000010575
ager@chromium.org3e875802009-06-29 08:26:34 +000010576 switch (op_) {
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010577 case Token::ADD: {
fschneider@chromium.org086aac62010-03-17 13:18:24 +000010578 // Registers containing left and right operands respectively.
10579 Register lhs, rhs;
10580
10581 if (HasArgsReversed()) {
10582 lhs = rax;
10583 rhs = rdx;
10584 } else {
10585 lhs = rdx;
10586 rhs = rax;
10587 }
10588
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010589 // Test for string arguments before calling runtime.
fschneider@chromium.org086aac62010-03-17 13:18:24 +000010590 Label not_strings, both_strings, not_string1, string1, string1_smi2;
whesse@chromium.orgcec079d2010-03-22 14:44:04 +000010591
10592 // If this stub has already generated FP-specific code then the arguments
10593 // are already in rdx, rax
10594 if (!ShouldGenerateFPCode() && !HasArgsInRegisters()) {
10595 GenerateLoadArguments(masm);
10596 }
10597
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010598 Condition is_smi;
fschneider@chromium.org086aac62010-03-17 13:18:24 +000010599 is_smi = masm->CheckSmi(lhs);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010600 __ j(is_smi, &not_string1);
fschneider@chromium.org086aac62010-03-17 13:18:24 +000010601 __ CmpObjectType(lhs, FIRST_NONSTRING_TYPE, r8);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010602 __ j(above_equal, &not_string1);
10603
10604 // First argument is a a string, test second.
fschneider@chromium.org086aac62010-03-17 13:18:24 +000010605 is_smi = masm->CheckSmi(rhs);
10606 __ j(is_smi, &string1_smi2);
10607 __ CmpObjectType(rhs, FIRST_NONSTRING_TYPE, r9);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010608 __ j(above_equal, &string1);
10609
10610 // First and second argument are strings.
fschneider@chromium.org086aac62010-03-17 13:18:24 +000010611 StringAddStub string_add_stub(NO_STRING_CHECK_IN_STUB);
10612 __ TailCallStub(&string_add_stub);
10613
10614 __ bind(&string1_smi2);
10615 // First argument is a string, second is a smi. Try to lookup the number
10616 // string for the smi in the number string cache.
10617 NumberToStringStub::GenerateLookupNumberStringCache(
10618 masm, rhs, rbx, rcx, r8, true, &string1);
10619
10620 // Replace second argument on stack and tailcall string add stub to make
10621 // the result.
10622 __ movq(Operand(rsp, 1 * kPointerSize), rbx);
10623 __ TailCallStub(&string_add_stub);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010624
10625 // Only first argument is a string.
10626 __ bind(&string1);
fschneider@chromium.org086aac62010-03-17 13:18:24 +000010627 __ InvokeBuiltin(Builtins::STRING_ADD_LEFT, JUMP_FUNCTION);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010628
10629 // First argument was not a string, test second.
10630 __ bind(&not_string1);
fschneider@chromium.org086aac62010-03-17 13:18:24 +000010631 is_smi = masm->CheckSmi(rhs);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010632 __ j(is_smi, &not_strings);
fschneider@chromium.org086aac62010-03-17 13:18:24 +000010633 __ CmpObjectType(rhs, FIRST_NONSTRING_TYPE, rhs);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010634 __ j(above_equal, &not_strings);
10635
10636 // Only second argument is a string.
fschneider@chromium.org086aac62010-03-17 13:18:24 +000010637 __ InvokeBuiltin(Builtins::STRING_ADD_RIGHT, JUMP_FUNCTION);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010638
10639 __ bind(&not_strings);
10640 // Neither argument is a string.
ager@chromium.org3e875802009-06-29 08:26:34 +000010641 __ InvokeBuiltin(Builtins::ADD, JUMP_FUNCTION);
10642 break;
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010643 }
ager@chromium.org3e875802009-06-29 08:26:34 +000010644 case Token::SUB:
10645 __ InvokeBuiltin(Builtins::SUB, JUMP_FUNCTION);
10646 break;
10647 case Token::MUL:
10648 __ InvokeBuiltin(Builtins::MUL, JUMP_FUNCTION);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010649 break;
ager@chromium.org3e875802009-06-29 08:26:34 +000010650 case Token::DIV:
10651 __ InvokeBuiltin(Builtins::DIV, JUMP_FUNCTION);
10652 break;
10653 case Token::MOD:
10654 __ InvokeBuiltin(Builtins::MOD, JUMP_FUNCTION);
10655 break;
10656 case Token::BIT_OR:
10657 __ InvokeBuiltin(Builtins::BIT_OR, JUMP_FUNCTION);
10658 break;
10659 case Token::BIT_AND:
10660 __ InvokeBuiltin(Builtins::BIT_AND, JUMP_FUNCTION);
10661 break;
10662 case Token::BIT_XOR:
10663 __ InvokeBuiltin(Builtins::BIT_XOR, JUMP_FUNCTION);
10664 break;
10665 case Token::SAR:
10666 __ InvokeBuiltin(Builtins::SAR, JUMP_FUNCTION);
10667 break;
10668 case Token::SHL:
10669 __ InvokeBuiltin(Builtins::SHL, JUMP_FUNCTION);
10670 break;
10671 case Token::SHR:
10672 __ InvokeBuiltin(Builtins::SHR, JUMP_FUNCTION);
10673 break;
10674 default:
10675 UNREACHABLE();
10676 }
10677}
10678
10679
ager@chromium.org3811b432009-10-28 14:53:37 +000010680void GenericBinaryOpStub::GenerateLoadArguments(MacroAssembler* masm) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +000010681 ASSERT(!HasArgsInRegisters());
10682 __ movq(rax, Operand(rsp, 1 * kPointerSize));
10683 __ movq(rdx, Operand(rsp, 2 * kPointerSize));
ager@chromium.org3811b432009-10-28 14:53:37 +000010684}
10685
10686
10687void GenericBinaryOpStub::GenerateReturn(MacroAssembler* masm) {
10688 // If arguments are not passed in registers remove them from the stack before
10689 // returning.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010690 if (!HasArgsInRegisters()) {
ager@chromium.org3811b432009-10-28 14:53:37 +000010691 __ ret(2 * kPointerSize); // Remove both operands
10692 } else {
10693 __ ret(0);
10694 }
10695}
10696
10697
whesse@chromium.orgcec079d2010-03-22 14:44:04 +000010698void GenericBinaryOpStub::GenerateRegisterArgsPush(MacroAssembler* masm) {
10699 ASSERT(HasArgsInRegisters());
10700 __ pop(rcx);
10701 if (HasArgsReversed()) {
10702 __ push(rax);
10703 __ push(rdx);
10704 } else {
10705 __ push(rdx);
10706 __ push(rax);
10707 }
10708 __ push(rcx);
10709}
10710
10711
10712void GenericBinaryOpStub::GenerateTypeTransition(MacroAssembler* masm) {
10713 Label get_result;
10714
10715 // Keep a copy of operands on the stack and make sure they are also in
10716 // rdx, rax.
10717 if (HasArgsInRegisters()) {
10718 GenerateRegisterArgsPush(masm);
10719 } else {
10720 GenerateLoadArguments(masm);
10721 }
10722
10723 // Internal frame is necessary to handle exceptions properly.
10724 __ EnterInternalFrame();
10725
10726 // Push arguments on stack if the stub expects them there.
10727 if (!HasArgsInRegisters()) {
10728 __ push(rdx);
10729 __ push(rax);
10730 }
10731 // Call the stub proper to get the result in rax.
10732 __ call(&get_result);
10733 __ LeaveInternalFrame();
10734
10735 // Left and right arguments are already on stack.
10736 __ pop(rcx);
10737 // Push the operation result. The tail call to BinaryOp_Patch will
10738 // return it to the original caller..
10739 __ push(rax);
10740
10741 // Push this stub's key.
10742 __ movq(rax, Immediate(MinorKey()));
10743 __ Integer32ToSmi(rax, rax);
10744 __ push(rax);
10745
10746 // Although the operation and the type info are encoded into the key,
10747 // the encoding is opaque, so push them too.
10748 __ movq(rax, Immediate(op_));
10749 __ Integer32ToSmi(rax, rax);
10750 __ push(rax);
10751
10752 __ movq(rax, Immediate(runtime_operands_type_));
10753 __ Integer32ToSmi(rax, rax);
10754 __ push(rax);
10755
10756 __ push(rcx);
10757
10758 // Perform patching to an appropriate fast case and return the result.
10759 __ TailCallExternalReference(
10760 ExternalReference(IC_Utility(IC::kBinaryOp_Patch)),
10761 6,
10762 1);
10763
10764 // The entry point for the result calculation is assumed to be immediately
10765 // after this sequence.
10766 __ bind(&get_result);
10767}
10768
10769
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010770Handle<Code> GetBinaryOpStub(int key, BinaryOpIC::TypeInfo type_info) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +000010771 GenericBinaryOpStub stub(key, type_info);
10772 return stub.GetCode();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010773}
10774
10775
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010776int CompareStub::MinorKey() {
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +000010777 // Encode the three parameters in a unique 16 bit value. To avoid duplicate
10778 // stubs the never NaN NaN condition is only taken into account if the
10779 // condition is equals.
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +000010780 ASSERT(static_cast<unsigned>(cc_) < (1 << 13));
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +000010781 return ConditionField::encode(static_cast<unsigned>(cc_))
10782 | StrictField::encode(strict_)
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +000010783 | NeverNanNanField::encode(cc_ == equal ? never_nan_nan_ : false)
10784 | IncludeNumberCompareField::encode(include_number_compare_);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000010785}
10786
10787
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +000010788// Unfortunately you have to run without snapshots to see most of these
10789// names in the profile since most compare stubs end up in the snapshot.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000010790const char* CompareStub::GetName() {
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +000010791 if (name_ != NULL) return name_;
10792 const int kMaxNameLength = 100;
10793 name_ = Bootstrapper::AllocateAutoDeletedArray(kMaxNameLength);
10794 if (name_ == NULL) return "OOM";
10795
10796 const char* cc_name;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000010797 switch (cc_) {
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +000010798 case less: cc_name = "LT"; break;
10799 case greater: cc_name = "GT"; break;
10800 case less_equal: cc_name = "LE"; break;
10801 case greater_equal: cc_name = "GE"; break;
10802 case equal: cc_name = "EQ"; break;
10803 case not_equal: cc_name = "NE"; break;
10804 default: cc_name = "UnknownCondition"; break;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000010805 }
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +000010806
10807 const char* strict_name = "";
10808 if (strict_ && (cc_ == equal || cc_ == not_equal)) {
10809 strict_name = "_STRICT";
10810 }
10811
10812 const char* never_nan_nan_name = "";
10813 if (never_nan_nan_ && (cc_ == equal || cc_ == not_equal)) {
10814 never_nan_nan_name = "_NO_NAN";
10815 }
10816
10817 const char* include_number_compare_name = "";
10818 if (!include_number_compare_) {
10819 include_number_compare_name = "_NO_NUMBER";
10820 }
10821
10822 OS::SNPrintF(Vector<char>(name_, kMaxNameLength),
10823 "CompareStub_%s%s%s%s",
10824 cc_name,
10825 strict_name,
10826 never_nan_nan_name,
10827 include_number_compare_name);
10828 return name_;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010829}
10830
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +000010831
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010832// -------------------------------------------------------------------------
10833// StringCharCodeAtGenerator
10834
10835void StringCharCodeAtGenerator::GenerateFast(MacroAssembler* masm) {
10836 Label flat_string;
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010837 Label ascii_string;
10838 Label got_char_code;
10839
10840 // If the receiver is a smi trigger the non-string case.
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010841 __ JumpIfSmi(object_, receiver_not_string_);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010842
10843 // Fetch the instance type of the receiver into result register.
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010844 __ movq(result_, FieldOperand(object_, HeapObject::kMapOffset));
10845 __ movzxbl(result_, FieldOperand(result_, Map::kInstanceTypeOffset));
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010846 // If the receiver is not a string trigger the non-string case.
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010847 __ testb(result_, Immediate(kIsNotStringMask));
10848 __ j(not_zero, receiver_not_string_);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010849
10850 // If the index is non-smi trigger the non-smi case.
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010851 __ JumpIfNotSmi(index_, &index_not_smi_);
10852
10853 // Put smi-tagged index into scratch register.
10854 __ movq(scratch_, index_);
10855 __ bind(&got_smi_index_);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010856
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010857 // Check for index out of range.
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010858 __ SmiCompare(scratch_, FieldOperand(object_, String::kLengthOffset));
10859 __ j(above_equal, index_out_of_range_);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010860
10861 // We need special handling for non-flat strings.
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010862 ASSERT(kSeqStringTag == 0);
10863 __ testb(result_, Immediate(kStringRepresentationMask));
10864 __ j(zero, &flat_string);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010865
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010866 // Handle non-flat strings.
10867 __ testb(result_, Immediate(kIsConsStringMask));
10868 __ j(zero, &call_runtime_);
10869
10870 // ConsString.
10871 // Check whether the right hand side is the empty string (i.e. if
10872 // this is really a flat string in a cons string). If that is not
10873 // the case we would rather go to the runtime system now to flatten
10874 // the string.
10875 __ CompareRoot(FieldOperand(object_, ConsString::kSecondOffset),
10876 Heap::kEmptyStringRootIndex);
10877 __ j(not_equal, &call_runtime_);
10878 // Get the first of the two strings and load its instance type.
10879 __ movq(object_, FieldOperand(object_, ConsString::kFirstOffset));
10880 __ movq(result_, FieldOperand(object_, HeapObject::kMapOffset));
10881 __ movzxbl(result_, FieldOperand(result_, Map::kInstanceTypeOffset));
10882 // If the first cons component is also non-flat, then go to runtime.
10883 ASSERT(kSeqStringTag == 0);
10884 __ testb(result_, Immediate(kStringRepresentationMask));
10885 __ j(not_zero, &call_runtime_);
ager@chromium.orgac091b72010-05-05 07:34:42 +000010886
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010887 // Check for 1-byte or 2-byte string.
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010888 __ bind(&flat_string);
10889 ASSERT(kAsciiStringTag != 0);
10890 __ testb(result_, Immediate(kStringEncodingMask));
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010891 __ j(not_zero, &ascii_string);
10892
10893 // 2-byte string.
10894 // Load the 2-byte character code into the result register.
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010895 __ SmiToInteger32(scratch_, scratch_);
10896 __ movzxwl(result_, FieldOperand(object_,
10897 scratch_, times_2,
10898 SeqTwoByteString::kHeaderSize));
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010899 __ jmp(&got_char_code);
10900
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010901 // ASCII string.
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010902 // Load the byte into the result register.
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010903 __ bind(&ascii_string);
10904 __ SmiToInteger32(scratch_, scratch_);
10905 __ movzxbl(result_, FieldOperand(object_,
10906 scratch_, times_1,
10907 SeqAsciiString::kHeaderSize));
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010908 __ bind(&got_char_code);
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010909 __ Integer32ToSmi(result_, result_);
10910 __ bind(&exit_);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010911}
10912
10913
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010914void StringCharCodeAtGenerator::GenerateSlow(
10915 MacroAssembler* masm, const RuntimeCallHelper& call_helper) {
10916 __ Abort("Unexpected fallthrough to CharCodeAt slow case");
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010917
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010918 // Index is not a smi.
10919 __ bind(&index_not_smi_);
10920 // If index is a heap number, try converting it to an integer.
10921 __ CheckMap(index_, Factory::heap_number_map(), index_not_number_, true);
10922 call_helper.BeforeCall(masm);
10923 __ push(object_);
10924 __ push(index_);
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010925 __ push(index_); // Consumed by runtime conversion function.
10926 if (index_flags_ == STRING_INDEX_IS_NUMBER) {
10927 __ CallRuntime(Runtime::kNumberToIntegerMapMinusZero, 1);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010928 } else {
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010929 ASSERT(index_flags_ == STRING_INDEX_IS_ARRAY_INDEX);
10930 // NumberToSmi discards numbers that are not exact integers.
10931 __ CallRuntime(Runtime::kNumberToSmi, 1);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010932 }
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010933 if (!scratch_.is(rax)) {
10934 // Save the conversion result before the pop instructions below
10935 // have a chance to overwrite it.
10936 __ movq(scratch_, rax);
10937 }
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010938 __ pop(index_);
10939 __ pop(object_);
sgjesse@chromium.org82dbbab2010-06-02 08:57:44 +000010940 // Reload the instance type.
10941 __ movq(result_, FieldOperand(object_, HeapObject::kMapOffset));
10942 __ movzxbl(result_, FieldOperand(result_, Map::kInstanceTypeOffset));
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010943 call_helper.AfterCall(masm);
10944 // If index is still not a smi, it must be out of range.
10945 __ JumpIfNotSmi(scratch_, index_out_of_range_);
10946 // Otherwise, return to the fast path.
10947 __ jmp(&got_smi_index_);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010948
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010949 // Call runtime. We get here when the receiver is a string and the
10950 // index is a number, but the code of getting the actual character
10951 // is too complex (e.g., when the string needs to be flattened).
10952 __ bind(&call_runtime_);
10953 call_helper.BeforeCall(masm);
10954 __ push(object_);
10955 __ push(index_);
10956 __ CallRuntime(Runtime::kStringCharCodeAt, 2);
10957 if (!result_.is(rax)) {
10958 __ movq(result_, rax);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000010959 }
ricow@chromium.org30ce4112010-05-31 10:38:25 +000010960 call_helper.AfterCall(masm);
10961 __ jmp(&exit_);
10962
10963 __ Abort("Unexpected fallthrough from CharCodeAt slow case");
10964}
10965
10966
10967// -------------------------------------------------------------------------
10968// StringCharFromCodeGenerator
10969
10970void StringCharFromCodeGenerator::GenerateFast(MacroAssembler* masm) {
10971 // Fast case of Heap::LookupSingleCharacterStringFromCode.
10972 __ JumpIfNotSmi(code_, &slow_case_);
10973 __ SmiCompare(code_, Smi::FromInt(String::kMaxAsciiCharCode));
10974 __ j(above, &slow_case_);
10975
10976 __ LoadRoot(result_, Heap::kSingleCharacterStringCacheRootIndex);
10977 SmiIndex index = masm->SmiToIndex(kScratchRegister, code_, kPointerSizeLog2);
10978 __ movq(result_, FieldOperand(result_, index.reg, index.scale,
10979 FixedArray::kHeaderSize));
10980 __ CompareRoot(result_, Heap::kUndefinedValueRootIndex);
10981 __ j(equal, &slow_case_);
10982 __ bind(&exit_);
10983}
10984
10985
10986void StringCharFromCodeGenerator::GenerateSlow(
10987 MacroAssembler* masm, const RuntimeCallHelper& call_helper) {
10988 __ Abort("Unexpected fallthrough to CharFromCode slow case");
10989
10990 __ bind(&slow_case_);
10991 call_helper.BeforeCall(masm);
10992 __ push(code_);
10993 __ CallRuntime(Runtime::kCharFromCode, 1);
10994 if (!result_.is(rax)) {
10995 __ movq(result_, rax);
10996 }
10997 call_helper.AfterCall(masm);
10998 __ jmp(&exit_);
10999
11000 __ Abort("Unexpected fallthrough from CharFromCode slow case");
11001}
11002
11003
11004// -------------------------------------------------------------------------
11005// StringCharAtGenerator
11006
11007void StringCharAtGenerator::GenerateFast(MacroAssembler* masm) {
11008 char_code_at_generator_.GenerateFast(masm);
11009 char_from_code_generator_.GenerateFast(masm);
11010}
11011
11012
11013void StringCharAtGenerator::GenerateSlow(
11014 MacroAssembler* masm, const RuntimeCallHelper& call_helper) {
11015 char_code_at_generator_.GenerateSlow(masm, call_helper);
11016 char_from_code_generator_.GenerateSlow(masm, call_helper);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000011017}
11018
11019
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +000011020void StringAddStub::Generate(MacroAssembler* masm) {
11021 Label string_add_runtime;
11022
11023 // Load the two arguments.
11024 __ movq(rax, Operand(rsp, 2 * kPointerSize)); // First argument.
11025 __ movq(rdx, Operand(rsp, 1 * kPointerSize)); // Second argument.
11026
11027 // Make sure that both arguments are strings if not known in advance.
11028 if (string_check_) {
11029 Condition is_smi;
11030 is_smi = masm->CheckSmi(rax);
11031 __ j(is_smi, &string_add_runtime);
11032 __ CmpObjectType(rax, FIRST_NONSTRING_TYPE, r8);
11033 __ j(above_equal, &string_add_runtime);
11034
11035 // First argument is a a string, test second.
11036 is_smi = masm->CheckSmi(rdx);
11037 __ j(is_smi, &string_add_runtime);
11038 __ CmpObjectType(rdx, FIRST_NONSTRING_TYPE, r9);
11039 __ j(above_equal, &string_add_runtime);
11040 }
11041
11042 // Both arguments are strings.
11043 // rax: first string
11044 // rdx: second string
11045 // Check if either of the strings are empty. In that case return the other.
11046 Label second_not_zero_length, both_not_zero_length;
ager@chromium.orgac091b72010-05-05 07:34:42 +000011047 __ movq(rcx, FieldOperand(rdx, String::kLengthOffset));
11048 __ SmiTest(rcx);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +000011049 __ j(not_zero, &second_not_zero_length);
11050 // Second string is empty, result is first string which is already in rax.
11051 __ IncrementCounter(&Counters::string_add_native, 1);
11052 __ ret(2 * kPointerSize);
11053 __ bind(&second_not_zero_length);
ager@chromium.orgac091b72010-05-05 07:34:42 +000011054 __ movq(rbx, FieldOperand(rax, String::kLengthOffset));
11055 __ SmiTest(rbx);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +000011056 __ j(not_zero, &both_not_zero_length);
11057 // First string is empty, result is second string which is in rdx.
11058 __ movq(rax, rdx);
11059 __ IncrementCounter(&Counters::string_add_native, 1);
11060 __ ret(2 * kPointerSize);
11061
11062 // Both strings are non-empty.
11063 // rax: first string
11064 // rbx: length of first string
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000011065 // rcx: length of second string
11066 // rdx: second string
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000011067 // r8: map of first string if string check was performed above
11068 // r9: map of second string if string check was performed above
11069 Label string_add_flat_result, longer_than_two;
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +000011070 __ bind(&both_not_zero_length);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000011071
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +000011072 // If arguments where known to be strings, maps are not loaded to r8 and r9
11073 // by the code above.
11074 if (!string_check_) {
11075 __ movq(r8, FieldOperand(rax, HeapObject::kMapOffset));
11076 __ movq(r9, FieldOperand(rdx, HeapObject::kMapOffset));
11077 }
11078 // Get the instance types of the two strings as they will be needed soon.
11079 __ movzxbl(r8, FieldOperand(r8, Map::kInstanceTypeOffset));
11080 __ movzxbl(r9, FieldOperand(r9, Map::kInstanceTypeOffset));
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000011081
11082 // Look at the length of the result of adding the two strings.
ager@chromium.orgac091b72010-05-05 07:34:42 +000011083 ASSERT(String::kMaxLength <= Smi::kMaxValue / 2);
11084 __ SmiAdd(rbx, rbx, rcx, NULL);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000011085 // Use the runtime system when adding two one character strings, as it
11086 // contains optimizations for this specific case using the symbol table.
ager@chromium.orgac091b72010-05-05 07:34:42 +000011087 __ SmiCompare(rbx, Smi::FromInt(2));
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000011088 __ j(not_equal, &longer_than_two);
11089
11090 // Check that both strings are non-external ascii strings.
11091 __ JumpIfBothInstanceTypesAreNotSequentialAscii(r8, r9, rbx, rcx,
11092 &string_add_runtime);
11093
11094 // Get the two characters forming the sub string.
11095 __ movzxbq(rbx, FieldOperand(rax, SeqAsciiString::kHeaderSize));
11096 __ movzxbq(rcx, FieldOperand(rdx, SeqAsciiString::kHeaderSize));
11097
11098 // Try to lookup two character string in symbol table. If it is not found
11099 // just allocate a new one.
11100 Label make_two_character_string, make_flat_ascii_string;
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000011101 StringHelper::GenerateTwoCharacterSymbolTableProbe(
11102 masm, rbx, rcx, r14, r12, rdi, r15, &make_two_character_string);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000011103 __ IncrementCounter(&Counters::string_add_native, 1);
11104 __ ret(2 * kPointerSize);
11105
11106 __ bind(&make_two_character_string);
11107 __ Set(rbx, 2);
11108 __ jmp(&make_flat_ascii_string);
11109
11110 __ bind(&longer_than_two);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +000011111 // Check if resulting string will be flat.
ager@chromium.orgac091b72010-05-05 07:34:42 +000011112 __ SmiCompare(rbx, Smi::FromInt(String::kMinNonFlatLength));
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +000011113 __ j(below, &string_add_flat_result);
11114 // Handle exceptionally long strings in the runtime system.
11115 ASSERT((String::kMaxLength & 0x80000000) == 0);
ager@chromium.orgac091b72010-05-05 07:34:42 +000011116 __ SmiCompare(rbx, Smi::FromInt(String::kMaxLength));
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +000011117 __ j(above, &string_add_runtime);
11118
11119 // If result is not supposed to be flat, allocate a cons string object. If
11120 // both strings are ascii the result is an ascii cons string.
11121 // rax: first string
11122 // ebx: length of resulting flat string
11123 // rdx: second string
11124 // r8: instance type of first string
11125 // r9: instance type of second string
11126 Label non_ascii, allocated;
11127 __ movl(rcx, r8);
11128 __ and_(rcx, r9);
11129 ASSERT(kStringEncodingMask == kAsciiStringTag);
11130 __ testl(rcx, Immediate(kAsciiStringTag));
11131 __ j(zero, &non_ascii);
11132 // Allocate an acsii cons string.
11133 __ AllocateAsciiConsString(rcx, rdi, no_reg, &string_add_runtime);
11134 __ bind(&allocated);
11135 // Fill the fields of the cons string.
ager@chromium.orgac091b72010-05-05 07:34:42 +000011136 __ movq(FieldOperand(rcx, ConsString::kLengthOffset), rbx);
ricow@chromium.org30ce4112010-05-31 10:38:25 +000011137 __ movq(FieldOperand(rcx, ConsString::kHashFieldOffset),
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +000011138 Immediate(String::kEmptyHashField));
11139 __ movq(FieldOperand(rcx, ConsString::kFirstOffset), rax);
11140 __ movq(FieldOperand(rcx, ConsString::kSecondOffset), rdx);
11141 __ movq(rax, rcx);
11142 __ IncrementCounter(&Counters::string_add_native, 1);
11143 __ ret(2 * kPointerSize);
11144 __ bind(&non_ascii);
11145 // Allocate a two byte cons string.
11146 __ AllocateConsString(rcx, rdi, no_reg, &string_add_runtime);
11147 __ jmp(&allocated);
11148
11149 // Handle creating a flat result. First check that both strings are not
11150 // external strings.
11151 // rax: first string
ager@chromium.orgac091b72010-05-05 07:34:42 +000011152 // ebx: length of resulting flat string as smi
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +000011153 // rdx: second string
11154 // r8: instance type of first string
11155 // r9: instance type of first string
11156 __ bind(&string_add_flat_result);
ager@chromium.orgac091b72010-05-05 07:34:42 +000011157 __ SmiToInteger32(rbx, rbx);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +000011158 __ movl(rcx, r8);
11159 __ and_(rcx, Immediate(kStringRepresentationMask));
11160 __ cmpl(rcx, Immediate(kExternalStringTag));
11161 __ j(equal, &string_add_runtime);
11162 __ movl(rcx, r9);
11163 __ and_(rcx, Immediate(kStringRepresentationMask));
11164 __ cmpl(rcx, Immediate(kExternalStringTag));
11165 __ j(equal, &string_add_runtime);
11166 // Now check if both strings are ascii strings.
11167 // rax: first string
11168 // ebx: length of resulting flat string
11169 // rdx: second string
11170 // r8: instance type of first string
11171 // r9: instance type of second string
11172 Label non_ascii_string_add_flat_result;
11173 ASSERT(kStringEncodingMask == kAsciiStringTag);
11174 __ testl(r8, Immediate(kAsciiStringTag));
11175 __ j(zero, &non_ascii_string_add_flat_result);
11176 __ testl(r9, Immediate(kAsciiStringTag));
11177 __ j(zero, &string_add_runtime);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000011178
11179 __ bind(&make_flat_ascii_string);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +000011180 // Both strings are ascii strings. As they are short they are both flat.
11181 __ AllocateAsciiString(rcx, rbx, rdi, r14, r15, &string_add_runtime);
11182 // rcx: result string
11183 __ movq(rbx, rcx);
11184 // Locate first character of result.
11185 __ addq(rcx, Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag));
11186 // Locate first character of first argument
ricow@chromium.org30ce4112010-05-31 10:38:25 +000011187 __ SmiToInteger32(rdi, FieldOperand(rax, String::kLengthOffset));
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +000011188 __ addq(rax, Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag));
11189 // rax: first char of first argument
11190 // rbx: result string
11191 // rcx: first character of result
11192 // rdx: second string
11193 // rdi: length of first argument
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000011194 StringHelper::GenerateCopyCharacters(masm, rcx, rax, rdi, true);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +000011195 // Locate first character of second argument.
ricow@chromium.org30ce4112010-05-31 10:38:25 +000011196 __ SmiToInteger32(rdi, FieldOperand(rdx, String::kLengthOffset));
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +000011197 __ addq(rdx, Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag));
11198 // rbx: result string
11199 // rcx: next character of result
11200 // rdx: first char of second argument
11201 // rdi: length of second argument
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000011202 StringHelper::GenerateCopyCharacters(masm, rcx, rdx, rdi, true);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +000011203 __ movq(rax, rbx);
11204 __ IncrementCounter(&Counters::string_add_native, 1);
11205 __ ret(2 * kPointerSize);
11206
11207 // Handle creating a flat two byte result.
11208 // rax: first string - known to be two byte
11209 // rbx: length of resulting flat string
11210 // rdx: second string
11211 // r8: instance type of first string
11212 // r9: instance type of first string
11213 __ bind(&non_ascii_string_add_flat_result);
11214 __ and_(r9, Immediate(kAsciiStringTag));
11215 __ j(not_zero, &string_add_runtime);
11216 // Both strings are two byte strings. As they are short they are both
11217 // flat.
11218 __ AllocateTwoByteString(rcx, rbx, rdi, r14, r15, &string_add_runtime);
11219 // rcx: result string
11220 __ movq(rbx, rcx);
11221 // Locate first character of result.
11222 __ addq(rcx, Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
11223 // Locate first character of first argument.
ricow@chromium.org30ce4112010-05-31 10:38:25 +000011224 __ SmiToInteger32(rdi, FieldOperand(rax, String::kLengthOffset));
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +000011225 __ addq(rax, Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
11226 // rax: first char of first argument
11227 // rbx: result string
11228 // rcx: first character of result
11229 // rdx: second argument
11230 // rdi: length of first argument
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000011231 StringHelper::GenerateCopyCharacters(masm, rcx, rax, rdi, false);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +000011232 // Locate first character of second argument.
ricow@chromium.org30ce4112010-05-31 10:38:25 +000011233 __ SmiToInteger32(rdi, FieldOperand(rdx, String::kLengthOffset));
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +000011234 __ addq(rdx, Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
11235 // rbx: result string
11236 // rcx: next character of result
11237 // rdx: first char of second argument
11238 // rdi: length of second argument
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000011239 StringHelper::GenerateCopyCharacters(masm, rcx, rdx, rdi, false);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +000011240 __ movq(rax, rbx);
11241 __ IncrementCounter(&Counters::string_add_native, 1);
11242 __ ret(2 * kPointerSize);
11243
11244 // Just jump to runtime to add the two strings.
11245 __ bind(&string_add_runtime);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000011246 __ TailCallRuntime(Runtime::kStringAdd, 2, 1);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +000011247}
11248
11249
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000011250void StringHelper::GenerateCopyCharacters(MacroAssembler* masm,
11251 Register dest,
11252 Register src,
11253 Register count,
11254 bool ascii) {
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +000011255 Label loop;
11256 __ bind(&loop);
11257 // This loop just copies one character at a time, as it is only used for very
11258 // short strings.
11259 if (ascii) {
11260 __ movb(kScratchRegister, Operand(src, 0));
11261 __ movb(Operand(dest, 0), kScratchRegister);
ricow@chromium.org30ce4112010-05-31 10:38:25 +000011262 __ incq(src);
11263 __ incq(dest);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +000011264 } else {
11265 __ movzxwl(kScratchRegister, Operand(src, 0));
11266 __ movw(Operand(dest, 0), kScratchRegister);
11267 __ addq(src, Immediate(2));
11268 __ addq(dest, Immediate(2));
11269 }
ricow@chromium.org30ce4112010-05-31 10:38:25 +000011270 __ decl(count);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +000011271 __ j(not_zero, &loop);
11272}
11273
11274
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000011275void StringHelper::GenerateCopyCharactersREP(MacroAssembler* masm,
11276 Register dest,
11277 Register src,
11278 Register count,
11279 bool ascii) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000011280 // Copy characters using rep movs of doublewords. Align destination on 4 byte
11281 // boundary before starting rep movs. Copy remaining characters after running
11282 // rep movs.
ricow@chromium.org30ce4112010-05-31 10:38:25 +000011283 // Count is positive int32, dest and src are character pointers.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000011284 ASSERT(dest.is(rdi)); // rep movs destination
11285 ASSERT(src.is(rsi)); // rep movs source
11286 ASSERT(count.is(rcx)); // rep movs count
11287
11288 // Nothing to do for zero characters.
11289 Label done;
ricow@chromium.org30ce4112010-05-31 10:38:25 +000011290 __ testl(count, count);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000011291 __ j(zero, &done);
11292
11293 // Make count the number of bytes to copy.
11294 if (!ascii) {
11295 ASSERT_EQ(2, sizeof(uc16)); // NOLINT
ricow@chromium.org30ce4112010-05-31 10:38:25 +000011296 __ addl(count, count);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000011297 }
11298
11299 // Don't enter the rep movs if there are less than 4 bytes to copy.
11300 Label last_bytes;
ricow@chromium.org30ce4112010-05-31 10:38:25 +000011301 __ testl(count, Immediate(~7));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000011302 __ j(zero, &last_bytes);
11303
11304 // Copy from edi to esi using rep movs instruction.
ricow@chromium.org30ce4112010-05-31 10:38:25 +000011305 __ movl(kScratchRegister, count);
11306 __ shr(count, Immediate(3)); // Number of doublewords to copy.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000011307 __ repmovsq();
11308
11309 // Find number of bytes left.
ricow@chromium.org30ce4112010-05-31 10:38:25 +000011310 __ movl(count, kScratchRegister);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000011311 __ and_(count, Immediate(7));
11312
11313 // Check if there are more bytes to copy.
11314 __ bind(&last_bytes);
ricow@chromium.org30ce4112010-05-31 10:38:25 +000011315 __ testl(count, count);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000011316 __ j(zero, &done);
11317
11318 // Copy remaining characters.
11319 Label loop;
11320 __ bind(&loop);
11321 __ movb(kScratchRegister, Operand(src, 0));
11322 __ movb(Operand(dest, 0), kScratchRegister);
ricow@chromium.org30ce4112010-05-31 10:38:25 +000011323 __ incq(src);
11324 __ incq(dest);
11325 __ decl(count);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000011326 __ j(not_zero, &loop);
11327
11328 __ bind(&done);
11329}
11330
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000011331void StringHelper::GenerateTwoCharacterSymbolTableProbe(MacroAssembler* masm,
11332 Register c1,
11333 Register c2,
11334 Register scratch1,
11335 Register scratch2,
11336 Register scratch3,
11337 Register scratch4,
11338 Label* not_found) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000011339 // Register scratch3 is the general scratch register in this function.
11340 Register scratch = scratch3;
11341
11342 // Make sure that both characters are not digits as such strings has a
11343 // different hash algorithm. Don't try to look for these in the symbol table.
11344 Label not_array_index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +000011345 __ leal(scratch, Operand(c1, -'0'));
11346 __ cmpl(scratch, Immediate(static_cast<int>('9' - '0')));
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000011347 __ j(above, &not_array_index);
ricow@chromium.org30ce4112010-05-31 10:38:25 +000011348 __ leal(scratch, Operand(c2, -'0'));
11349 __ cmpl(scratch, Immediate(static_cast<int>('9' - '0')));
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000011350 __ j(below_equal, not_found);
11351
11352 __ bind(&not_array_index);
11353 // Calculate the two character string hash.
11354 Register hash = scratch1;
11355 GenerateHashInit(masm, hash, c1, scratch);
11356 GenerateHashAddCharacter(masm, hash, c2, scratch);
11357 GenerateHashGetHash(masm, hash, scratch);
11358
11359 // Collect the two characters in a register.
11360 Register chars = c1;
11361 __ shl(c2, Immediate(kBitsPerByte));
11362 __ orl(chars, c2);
11363
11364 // chars: two character string, char 1 in byte 0 and char 2 in byte 1.
11365 // hash: hash of two character string.
11366
11367 // Load the symbol table.
11368 Register symbol_table = c2;
11369 __ LoadRoot(symbol_table, Heap::kSymbolTableRootIndex);
11370
11371 // Calculate capacity mask from the symbol table capacity.
11372 Register mask = scratch2;
ricow@chromium.org30ce4112010-05-31 10:38:25 +000011373 __ SmiToInteger32(mask,
11374 FieldOperand(symbol_table, SymbolTable::kCapacityOffset));
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000011375 __ decl(mask);
11376
11377 Register undefined = scratch4;
11378 __ LoadRoot(undefined, Heap::kUndefinedValueRootIndex);
11379
11380 // Registers
11381 // chars: two character string, char 1 in byte 0 and char 2 in byte 1.
11382 // hash: hash of two character string (32-bit int)
11383 // symbol_table: symbol table
11384 // mask: capacity mask (32-bit int)
11385 // undefined: undefined value
11386 // scratch: -
11387
11388 // Perform a number of probes in the symbol table.
11389 static const int kProbes = 4;
11390 Label found_in_symbol_table;
11391 Label next_probe[kProbes];
11392 for (int i = 0; i < kProbes; i++) {
11393 // Calculate entry in symbol table.
11394 __ movl(scratch, hash);
11395 if (i > 0) {
11396 __ addl(scratch, Immediate(SymbolTable::GetProbeOffset(i)));
11397 }
11398 __ andl(scratch, mask);
11399
11400 // Load the entry from the symble table.
11401 Register candidate = scratch; // Scratch register contains candidate.
11402 ASSERT_EQ(1, SymbolTable::kEntrySize);
11403 __ movq(candidate,
ricow@chromium.org30ce4112010-05-31 10:38:25 +000011404 FieldOperand(symbol_table,
11405 scratch,
11406 times_pointer_size,
11407 SymbolTable::kElementsStartOffset));
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000011408
11409 // If entry is undefined no string with this hash can be found.
11410 __ cmpq(candidate, undefined);
11411 __ j(equal, not_found);
11412
11413 // If length is not 2 the string is not a candidate.
ager@chromium.orgac091b72010-05-05 07:34:42 +000011414 __ SmiCompare(FieldOperand(candidate, String::kLengthOffset),
11415 Smi::FromInt(2));
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000011416 __ j(not_equal, &next_probe[i]);
11417
11418 // We use kScratchRegister as a temporary register in assumption that
11419 // JumpIfInstanceTypeIsNotSequentialAscii does not use it implicitly
11420 Register temp = kScratchRegister;
11421
11422 // Check that the candidate is a non-external ascii string.
11423 __ movq(temp, FieldOperand(candidate, HeapObject::kMapOffset));
11424 __ movzxbl(temp, FieldOperand(temp, Map::kInstanceTypeOffset));
11425 __ JumpIfInstanceTypeIsNotSequentialAscii(
11426 temp, temp, &next_probe[i]);
11427
11428 // Check if the two characters match.
11429 __ movl(temp, FieldOperand(candidate, SeqAsciiString::kHeaderSize));
11430 __ andl(temp, Immediate(0x0000ffff));
11431 __ cmpl(chars, temp);
11432 __ j(equal, &found_in_symbol_table);
11433 __ bind(&next_probe[i]);
11434 }
11435
11436 // No matching 2 character string found by probing.
11437 __ jmp(not_found);
11438
11439 // Scratch register contains result when we fall through to here.
11440 Register result = scratch;
11441 __ bind(&found_in_symbol_table);
11442 if (!result.is(rax)) {
11443 __ movq(rax, result);
11444 }
11445}
11446
11447
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000011448void StringHelper::GenerateHashInit(MacroAssembler* masm,
11449 Register hash,
11450 Register character,
11451 Register scratch) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000011452 // hash = character + (character << 10);
11453 __ movl(hash, character);
11454 __ shll(hash, Immediate(10));
11455 __ addl(hash, character);
11456 // hash ^= hash >> 6;
11457 __ movl(scratch, hash);
11458 __ sarl(scratch, Immediate(6));
11459 __ xorl(hash, scratch);
11460}
11461
11462
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000011463void StringHelper::GenerateHashAddCharacter(MacroAssembler* masm,
11464 Register hash,
11465 Register character,
11466 Register scratch) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000011467 // hash += character;
11468 __ addl(hash, character);
11469 // hash += hash << 10;
11470 __ movl(scratch, hash);
11471 __ shll(scratch, Immediate(10));
11472 __ addl(hash, scratch);
11473 // hash ^= hash >> 6;
11474 __ movl(scratch, hash);
11475 __ sarl(scratch, Immediate(6));
11476 __ xorl(hash, scratch);
11477}
11478
11479
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000011480void StringHelper::GenerateHashGetHash(MacroAssembler* masm,
11481 Register hash,
11482 Register scratch) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000011483 // hash += hash << 3;
ricow@chromium.org30ce4112010-05-31 10:38:25 +000011484 __ leal(hash, Operand(hash, hash, times_8, 0));
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000011485 // hash ^= hash >> 11;
11486 __ movl(scratch, hash);
11487 __ sarl(scratch, Immediate(11));
11488 __ xorl(hash, scratch);
11489 // hash += hash << 15;
11490 __ movl(scratch, hash);
11491 __ shll(scratch, Immediate(15));
11492 __ addl(hash, scratch);
11493
11494 // if (hash == 0) hash = 27;
11495 Label hash_not_zero;
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000011496 __ j(not_zero, &hash_not_zero);
11497 __ movl(hash, Immediate(27));
11498 __ bind(&hash_not_zero);
11499}
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000011500
11501void SubStringStub::Generate(MacroAssembler* masm) {
11502 Label runtime;
11503
11504 // Stack frame on entry.
11505 // rsp[0]: return address
11506 // rsp[8]: to
11507 // rsp[16]: from
11508 // rsp[24]: string
11509
11510 const int kToOffset = 1 * kPointerSize;
11511 const int kFromOffset = kToOffset + kPointerSize;
11512 const int kStringOffset = kFromOffset + kPointerSize;
11513 const int kArgumentsSize = (kStringOffset + kPointerSize) - kToOffset;
11514
11515 // Make sure first argument is a string.
11516 __ movq(rax, Operand(rsp, kStringOffset));
11517 ASSERT_EQ(0, kSmiTag);
11518 __ testl(rax, Immediate(kSmiTagMask));
11519 __ j(zero, &runtime);
11520 Condition is_string = masm->IsObjectStringType(rax, rbx, rbx);
11521 __ j(NegateCondition(is_string), &runtime);
11522
11523 // rax: string
11524 // rbx: instance type
11525 // Calculate length of sub string using the smi values.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000011526 Label result_longer_than_two;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000011527 __ movq(rcx, Operand(rsp, kToOffset));
11528 __ movq(rdx, Operand(rsp, kFromOffset));
11529 __ JumpIfNotBothPositiveSmi(rcx, rdx, &runtime);
11530
11531 __ SmiSub(rcx, rcx, rdx, NULL); // Overflow doesn't happen.
11532 __ j(negative, &runtime);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000011533 // Special handling of sub-strings of length 1 and 2. One character strings
11534 // are handled in the runtime system (looked up in the single character
11535 // cache). Two character strings are looked for in the symbol cache.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000011536 __ SmiToInteger32(rcx, rcx);
11537 __ cmpl(rcx, Immediate(2));
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000011538 __ j(greater, &result_longer_than_two);
11539 __ j(less, &runtime);
11540
11541 // Sub string of length 2 requested.
11542 // rax: string
11543 // rbx: instance type
11544 // rcx: sub string length (value is 2)
11545 // rdx: from index (smi)
11546 __ JumpIfInstanceTypeIsNotSequentialAscii(rbx, rbx, &runtime);
11547
11548 // Get the two characters forming the sub string.
11549 __ SmiToInteger32(rdx, rdx); // From index is no longer smi.
11550 __ movzxbq(rbx, FieldOperand(rax, rdx, times_1, SeqAsciiString::kHeaderSize));
11551 __ movzxbq(rcx,
11552 FieldOperand(rax, rdx, times_1, SeqAsciiString::kHeaderSize + 1));
11553
11554 // Try to lookup two character string in symbol table.
11555 Label make_two_character_string;
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000011556 StringHelper::GenerateTwoCharacterSymbolTableProbe(
11557 masm, rbx, rcx, rax, rdx, rdi, r14, &make_two_character_string);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000011558 __ ret(3 * kPointerSize);
11559
11560 __ bind(&make_two_character_string);
11561 // Setup registers for allocating the two character string.
11562 __ movq(rax, Operand(rsp, kStringOffset));
11563 __ movq(rbx, FieldOperand(rax, HeapObject::kMapOffset));
11564 __ movzxbl(rbx, FieldOperand(rbx, Map::kInstanceTypeOffset));
11565 __ Set(rcx, 2);
11566
11567 __ bind(&result_longer_than_two);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000011568
11569 // rax: string
11570 // rbx: instance type
11571 // rcx: result string length
11572 // Check for flat ascii string
11573 Label non_ascii_flat;
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000011574 __ JumpIfInstanceTypeIsNotSequentialAscii(rbx, rbx, &non_ascii_flat);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000011575
11576 // Allocate the result.
11577 __ AllocateAsciiString(rax, rcx, rbx, rdx, rdi, &runtime);
11578
11579 // rax: result string
11580 // rcx: result string length
11581 __ movq(rdx, rsi); // esi used by following code.
11582 // Locate first character of result.
11583 __ lea(rdi, FieldOperand(rax, SeqAsciiString::kHeaderSize));
11584 // Load string argument and locate character of sub string start.
11585 __ movq(rsi, Operand(rsp, kStringOffset));
11586 __ movq(rbx, Operand(rsp, kFromOffset));
11587 {
11588 SmiIndex smi_as_index = masm->SmiToIndex(rbx, rbx, times_1);
11589 __ lea(rsi, Operand(rsi, smi_as_index.reg, smi_as_index.scale,
11590 SeqAsciiString::kHeaderSize - kHeapObjectTag));
11591 }
11592
11593 // rax: result string
11594 // rcx: result length
11595 // rdx: original value of rsi
11596 // rdi: first character of result
11597 // rsi: character of sub string start
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000011598 StringHelper::GenerateCopyCharactersREP(masm, rdi, rsi, rcx, true);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000011599 __ movq(rsi, rdx); // Restore rsi.
11600 __ IncrementCounter(&Counters::sub_string_native, 1);
11601 __ ret(kArgumentsSize);
11602
11603 __ bind(&non_ascii_flat);
11604 // rax: string
11605 // rbx: instance type & kStringRepresentationMask | kStringEncodingMask
11606 // rcx: result string length
11607 // Check for sequential two byte string
11608 __ cmpb(rbx, Immediate(kSeqStringTag | kTwoByteStringTag));
11609 __ j(not_equal, &runtime);
11610
11611 // Allocate the result.
11612 __ AllocateTwoByteString(rax, rcx, rbx, rdx, rdi, &runtime);
11613
11614 // rax: result string
11615 // rcx: result string length
11616 __ movq(rdx, rsi); // esi used by following code.
11617 // Locate first character of result.
11618 __ lea(rdi, FieldOperand(rax, SeqTwoByteString::kHeaderSize));
11619 // Load string argument and locate character of sub string start.
11620 __ movq(rsi, Operand(rsp, kStringOffset));
11621 __ movq(rbx, Operand(rsp, kFromOffset));
11622 {
11623 SmiIndex smi_as_index = masm->SmiToIndex(rbx, rbx, times_2);
11624 __ lea(rsi, Operand(rsi, smi_as_index.reg, smi_as_index.scale,
11625 SeqAsciiString::kHeaderSize - kHeapObjectTag));
11626 }
11627
11628 // rax: result string
11629 // rcx: result length
11630 // rdx: original value of rsi
11631 // rdi: first character of result
11632 // rsi: character of sub string start
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000011633 StringHelper::GenerateCopyCharactersREP(masm, rdi, rsi, rcx, false);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000011634 __ movq(rsi, rdx); // Restore esi.
11635 __ IncrementCounter(&Counters::sub_string_native, 1);
11636 __ ret(kArgumentsSize);
11637
11638 // Just jump to runtime to create the sub string.
11639 __ bind(&runtime);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000011640 __ TailCallRuntime(Runtime::kSubString, 3, 1);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000011641}
11642
11643
11644void StringCompareStub::GenerateCompareFlatAsciiStrings(MacroAssembler* masm,
11645 Register left,
11646 Register right,
11647 Register scratch1,
11648 Register scratch2,
11649 Register scratch3,
11650 Register scratch4) {
11651 // Ensure that you can always subtract a string length from a non-negative
11652 // number (e.g. another length).
11653 ASSERT(String::kMaxLength < 0x7fffffff);
11654
11655 // Find minimum length and length difference.
ager@chromium.orgac091b72010-05-05 07:34:42 +000011656 __ movq(scratch1, FieldOperand(left, String::kLengthOffset));
11657 __ movq(scratch4, scratch1);
11658 __ SmiSub(scratch4,
11659 scratch4,
11660 FieldOperand(right, String::kLengthOffset),
11661 NULL);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000011662 // Register scratch4 now holds left.length - right.length.
11663 const Register length_difference = scratch4;
11664 Label left_shorter;
11665 __ j(less, &left_shorter);
11666 // The right string isn't longer that the left one.
11667 // Get the right string's length by subtracting the (non-negative) difference
11668 // from the left string's length.
ager@chromium.orgac091b72010-05-05 07:34:42 +000011669 __ SmiSub(scratch1, scratch1, length_difference, NULL);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000011670 __ bind(&left_shorter);
11671 // Register scratch1 now holds Min(left.length, right.length).
11672 const Register min_length = scratch1;
11673
11674 Label compare_lengths;
11675 // If min-length is zero, go directly to comparing lengths.
ager@chromium.orgac091b72010-05-05 07:34:42 +000011676 __ SmiTest(min_length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000011677 __ j(zero, &compare_lengths);
11678
ager@chromium.orgac091b72010-05-05 07:34:42 +000011679 __ SmiToInteger32(min_length, min_length);
11680
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000011681 // Registers scratch2 and scratch3 are free.
11682 Label result_not_equal;
11683 Label loop;
11684 {
11685 // Check characters 0 .. min_length - 1 in a loop.
11686 // Use scratch3 as loop index, min_length as limit and scratch2
11687 // for computation.
11688 const Register index = scratch3;
11689 __ movl(index, Immediate(0)); // Index into strings.
11690 __ bind(&loop);
11691 // Compare characters.
11692 // TODO(lrn): Could we load more than one character at a time?
11693 __ movb(scratch2, FieldOperand(left,
11694 index,
11695 times_1,
11696 SeqAsciiString::kHeaderSize));
11697 // Increment index and use -1 modifier on next load to give
11698 // the previous load extra time to complete.
11699 __ addl(index, Immediate(1));
11700 __ cmpb(scratch2, FieldOperand(right,
11701 index,
11702 times_1,
11703 SeqAsciiString::kHeaderSize - 1));
11704 __ j(not_equal, &result_not_equal);
11705 __ cmpl(index, min_length);
11706 __ j(not_equal, &loop);
11707 }
11708 // Completed loop without finding different characters.
11709 // Compare lengths (precomputed).
11710 __ bind(&compare_lengths);
ager@chromium.orgac091b72010-05-05 07:34:42 +000011711 __ SmiTest(length_difference);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000011712 __ j(not_zero, &result_not_equal);
11713
11714 // Result is EQUAL.
11715 __ Move(rax, Smi::FromInt(EQUAL));
11716 __ ret(2 * kPointerSize);
11717
11718 Label result_greater;
11719 __ bind(&result_not_equal);
11720 // Unequal comparison of left to right, either character or length.
11721 __ j(greater, &result_greater);
11722
11723 // Result is LESS.
11724 __ Move(rax, Smi::FromInt(LESS));
11725 __ ret(2 * kPointerSize);
11726
11727 // Result is GREATER.
11728 __ bind(&result_greater);
11729 __ Move(rax, Smi::FromInt(GREATER));
11730 __ ret(2 * kPointerSize);
11731}
11732
11733
11734void StringCompareStub::Generate(MacroAssembler* masm) {
11735 Label runtime;
11736
11737 // Stack frame on entry.
11738 // rsp[0]: return address
11739 // rsp[8]: right string
11740 // rsp[16]: left string
11741
11742 __ movq(rdx, Operand(rsp, 2 * kPointerSize)); // left
11743 __ movq(rax, Operand(rsp, 1 * kPointerSize)); // right
11744
11745 // Check for identity.
11746 Label not_same;
11747 __ cmpq(rdx, rax);
11748 __ j(not_equal, &not_same);
11749 __ Move(rax, Smi::FromInt(EQUAL));
11750 __ IncrementCounter(&Counters::string_compare_native, 1);
11751 __ ret(2 * kPointerSize);
11752
11753 __ bind(&not_same);
11754
11755 // Check that both are sequential ASCII strings.
11756 __ JumpIfNotBothSequentialAsciiStrings(rdx, rax, rcx, rbx, &runtime);
11757
11758 // Inline comparison of ascii strings.
11759 __ IncrementCounter(&Counters::string_compare_native, 1);
11760 GenerateCompareFlatAsciiStrings(masm, rdx, rax, rcx, rbx, rdi, r8);
11761
11762 // Call the runtime; it returns -1 (less), 0 (equal), or 1 (greater)
11763 // tagged as a small integer.
11764 __ bind(&runtime);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000011765 __ TailCallRuntime(Runtime::kStringCompare, 2, 1);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000011766}
11767
ager@chromium.org3811b432009-10-28 14:53:37 +000011768#undef __
11769
11770#define __ masm.
11771
11772#ifdef _WIN64
11773typedef double (*ModuloFunction)(double, double);
11774// Define custom fmod implementation.
11775ModuloFunction CreateModuloFunction() {
11776 size_t actual_size;
11777 byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
11778 &actual_size,
11779 true));
11780 CHECK(buffer);
ager@chromium.orgc4c92722009-11-18 14:12:51 +000011781 Assembler masm(buffer, static_cast<int>(actual_size));
ager@chromium.org3811b432009-10-28 14:53:37 +000011782 // Generated code is put into a fixed, unmovable, buffer, and not into
11783 // the V8 heap. We can't, and don't, refer to any relocatable addresses
11784 // (e.g. the JavaScript nan-object).
11785
11786 // Windows 64 ABI passes double arguments in xmm0, xmm1 and
11787 // returns result in xmm0.
11788 // Argument backing space is allocated on the stack above
11789 // the return address.
11790
11791 // Compute x mod y.
11792 // Load y and x (use argument backing store as temporary storage).
11793 __ movsd(Operand(rsp, kPointerSize * 2), xmm1);
11794 __ movsd(Operand(rsp, kPointerSize), xmm0);
11795 __ fld_d(Operand(rsp, kPointerSize * 2));
11796 __ fld_d(Operand(rsp, kPointerSize));
11797
11798 // Clear exception flags before operation.
11799 {
11800 Label no_exceptions;
11801 __ fwait();
11802 __ fnstsw_ax();
11803 // Clear if Illegal Operand or Zero Division exceptions are set.
11804 __ testb(rax, Immediate(5));
11805 __ j(zero, &no_exceptions);
11806 __ fnclex();
11807 __ bind(&no_exceptions);
11808 }
11809
11810 // Compute st(0) % st(1)
11811 {
11812 Label partial_remainder_loop;
11813 __ bind(&partial_remainder_loop);
11814 __ fprem();
11815 __ fwait();
11816 __ fnstsw_ax();
11817 __ testl(rax, Immediate(0x400 /* C2 */));
11818 // If C2 is set, computation only has partial result. Loop to
11819 // continue computation.
11820 __ j(not_zero, &partial_remainder_loop);
11821 }
11822
11823 Label valid_result;
11824 Label return_result;
11825 // If Invalid Operand or Zero Division exceptions are set,
11826 // return NaN.
11827 __ testb(rax, Immediate(5));
11828 __ j(zero, &valid_result);
11829 __ fstp(0); // Drop result in st(0).
11830 int64_t kNaNValue = V8_INT64_C(0x7ff8000000000000);
11831 __ movq(rcx, kNaNValue, RelocInfo::NONE);
11832 __ movq(Operand(rsp, kPointerSize), rcx);
11833 __ movsd(xmm0, Operand(rsp, kPointerSize));
11834 __ jmp(&return_result);
11835
11836 // If result is valid, return that.
11837 __ bind(&valid_result);
11838 __ fstp_d(Operand(rsp, kPointerSize));
11839 __ movsd(xmm0, Operand(rsp, kPointerSize));
11840
11841 // Clean up FPU stack and exceptions and return xmm0
11842 __ bind(&return_result);
11843 __ fstp(0); // Unload y.
11844
11845 Label clear_exceptions;
11846 __ testb(rax, Immediate(0x3f /* Any Exception*/));
11847 __ j(not_zero, &clear_exceptions);
11848 __ ret(0);
11849 __ bind(&clear_exceptions);
11850 __ fnclex();
11851 __ ret(0);
11852
11853 CodeDesc desc;
11854 masm.GetCode(&desc);
11855 // Call the function from C++.
11856 return FUNCTION_CAST<ModuloFunction>(buffer);
11857}
11858
11859#endif
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000011860
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000011861
ager@chromium.orge2902be2009-06-08 12:21:35 +000011862#undef __
kasperl@chromium.org71affb52009-05-26 05:44:31 +000011863
11864} } // namespace v8::internal
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +000011865
11866#endif // V8_TARGET_ARCH_X64