blob: c883a48f7bdf9b1e6284dd5ff0ed25bf2516d052 [file] [log] [blame]
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001// Copyright 2010 the V8 project authors. All rights reserved.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28#include "v8.h"
29
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +000030#if defined(V8_TARGET_ARCH_IA32)
31
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000032#include "bootstrapper.h"
33#include "codegen-inl.h"
ager@chromium.orgc4c92722009-11-18 14:12:51 +000034#include "compiler.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000035#include "debug.h"
ager@chromium.org5ec48922009-05-05 07:25:34 +000036#include "ic-inl.h"
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000037#include "jsregexp.h"
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000038#include "parser.h"
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000039#include "regexp-macro-assembler.h"
40#include "regexp-stack.h"
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000041#include "register-allocator-inl.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000042#include "runtime.h"
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000043#include "scopes.h"
sgjesse@chromium.org833cdd72010-02-26 10:06:16 +000044#include "virtual-frame-inl.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000045
kasperl@chromium.org71affb52009-05-26 05:44:31 +000046namespace v8 {
47namespace internal {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000048
ricow@chromium.org30ce4112010-05-31 10:38:25 +000049#define __ ACCESS_MASM(masm)
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000050
kasperl@chromium.orgb9123622008-09-17 14:05:56 +000051// -------------------------------------------------------------------------
ricow@chromium.org30ce4112010-05-31 10:38:25 +000052// Platform-specific FrameRegisterState functions.
ager@chromium.orge2902be2009-06-08 12:21:35 +000053
ricow@chromium.org30ce4112010-05-31 10:38:25 +000054void FrameRegisterState::Save(MacroAssembler* masm) const {
ager@chromium.orge2902be2009-06-08 12:21:35 +000055 for (int i = 0; i < RegisterAllocator::kNumRegisters; i++) {
56 int action = registers_[i];
57 if (action == kPush) {
58 __ push(RegisterAllocator::ToRegister(i));
59 } else if (action != kIgnore && (action & kSyncedFlag) == 0) {
60 __ mov(Operand(ebp, action), RegisterAllocator::ToRegister(i));
61 }
62 }
63}
64
65
ricow@chromium.org30ce4112010-05-31 10:38:25 +000066void FrameRegisterState::Restore(MacroAssembler* masm) const {
ager@chromium.orge2902be2009-06-08 12:21:35 +000067 // Restore registers in reverse order due to the stack.
68 for (int i = RegisterAllocator::kNumRegisters - 1; i >= 0; i--) {
69 int action = registers_[i];
70 if (action == kPush) {
71 __ pop(RegisterAllocator::ToRegister(i));
72 } else if (action != kIgnore) {
73 action &= ~kSyncedFlag;
74 __ mov(RegisterAllocator::ToRegister(i), Operand(ebp, action));
75 }
76 }
77}
78
79
ricow@chromium.org30ce4112010-05-31 10:38:25 +000080#undef __
81#define __ ACCESS_MASM(masm_)
82
83// -------------------------------------------------------------------------
84// Platform-specific DeferredCode functions.
85
86void DeferredCode::SaveRegisters() {
87 frame_state_.Save(masm_);
88}
89
90
91void DeferredCode::RestoreRegisters() {
92 frame_state_.Restore(masm_);
93}
94
95
96// -------------------------------------------------------------------------
97// Platform-specific RuntimeCallHelper functions.
98
99void VirtualFrameRuntimeCallHelper::BeforeCall(MacroAssembler* masm) const {
100 frame_state_->Save(masm);
101}
102
103
104void VirtualFrameRuntimeCallHelper::AfterCall(MacroAssembler* masm) const {
105 frame_state_->Restore(masm);
106}
107
108
109void ICRuntimeCallHelper::BeforeCall(MacroAssembler* masm) const {
110 masm->EnterInternalFrame();
111}
112
113
114void ICRuntimeCallHelper::AfterCall(MacroAssembler* masm) const {
115 masm->LeaveInternalFrame();
116}
117
118
ager@chromium.orge2902be2009-06-08 12:21:35 +0000119// -------------------------------------------------------------------------
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000120// CodeGenState implementation.
121
ager@chromium.org7c537e22008-10-16 08:43:32 +0000122CodeGenState::CodeGenState(CodeGenerator* owner)
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000123 : owner_(owner),
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000124 destination_(NULL),
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000125 previous_(NULL) {
126 owner_->set_state(this);
127}
128
129
ager@chromium.org7c537e22008-10-16 08:43:32 +0000130CodeGenState::CodeGenState(CodeGenerator* owner,
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000131 ControlDestination* destination)
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000132 : owner_(owner),
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000133 destination_(destination),
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000134 previous_(owner->state()) {
135 owner_->set_state(this);
136}
137
138
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000139CodeGenState::~CodeGenState() {
140 ASSERT(owner_->state() == this);
141 owner_->set_state(previous_);
142}
143
144
ager@chromium.org7c537e22008-10-16 08:43:32 +0000145// -------------------------------------------------------------------------
146// CodeGenerator implementation
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000147
ager@chromium.org5c838252010-02-19 08:53:10 +0000148CodeGenerator::CodeGenerator(MacroAssembler* masm)
149 : deferred_(8),
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000150 masm_(masm),
ager@chromium.org5c838252010-02-19 08:53:10 +0000151 info_(NULL),
ager@chromium.org7c537e22008-10-16 08:43:32 +0000152 frame_(NULL),
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000153 allocator_(NULL),
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000154 state_(NULL),
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000155 loop_nesting_(0),
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000156 in_safe_int32_mode_(false),
157 safe_int32_mode_enabled_(true),
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000158 function_return_is_shadowed_(false),
159 in_spilled_code_(false) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000160}
161
162
163// Calling conventions:
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000164// ebp: caller's frame pointer
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000165// esp: stack pointer
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000166// edi: called JS function
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000167// esi: callee's context
168
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000169void CodeGenerator::Generate(CompilationInfo* info) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000170 // Record the position for debugging purposes.
ager@chromium.org5c838252010-02-19 08:53:10 +0000171 CodeForFunctionPosition(info->function());
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000172 Comment cmnt(masm_, "[ function compiled by virtual frame code generator");
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000173
174 // Initialize state.
ager@chromium.org5c838252010-02-19 08:53:10 +0000175 info_ = info;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000176 ASSERT(allocator_ == NULL);
177 RegisterAllocator register_allocator(this);
178 allocator_ = &register_allocator;
ager@chromium.org7c537e22008-10-16 08:43:32 +0000179 ASSERT(frame_ == NULL);
kasperl@chromium.org71affb52009-05-26 05:44:31 +0000180 frame_ = new VirtualFrame();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000181 set_in_spilled_code(false);
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000182
183 // Adjust for function-level loop nesting.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000184 ASSERT_EQ(0, loop_nesting_);
185 loop_nesting_ = info->loop_nesting();
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000186
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +0000187 JumpTarget::set_compiling_deferred_code(false);
188
iposva@chromium.orgda86e9b2009-05-15 23:13:59 +0000189#ifdef DEBUG
190 if (strlen(FLAG_stop_at) > 0 &&
ager@chromium.org5c838252010-02-19 08:53:10 +0000191 info->function()->name()->IsEqualTo(CStrVector(FLAG_stop_at))) {
iposva@chromium.orgda86e9b2009-05-15 23:13:59 +0000192 frame_->SpillAll();
193 __ int3();
194 }
195#endif
196
197 // New scope to get automatic timing calculation.
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000198 { HistogramTimerScope codegen_timer(&Counters::code_generation);
ager@chromium.org7c537e22008-10-16 08:43:32 +0000199 CodeGenState state(this);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000200
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000201 // Entry:
202 // Stack: receiver, arguments, return address.
203 // ebp: caller's frame pointer
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000204 // esp: stack pointer
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000205 // edi: called JS function
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000206 // esi: callee's context
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000207 allocator_->Initialize();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000208
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000209 if (info->mode() == CompilationInfo::PRIMARY) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000210 frame_->Enter();
211
212 // Allocate space for locals and initialize them.
213 frame_->AllocateStackSlots();
214
215 // Allocate the local context if needed.
ager@chromium.org5c838252010-02-19 08:53:10 +0000216 int heap_slots = scope()->num_heap_slots() - Context::MIN_CONTEXT_SLOTS;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000217 if (heap_slots > 0) {
218 Comment cmnt(masm_, "[ allocate local context");
219 // Allocate local context.
220 // Get outer context and create a new context based on it.
221 frame_->PushFunction();
222 Result context;
223 if (heap_slots <= FastNewContextStub::kMaximumSlots) {
224 FastNewContextStub stub(heap_slots);
225 context = frame_->CallStub(&stub, 1);
226 } else {
227 context = frame_->CallRuntime(Runtime::kNewContext, 1);
228 }
229
230 // Update context local.
231 frame_->SaveContextRegister();
232
233 // Verify that the runtime call result and esi agree.
234 if (FLAG_debug_code) {
235 __ cmp(context.reg(), Operand(esi));
236 __ Assert(equal, "Runtime::NewContext should end up in esi");
237 }
238 }
239
240 // TODO(1241774): Improve this code:
241 // 1) only needed if we have a context
242 // 2) no need to recompute context ptr every single time
243 // 3) don't copy parameter operand code from SlotOperand!
244 {
245 Comment cmnt2(masm_, "[ copy context parameters into .context");
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000246 // Note that iteration order is relevant here! If we have the same
247 // parameter twice (e.g., function (x, y, x)), and that parameter
248 // needs to be copied into the context, it must be the last argument
249 // passed to the parameter that needs to be copied. This is a rare
250 // case so we don't check for it, instead we rely on the copying
251 // order: such a parameter is copied repeatedly into the same
252 // context location and thus the last value is what is seen inside
253 // the function.
ager@chromium.org5c838252010-02-19 08:53:10 +0000254 for (int i = 0; i < scope()->num_parameters(); i++) {
255 Variable* par = scope()->parameter(i);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000256 Slot* slot = par->slot();
257 if (slot != NULL && slot->type() == Slot::CONTEXT) {
258 // The use of SlotOperand below is safe in unspilled code
259 // because the slot is guaranteed to be a context slot.
260 //
261 // There are no parameters in the global scope.
ager@chromium.org5c838252010-02-19 08:53:10 +0000262 ASSERT(!scope()->is_global_scope());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000263 frame_->PushParameterAt(i);
264 Result value = frame_->Pop();
265 value.ToRegister();
266
267 // SlotOperand loads context.reg() with the context object
268 // stored to, used below in RecordWrite.
269 Result context = allocator_->Allocate();
270 ASSERT(context.is_valid());
271 __ mov(SlotOperand(slot, context.reg()), value.reg());
272 int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize;
273 Result scratch = allocator_->Allocate();
274 ASSERT(scratch.is_valid());
275 frame_->Spill(context.reg());
276 frame_->Spill(value.reg());
277 __ RecordWrite(context.reg(), offset, value.reg(), scratch.reg());
278 }
279 }
280 }
281
282 // Store the arguments object. This must happen after context
283 // initialization because the arguments object may be stored in
284 // the context.
285 if (ArgumentsMode() != NO_ARGUMENTS_ALLOCATION) {
286 StoreArgumentsObject(true);
287 }
288
289 // Initialize ThisFunction reference if present.
ager@chromium.org5c838252010-02-19 08:53:10 +0000290 if (scope()->is_function_scope() && scope()->function() != NULL) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000291 frame_->Push(Factory::the_hole_value());
ager@chromium.org5c838252010-02-19 08:53:10 +0000292 StoreToSlot(scope()->function()->slot(), NOT_CONST_INIT);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000293 }
294 } else {
295 // When used as the secondary compiler for splitting, ebp, esi,
296 // and edi have been pushed on the stack. Adjust the virtual
297 // frame to match this state.
298 frame_->Adjust(3);
299 allocator_->Unuse(edi);
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000300
301 // Bind all the bailout labels to the beginning of the function.
302 List<CompilationInfo::Bailout*>* bailouts = info->bailouts();
303 for (int i = 0; i < bailouts->length(); i++) {
304 __ bind(bailouts->at(i)->label());
305 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000306 }
307
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000308 // Initialize the function return target after the locals are set
309 // up, because it needs the expected frame height from the frame.
kasperl@chromium.org71affb52009-05-26 05:44:31 +0000310 function_return_.set_direction(JumpTarget::BIDIRECTIONAL);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000311 function_return_is_shadowed_ = false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000312
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000313 // Generate code to 'execute' declarations and initialize functions
314 // (source elements). In case of an illegal redeclaration we need to
315 // handle that instead of processing the declarations.
ager@chromium.org5c838252010-02-19 08:53:10 +0000316 if (scope()->HasIllegalRedeclaration()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000317 Comment cmnt(masm_, "[ illegal redeclarations");
ager@chromium.org5c838252010-02-19 08:53:10 +0000318 scope()->VisitIllegalRedeclaration(this);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000319 } else {
320 Comment cmnt(masm_, "[ declarations");
ager@chromium.org5c838252010-02-19 08:53:10 +0000321 ProcessDeclarations(scope()->declarations());
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000322 // Bail out if a stack-overflow exception occurred when processing
323 // declarations.
kasper.lund212ac232008-07-16 07:07:30 +0000324 if (HasStackOverflow()) return;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000325 }
326
327 if (FLAG_trace) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000328 frame_->CallRuntime(Runtime::kTraceEnter, 0);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +0000329 // Ignore the return value.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000330 }
331 CheckStack();
332
333 // Compile the body of the function in a vanilla state. Don't
334 // bother compiling all the code if the scope has an illegal
335 // redeclaration.
ager@chromium.org5c838252010-02-19 08:53:10 +0000336 if (!scope()->HasIllegalRedeclaration()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000337 Comment cmnt(masm_, "[ function body");
338#ifdef DEBUG
339 bool is_builtin = Bootstrapper::IsActive();
340 bool should_trace =
341 is_builtin ? FLAG_trace_builtin_calls : FLAG_trace_calls;
342 if (should_trace) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000343 frame_->CallRuntime(Runtime::kDebugTrace, 0);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +0000344 // Ignore the return value.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000345 }
346#endif
ager@chromium.org5c838252010-02-19 08:53:10 +0000347 VisitStatements(info->function()->body());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000348
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000349 // Handle the return from the function.
350 if (has_valid_frame()) {
351 // If there is a valid frame, control flow can fall off the end of
352 // the body. In that case there is an implicit return statement.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000353 ASSERT(!function_return_is_shadowed_);
ager@chromium.org5c838252010-02-19 08:53:10 +0000354 CodeForReturnPosition(info->function());
kasperl@chromium.org8ccb0be2009-04-07 07:21:39 +0000355 frame_->PrepareForReturn();
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +0000356 Result undefined(Factory::undefined_value());
kasperl@chromium.org8ccb0be2009-04-07 07:21:39 +0000357 if (function_return_.is_bound()) {
358 function_return_.Jump(&undefined);
359 } else {
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000360 function_return_.Bind(&undefined);
kasperl@chromium.org8ccb0be2009-04-07 07:21:39 +0000361 GenerateReturnSequence(&undefined);
362 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000363 } else if (function_return_.is_linked()) {
364 // If the return target has dangling jumps to it, then we have not
365 // yet generated the return sequence. This can happen when (a)
366 // control does not flow off the end of the body so we did not
367 // compile an artificial return statement just above, and (b) there
368 // are return statements in the body but (c) they are all shadowed.
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +0000369 Result return_value;
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000370 function_return_.Bind(&return_value);
kasperl@chromium.org8ccb0be2009-04-07 07:21:39 +0000371 GenerateReturnSequence(&return_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000372 }
373 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000374 }
375
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000376 // Adjust for function-level loop nesting.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000377 ASSERT_EQ(info->loop_nesting(), loop_nesting_);
378 loop_nesting_ = 0;
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000379
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000380 // Code generation state must be reset.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000381 ASSERT(state_ == NULL);
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000382 ASSERT(loop_nesting() == 0);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000383 ASSERT(!function_return_is_shadowed_);
384 function_return_.Unuse();
385 DeleteFrame();
386
387 // Process any deferred code using the register allocator.
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +0000388 if (!HasStackOverflow()) {
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +0000389 HistogramTimerScope deferred_timer(&Counters::deferred_code_generation);
390 JumpTarget::set_compiling_deferred_code(true);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000391 ProcessDeferred();
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +0000392 JumpTarget::set_compiling_deferred_code(false);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000393 }
394
395 // There is no need to delete the register allocator, it is a
396 // stack-allocated local.
397 allocator_ = NULL;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000398}
399
400
ager@chromium.org7c537e22008-10-16 08:43:32 +0000401Operand CodeGenerator::SlotOperand(Slot* slot, Register tmp) {
402 // Currently, this assertion will fail if we try to assign to
403 // a constant variable that is constant because it is read-only
404 // (such as the variable referring to a named function expression).
405 // We need to implement assignments to read-only variables.
406 // Ideally, we should do this during AST generation (by converting
407 // such assignments into expression statements); however, in general
408 // we may not be able to make the decision until past AST generation,
409 // that is when the entire program is known.
410 ASSERT(slot != NULL);
411 int index = slot->index();
412 switch (slot->type()) {
413 case Slot::PARAMETER:
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000414 return frame_->ParameterAt(index);
ager@chromium.org7c537e22008-10-16 08:43:32 +0000415
416 case Slot::LOCAL:
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000417 return frame_->LocalAt(index);
ager@chromium.org7c537e22008-10-16 08:43:32 +0000418
419 case Slot::CONTEXT: {
420 // Follow the context chain if necessary.
421 ASSERT(!tmp.is(esi)); // do not overwrite context register
422 Register context = esi;
423 int chain_length = scope()->ContextChainLength(slot->var()->scope());
ager@chromium.org381abbb2009-02-25 13:23:22 +0000424 for (int i = 0; i < chain_length; i++) {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000425 // Load the closure.
426 // (All contexts, even 'with' contexts, have a closure,
427 // and it is the same for all contexts inside a function.
428 // There is no need to go to the function context first.)
429 __ mov(tmp, ContextOperand(context, Context::CLOSURE_INDEX));
430 // Load the function context (which is the incoming, outer context).
431 __ mov(tmp, FieldOperand(tmp, JSFunction::kContextOffset));
432 context = tmp;
433 }
434 // We may have a 'with' context now. Get the function context.
435 // (In fact this mov may never be the needed, since the scope analysis
436 // may not permit a direct context access in this case and thus we are
437 // always at a function context. However it is safe to dereference be-
438 // cause the function context of a function context is itself. Before
439 // deleting this mov we should try to create a counter-example first,
440 // though...)
441 __ mov(tmp, ContextOperand(context, Context::FCONTEXT_INDEX));
442 return ContextOperand(tmp, index);
443 }
444
445 default:
446 UNREACHABLE();
447 return Operand(eax);
448 }
449}
450
451
ager@chromium.org381abbb2009-02-25 13:23:22 +0000452Operand CodeGenerator::ContextSlotOperandCheckExtensions(Slot* slot,
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000453 Result tmp,
454 JumpTarget* slow) {
ager@chromium.org381abbb2009-02-25 13:23:22 +0000455 ASSERT(slot->type() == Slot::CONTEXT);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000456 ASSERT(tmp.is_register());
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +0000457 Register context = esi;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000458
ager@chromium.org381abbb2009-02-25 13:23:22 +0000459 for (Scope* s = scope(); s != slot->var()->scope(); s = s->outer_scope()) {
460 if (s->num_heap_slots() > 0) {
461 if (s->calls_eval()) {
462 // Check that extension is NULL.
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +0000463 __ cmp(ContextOperand(context, Context::EXTENSION_INDEX),
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000464 Immediate(0));
465 slow->Branch(not_equal, not_taken);
ager@chromium.org381abbb2009-02-25 13:23:22 +0000466 }
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +0000467 __ mov(tmp.reg(), ContextOperand(context, Context::CLOSURE_INDEX));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000468 __ mov(tmp.reg(), FieldOperand(tmp.reg(), JSFunction::kContextOffset));
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +0000469 context = tmp.reg();
ager@chromium.org381abbb2009-02-25 13:23:22 +0000470 }
471 }
472 // Check that last extension is NULL.
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +0000473 __ cmp(ContextOperand(context, Context::EXTENSION_INDEX), Immediate(0));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000474 slow->Branch(not_equal, not_taken);
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +0000475 __ mov(tmp.reg(), ContextOperand(context, Context::FCONTEXT_INDEX));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000476 return ContextOperand(tmp.reg(), slot->index());
ager@chromium.org381abbb2009-02-25 13:23:22 +0000477}
478
479
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000480// Emit code to load the value of an expression to the top of the
481// frame. If the expression is boolean-valued it may be compiled (or
482// partially compiled) into control flow to the control destination.
483// If force_control is true, control flow is forced.
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000484void CodeGenerator::LoadCondition(Expression* expr,
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000485 ControlDestination* dest,
486 bool force_control) {
487 ASSERT(!in_spilled_code());
488 int original_height = frame_->height();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000489
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000490 { CodeGenState new_state(this, dest);
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000491 Visit(expr);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000492
493 // If we hit a stack overflow, we may not have actually visited
494 // the expression. In that case, we ensure that we have a
495 // valid-looking frame state because we will continue to generate
496 // code as we unwind the C++ stack.
497 //
498 // It's possible to have both a stack overflow and a valid frame
499 // state (eg, a subexpression overflowed, visiting it returned
500 // with a dummied frame state, and visiting this expression
501 // returned with a normal-looking state).
502 if (HasStackOverflow() &&
503 !dest->is_used() &&
504 frame_->height() == original_height) {
505 dest->Goto(true);
506 }
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000507 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000508
509 if (force_control && !dest->is_used()) {
510 // Convert the TOS value into flow to the control destination.
511 ToBoolean(dest);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000512 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000513
514 ASSERT(!(force_control && !dest->is_used()));
515 ASSERT(dest->is_used() || frame_->height() == original_height + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000516}
517
518
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000519void CodeGenerator::LoadAndSpill(Expression* expression) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000520 ASSERT(in_spilled_code());
521 set_in_spilled_code(false);
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000522 Load(expression);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000523 frame_->SpillAll();
524 set_in_spilled_code(true);
525}
526
527
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000528void CodeGenerator::LoadInSafeInt32Mode(Expression* expr,
529 BreakTarget* unsafe_bailout) {
530 set_unsafe_bailout(unsafe_bailout);
531 set_in_safe_int32_mode(true);
532 Load(expr);
533 Result value = frame_->Pop();
534 ASSERT(frame_->HasNoUntaggedInt32Elements());
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +0000535 if (expr->GuaranteedSmiResult()) {
536 ConvertInt32ResultToSmi(&value);
537 } else {
538 ConvertInt32ResultToNumber(&value);
539 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000540 set_in_safe_int32_mode(false);
541 set_unsafe_bailout(NULL);
542 frame_->Push(&value);
543}
544
545
546void CodeGenerator::LoadWithSafeInt32ModeDisabled(Expression* expr) {
547 set_safe_int32_mode_enabled(false);
548 Load(expr);
549 set_safe_int32_mode_enabled(true);
550}
551
552
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +0000553void CodeGenerator::ConvertInt32ResultToSmi(Result* value) {
554 ASSERT(value->is_untagged_int32());
555 if (value->is_register()) {
556 __ add(value->reg(), Operand(value->reg()));
557 } else {
558 ASSERT(value->is_constant());
559 ASSERT(value->handle()->IsSmi());
560 }
561 value->set_untagged_int32(false);
562 value->set_type_info(TypeInfo::Smi());
563}
564
565
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000566void CodeGenerator::ConvertInt32ResultToNumber(Result* value) {
567 ASSERT(value->is_untagged_int32());
568 if (value->is_register()) {
569 Register val = value->reg();
570 JumpTarget done;
571 __ add(val, Operand(val));
572 done.Branch(no_overflow, value);
573 __ sar(val, 1);
574 // If there was an overflow, bits 30 and 31 of the original number disagree.
575 __ xor_(val, 0x80000000u);
576 if (CpuFeatures::IsSupported(SSE2)) {
577 CpuFeatures::Scope fscope(SSE2);
578 __ cvtsi2sd(xmm0, Operand(val));
579 } else {
580 // Move val to ST[0] in the FPU
581 // Push and pop are safe with respect to the virtual frame because
582 // all synced elements are below the actual stack pointer.
583 __ push(val);
584 __ fild_s(Operand(esp, 0));
585 __ pop(val);
586 }
587 Result scratch = allocator_->Allocate();
588 ASSERT(scratch.is_register());
589 Label allocation_failed;
590 __ AllocateHeapNumber(val, scratch.reg(),
591 no_reg, &allocation_failed);
592 VirtualFrame* clone = new VirtualFrame(frame_);
593 scratch.Unuse();
594 if (CpuFeatures::IsSupported(SSE2)) {
595 CpuFeatures::Scope fscope(SSE2);
596 __ movdbl(FieldOperand(val, HeapNumber::kValueOffset), xmm0);
597 } else {
598 __ fstp_d(FieldOperand(val, HeapNumber::kValueOffset));
599 }
600 done.Jump(value);
601
602 // Establish the virtual frame, cloned from where AllocateHeapNumber
603 // jumped to allocation_failed.
604 RegisterFile empty_regs;
605 SetFrame(clone, &empty_regs);
606 __ bind(&allocation_failed);
607 unsafe_bailout_->Jump();
608
609 done.Bind(value);
610 } else {
611 ASSERT(value->is_constant());
612 }
613 value->set_untagged_int32(false);
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +0000614 value->set_type_info(TypeInfo::Integer32());
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000615}
616
617
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000618void CodeGenerator::Load(Expression* expr) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000619#ifdef DEBUG
620 int original_height = frame_->height();
621#endif
622 ASSERT(!in_spilled_code());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000623
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000624 // If the expression should be a side-effect-free 32-bit int computation,
625 // compile that SafeInt32 path, and a bailout path.
626 if (!in_safe_int32_mode() &&
627 safe_int32_mode_enabled() &&
628 expr->side_effect_free() &&
629 expr->num_bit_ops() > 2 &&
630 CpuFeatures::IsSupported(SSE2)) {
631 BreakTarget unsafe_bailout;
632 JumpTarget done;
633 unsafe_bailout.set_expected_height(frame_->height());
634 LoadInSafeInt32Mode(expr, &unsafe_bailout);
635 done.Jump();
636
637 if (unsafe_bailout.is_linked()) {
638 unsafe_bailout.Bind();
639 LoadWithSafeInt32ModeDisabled(expr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000640 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000641 done.Bind();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000642 } else {
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000643 JumpTarget true_target;
644 JumpTarget false_target;
645
646 ControlDestination dest(&true_target, &false_target, true);
647 LoadCondition(expr, &dest, false);
648
649 if (dest.false_was_fall_through()) {
650 // The false target was just bound.
kasperl@chromium.org71affb52009-05-26 05:44:31 +0000651 JumpTarget loaded;
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000652 frame_->Push(Factory::false_value());
653 // There may be dangling jumps to the true target.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000654 if (true_target.is_linked()) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000655 loaded.Jump();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000656 true_target.Bind();
657 frame_->Push(Factory::true_value());
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000658 loaded.Bind();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000659 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000660
661 } else if (dest.is_used()) {
662 // There is true, and possibly false, control flow (with true as
663 // the fall through).
664 JumpTarget loaded;
665 frame_->Push(Factory::true_value());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000666 if (false_target.is_linked()) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000667 loaded.Jump();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000668 false_target.Bind();
669 frame_->Push(Factory::false_value());
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000670 loaded.Bind();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000671 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000672
673 } else {
674 // We have a valid value on top of the frame, but we still may
675 // have dangling jumps to the true and false targets from nested
676 // subexpressions (eg, the left subexpressions of the
677 // short-circuited boolean operators).
678 ASSERT(has_valid_frame());
679 if (true_target.is_linked() || false_target.is_linked()) {
680 JumpTarget loaded;
681 loaded.Jump(); // Don't lose the current TOS.
682 if (true_target.is_linked()) {
683 true_target.Bind();
684 frame_->Push(Factory::true_value());
685 if (false_target.is_linked()) {
686 loaded.Jump();
687 }
688 }
689 if (false_target.is_linked()) {
690 false_target.Bind();
691 frame_->Push(Factory::false_value());
692 }
693 loaded.Bind();
694 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000695 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000696 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000697 ASSERT(has_valid_frame());
698 ASSERT(frame_->height() == original_height + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000699}
700
701
ager@chromium.org7c537e22008-10-16 08:43:32 +0000702void CodeGenerator::LoadGlobal() {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000703 if (in_spilled_code()) {
704 frame_->EmitPush(GlobalObject());
705 } else {
706 Result temp = allocator_->Allocate();
707 __ mov(temp.reg(), GlobalObject());
708 frame_->Push(&temp);
709 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000710}
711
712
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000713void CodeGenerator::LoadGlobalReceiver() {
714 Result temp = allocator_->Allocate();
715 Register reg = temp.reg();
716 __ mov(reg, GlobalObject());
717 __ mov(reg, FieldOperand(reg, GlobalObject::kGlobalReceiverOffset));
718 frame_->Push(&temp);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000719}
720
721
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000722void CodeGenerator::LoadTypeofExpression(Expression* expr) {
723 // Special handling of identifiers as subexpressions of typeof.
724 Variable* variable = expr->AsVariableProxy()->AsVariable();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000725 if (variable != NULL && !variable->is_this() && variable->is_global()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000726 // For a global variable we build the property reference
727 // <global>.<variable> and perform a (regular non-contextual) property
728 // load to make sure we do not get reference errors.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000729 Slot global(variable, Slot::CONTEXT, Context::GLOBAL_INDEX);
730 Literal key(variable->name());
ager@chromium.org236ad962008-09-25 09:45:57 +0000731 Property property(&global, &key, RelocInfo::kNoPosition);
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000732 Reference ref(this, &property);
733 ref.GetValue();
734 } else if (variable != NULL && variable->slot() != NULL) {
735 // For a variable that rewrites to a slot, we signal it is the immediate
736 // subexpression of a typeof.
kmillikin@chromium.org9155e252010-05-26 13:27:57 +0000737 LoadFromSlotCheckForArguments(variable->slot(), INSIDE_TYPEOF);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000738 } else {
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000739 // Anything else can be handled normally.
740 Load(expr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000741 }
742}
743
744
ager@chromium.org5c838252010-02-19 08:53:10 +0000745ArgumentsAllocationMode CodeGenerator::ArgumentsMode() {
746 if (scope()->arguments() == NULL) return NO_ARGUMENTS_ALLOCATION;
747 ASSERT(scope()->arguments_shadow() != NULL);
ager@chromium.org3e875802009-06-29 08:26:34 +0000748 // We don't want to do lazy arguments allocation for functions that
749 // have heap-allocated contexts, because it interfers with the
750 // uninitialized const tracking in the context objects.
ager@chromium.org5c838252010-02-19 08:53:10 +0000751 return (scope()->num_heap_slots() > 0)
ager@chromium.org3e875802009-06-29 08:26:34 +0000752 ? EAGER_ARGUMENTS_ALLOCATION
753 : LAZY_ARGUMENTS_ALLOCATION;
754}
755
756
757Result CodeGenerator::StoreArgumentsObject(bool initial) {
758 ArgumentsAllocationMode mode = ArgumentsMode();
759 ASSERT(mode != NO_ARGUMENTS_ALLOCATION);
760
761 Comment cmnt(masm_, "[ store arguments object");
762 if (mode == LAZY_ARGUMENTS_ALLOCATION && initial) {
763 // When using lazy arguments allocation, we store the hole value
764 // as a sentinel indicating that the arguments object hasn't been
765 // allocated yet.
766 frame_->Push(Factory::the_hole_value());
767 } else {
768 ArgumentsAccessStub stub(ArgumentsAccessStub::NEW_OBJECT);
769 frame_->PushFunction();
770 frame_->PushReceiverSlotAddress();
ager@chromium.org5c838252010-02-19 08:53:10 +0000771 frame_->Push(Smi::FromInt(scope()->num_parameters()));
ager@chromium.org3e875802009-06-29 08:26:34 +0000772 Result result = frame_->CallStub(&stub, 3);
773 frame_->Push(&result);
774 }
775
ager@chromium.org5c838252010-02-19 08:53:10 +0000776 Variable* arguments = scope()->arguments()->var();
777 Variable* shadow = scope()->arguments_shadow()->var();
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000778 ASSERT(arguments != NULL && arguments->slot() != NULL);
779 ASSERT(shadow != NULL && shadow->slot() != NULL);
780 JumpTarget done;
781 bool skip_arguments = false;
782 if (mode == LAZY_ARGUMENTS_ALLOCATION && !initial) {
783 // We have to skip storing into the arguments slot if it has already
784 // been written to. This can happen if the a function has a local
785 // variable named 'arguments'.
kmillikin@chromium.org9155e252010-05-26 13:27:57 +0000786 LoadFromSlot(arguments->slot(), NOT_INSIDE_TYPEOF);
787 Result probe = frame_->Pop();
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000788 if (probe.is_constant()) {
789 // We have to skip updating the arguments object if it has
790 // been assigned a proper value.
791 skip_arguments = !probe.handle()->IsTheHole();
792 } else {
793 __ cmp(Operand(probe.reg()), Immediate(Factory::the_hole_value()));
794 probe.Unuse();
795 done.Branch(not_equal);
ager@chromium.org3e875802009-06-29 08:26:34 +0000796 }
ager@chromium.org3e875802009-06-29 08:26:34 +0000797 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000798 if (!skip_arguments) {
799 StoreToSlot(arguments->slot(), NOT_CONST_INIT);
800 if (mode == LAZY_ARGUMENTS_ALLOCATION) done.Bind();
801 }
802 StoreToSlot(shadow->slot(), NOT_CONST_INIT);
ager@chromium.org3e875802009-06-29 08:26:34 +0000803 return frame_->Pop();
804}
805
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000806//------------------------------------------------------------------------------
807// CodeGenerator implementation of variables, lookups, and stores.
ager@chromium.org3e875802009-06-29 08:26:34 +0000808
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000809Reference::Reference(CodeGenerator* cgen,
810 Expression* expression,
811 bool persist_after_get)
812 : cgen_(cgen),
813 expression_(expression),
814 type_(ILLEGAL),
815 persist_after_get_(persist_after_get) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000816 cgen->LoadReference(this);
817}
818
819
820Reference::~Reference() {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000821 ASSERT(is_unloaded() || is_illegal());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000822}
823
824
ager@chromium.org7c537e22008-10-16 08:43:32 +0000825void CodeGenerator::LoadReference(Reference* ref) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000826 // References are loaded from both spilled and unspilled code. Set the
827 // state to unspilled to allow that (and explicitly spill after
828 // construction at the construction sites).
829 bool was_in_spilled_code = in_spilled_code_;
830 in_spilled_code_ = false;
831
ager@chromium.org7c537e22008-10-16 08:43:32 +0000832 Comment cmnt(masm_, "[ LoadReference");
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000833 Expression* e = ref->expression();
834 Property* property = e->AsProperty();
835 Variable* var = e->AsVariableProxy()->AsVariable();
836
837 if (property != NULL) {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000838 // The expression is either a property or a variable proxy that rewrites
839 // to a property.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000840 Load(property->obj());
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000841 if (property->key()->IsPropertyName()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000842 ref->set_type(Reference::NAMED);
843 } else {
844 Load(property->key());
845 ref->set_type(Reference::KEYED);
846 }
847 } else if (var != NULL) {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000848 // The expression is a variable proxy that does not rewrite to a
849 // property. Global variables are treated as named property references.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000850 if (var->is_global()) {
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000851 // If eax is free, the register allocator prefers it. Thus the code
852 // generator will load the global object into eax, which is where
853 // LoadIC wants it. Most uses of Reference call LoadIC directly
854 // after the reference is created.
855 frame_->Spill(eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000856 LoadGlobal();
857 ref->set_type(Reference::NAMED);
858 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000859 ASSERT(var->slot() != NULL);
860 ref->set_type(Reference::SLOT);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000861 }
862 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000863 // Anything else is a runtime error.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000864 Load(e);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000865 frame_->CallRuntime(Runtime::kThrowReferenceError, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000866 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000867
868 in_spilled_code_ = was_in_spilled_code;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000869}
870
871
kasper.lund7276f142008-07-30 08:49:36 +0000872// ECMA-262, section 9.2, page 30: ToBoolean(). Pop the top of stack and
873// convert it to a boolean in the condition code register or jump to
874// 'false_target'/'true_target' as appropriate.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000875void CodeGenerator::ToBoolean(ControlDestination* dest) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000876 Comment cmnt(masm_, "[ ToBoolean");
877
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000878 // The value to convert should be popped from the frame.
879 Result value = frame_->Pop();
880 value.ToRegister();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000881
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000882 if (value.is_integer32()) { // Also takes Smi case.
883 Comment cmnt(masm_, "ONLY_INTEGER_32");
884 if (FLAG_debug_code) {
885 Label ok;
whesse@chromium.orgcec079d2010-03-22 14:44:04 +0000886 __ AbortIfNotNumber(value.reg());
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000887 __ test(value.reg(), Immediate(kSmiTagMask));
888 __ j(zero, &ok);
889 __ fldz();
890 __ fld_d(FieldOperand(value.reg(), HeapNumber::kValueOffset));
891 __ FCmp();
892 __ j(not_zero, &ok);
893 __ Abort("Smi was wrapped in HeapNumber in output from bitop");
894 __ bind(&ok);
895 }
896 // In the integer32 case there are no Smis hidden in heap numbers, so we
897 // need only test for Smi zero.
898 __ test(value.reg(), Operand(value.reg()));
899 dest->false_target()->Branch(zero);
900 value.Unuse();
901 dest->Split(not_zero);
902 } else if (value.is_number()) {
ager@chromium.org5c838252010-02-19 08:53:10 +0000903 Comment cmnt(masm_, "ONLY_NUMBER");
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +0000904 // Fast case if TypeInfo indicates only numbers.
ager@chromium.org5c838252010-02-19 08:53:10 +0000905 if (FLAG_debug_code) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +0000906 __ AbortIfNotNumber(value.reg());
ager@chromium.org5c838252010-02-19 08:53:10 +0000907 }
908 // Smi => false iff zero.
909 ASSERT(kSmiTag == 0);
910 __ test(value.reg(), Operand(value.reg()));
911 dest->false_target()->Branch(zero);
912 __ test(value.reg(), Immediate(kSmiTagMask));
913 dest->true_target()->Branch(zero);
914 __ fldz();
915 __ fld_d(FieldOperand(value.reg(), HeapNumber::kValueOffset));
916 __ FCmp();
917 value.Unuse();
918 dest->Split(not_zero);
919 } else {
920 // Fast case checks.
921 // 'false' => false.
922 __ cmp(value.reg(), Factory::false_value());
923 dest->false_target()->Branch(equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000924
ager@chromium.org5c838252010-02-19 08:53:10 +0000925 // 'true' => true.
926 __ cmp(value.reg(), Factory::true_value());
927 dest->true_target()->Branch(equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000928
ager@chromium.org5c838252010-02-19 08:53:10 +0000929 // 'undefined' => false.
930 __ cmp(value.reg(), Factory::undefined_value());
931 dest->false_target()->Branch(equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000932
ager@chromium.org5c838252010-02-19 08:53:10 +0000933 // Smi => false iff zero.
934 ASSERT(kSmiTag == 0);
935 __ test(value.reg(), Operand(value.reg()));
936 dest->false_target()->Branch(zero);
937 __ test(value.reg(), Immediate(kSmiTagMask));
938 dest->true_target()->Branch(zero);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000939
ager@chromium.org5c838252010-02-19 08:53:10 +0000940 // Call the stub for all other cases.
941 frame_->Push(&value); // Undo the Pop() from above.
942 ToBooleanStub stub;
943 Result temp = frame_->CallStub(&stub, 1);
944 // Convert the result to a condition code.
945 __ test(temp.reg(), Operand(temp.reg()));
946 temp.Unuse();
947 dest->Split(not_equal);
948 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000949}
950
951
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000952class FloatingPointHelper : public AllStatic {
953 public:
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000954
955 enum ArgLocation {
956 ARGS_ON_STACK,
957 ARGS_IN_REGISTERS
958 };
959
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000960 // Code pattern for loading a floating point value. Input value must
961 // be either a smi or a heap number object (fp value). Requirements:
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +0000962 // operand in register number. Returns operand as floating point number
963 // on FPU stack.
964 static void LoadFloatOperand(MacroAssembler* masm, Register number);
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +0000965
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000966 // Code pattern for loading floating point values. Input values must
kasper.lund7276f142008-07-30 08:49:36 +0000967 // be either smi or heap number objects (fp values). Requirements:
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000968 // operand_1 on TOS+1 or in edx, operand_2 on TOS+2 or in eax.
969 // Returns operands as floating point numbers on FPU stack.
970 static void LoadFloatOperands(MacroAssembler* masm,
971 Register scratch,
972 ArgLocation arg_location = ARGS_ON_STACK);
973
974 // Similar to LoadFloatOperand but assumes that both operands are smis.
975 // Expects operands in edx, eax.
976 static void LoadFloatSmis(MacroAssembler* masm, Register scratch);
977
kasper.lund7276f142008-07-30 08:49:36 +0000978 // Test if operands are smi or number objects (fp). Requirements:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000979 // operand_1 in eax, operand_2 in edx; falls through on float
980 // operands, jumps to the non_float label otherwise.
981 static void CheckFloatOperands(MacroAssembler* masm,
982 Label* non_float,
983 Register scratch);
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +0000984
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000985 // Takes the operands in edx and eax and loads them as integers in eax
986 // and ecx.
987 static void LoadAsIntegers(MacroAssembler* masm,
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +0000988 TypeInfo type_info,
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000989 bool use_sse3,
990 Label* operand_conversion_failure);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000991 static void LoadNumbersAsIntegers(MacroAssembler* masm,
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +0000992 TypeInfo type_info,
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000993 bool use_sse3,
994 Label* operand_conversion_failure);
995 static void LoadUnknownsAsIntegers(MacroAssembler* masm,
996 bool use_sse3,
997 Label* operand_conversion_failure);
998
ager@chromium.org5c838252010-02-19 08:53:10 +0000999 // Test if operands are smis or heap numbers and load them
1000 // into xmm0 and xmm1 if they are. Operands are in edx and eax.
1001 // Leaves operands unchanged.
1002 static void LoadSSE2Operands(MacroAssembler* masm);
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00001003
ager@chromium.org4af710e2009-09-15 12:20:11 +00001004 // Test if operands are numbers (smi or HeapNumber objects), and load
1005 // them into xmm0 and xmm1 if they are. Jump to label not_numbers if
1006 // either operand is not a number. Operands are in edx and eax.
1007 // Leaves operands unchanged.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001008 static void LoadSSE2Operands(MacroAssembler* masm, Label* not_numbers);
1009
1010 // Similar to LoadSSE2Operands but assumes that both operands are smis.
1011 // Expects operands in edx, eax.
1012 static void LoadSSE2Smis(MacroAssembler* masm, Register scratch);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001013};
1014
1015
kasper.lund7276f142008-07-30 08:49:36 +00001016const char* GenericBinaryOpStub::GetName() {
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00001017 if (name_ != NULL) return name_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001018 const int kMaxNameLength = 100;
1019 name_ = Bootstrapper::AllocateAutoDeletedArray(kMaxNameLength);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00001020 if (name_ == NULL) return "OOM";
1021 const char* op_name = Token::Name(op_);
1022 const char* overwrite_name;
1023 switch (mode_) {
1024 case NO_OVERWRITE: overwrite_name = "Alloc"; break;
1025 case OVERWRITE_RIGHT: overwrite_name = "OverwriteRight"; break;
1026 case OVERWRITE_LEFT: overwrite_name = "OverwriteLeft"; break;
1027 default: overwrite_name = "UnknownOverwrite"; break;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001028 }
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00001029
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001030 OS::SNPrintF(Vector<char>(name_, kMaxNameLength),
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001031 "GenericBinaryOpStub_%s_%s%s_%s%s_%s_%s",
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00001032 op_name,
1033 overwrite_name,
1034 (flags_ & NO_SMI_CODE_IN_STUB) ? "_NoSmiInStub" : "",
1035 args_in_registers_ ? "RegArgs" : "StackArgs",
ager@chromium.org5c838252010-02-19 08:53:10 +00001036 args_reversed_ ? "_R" : "",
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001037 static_operands_type_.ToString(),
1038 BinaryOpIC::GetName(runtime_operands_type_));
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00001039 return name_;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001040}
1041
1042
ager@chromium.orge2902be2009-06-08 12:21:35 +00001043// Call the specialized stub for a binary operation.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001044class DeferredInlineBinaryOperation: public DeferredCode {
1045 public:
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00001046 DeferredInlineBinaryOperation(Token::Value op,
ager@chromium.orge2902be2009-06-08 12:21:35 +00001047 Register dst,
1048 Register left,
1049 Register right,
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00001050 TypeInfo left_info,
1051 TypeInfo right_info,
ager@chromium.orge2902be2009-06-08 12:21:35 +00001052 OverwriteMode mode)
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001053 : op_(op), dst_(dst), left_(left), right_(right),
1054 left_info_(left_info), right_info_(right_info), mode_(mode) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001055 set_comment("[ DeferredInlineBinaryOperation");
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001056 }
1057
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001058 virtual void Generate();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001059
1060 private:
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001061 Token::Value op_;
ager@chromium.orge2902be2009-06-08 12:21:35 +00001062 Register dst_;
1063 Register left_;
1064 Register right_;
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00001065 TypeInfo left_info_;
1066 TypeInfo right_info_;
ager@chromium.orge2902be2009-06-08 12:21:35 +00001067 OverwriteMode mode_;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001068};
1069
1070
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001071void DeferredInlineBinaryOperation::Generate() {
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00001072 Label done;
1073 if (CpuFeatures::IsSupported(SSE2) && ((op_ == Token::ADD) ||
1074 (op_ ==Token::SUB) ||
1075 (op_ == Token::MUL) ||
1076 (op_ == Token::DIV))) {
1077 CpuFeatures::Scope use_sse2(SSE2);
1078 Label call_runtime, after_alloc_failure;
1079 Label left_smi, right_smi, load_right, do_op;
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001080 if (!left_info_.IsSmi()) {
1081 __ test(left_, Immediate(kSmiTagMask));
1082 __ j(zero, &left_smi);
1083 if (!left_info_.IsNumber()) {
1084 __ cmp(FieldOperand(left_, HeapObject::kMapOffset),
1085 Factory::heap_number_map());
1086 __ j(not_equal, &call_runtime);
1087 }
1088 __ movdbl(xmm0, FieldOperand(left_, HeapNumber::kValueOffset));
1089 if (mode_ == OVERWRITE_LEFT) {
1090 __ mov(dst_, left_);
1091 }
1092 __ jmp(&load_right);
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00001093
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001094 __ bind(&left_smi);
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00001095 } else {
1096 if (FLAG_debug_code) __ AbortIfNotSmi(left_);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001097 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001098 __ SmiUntag(left_);
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00001099 __ cvtsi2sd(xmm0, Operand(left_));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001100 __ SmiTag(left_);
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00001101 if (mode_ == OVERWRITE_LEFT) {
1102 Label alloc_failure;
1103 __ push(left_);
1104 __ AllocateHeapNumber(dst_, left_, no_reg, &after_alloc_failure);
1105 __ pop(left_);
1106 }
1107
1108 __ bind(&load_right);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001109 if (!right_info_.IsSmi()) {
1110 __ test(right_, Immediate(kSmiTagMask));
1111 __ j(zero, &right_smi);
1112 if (!right_info_.IsNumber()) {
1113 __ cmp(FieldOperand(right_, HeapObject::kMapOffset),
1114 Factory::heap_number_map());
1115 __ j(not_equal, &call_runtime);
1116 }
1117 __ movdbl(xmm1, FieldOperand(right_, HeapNumber::kValueOffset));
1118 if (mode_ == OVERWRITE_RIGHT) {
1119 __ mov(dst_, right_);
1120 } else if (mode_ == NO_OVERWRITE) {
1121 Label alloc_failure;
1122 __ push(left_);
1123 __ AllocateHeapNumber(dst_, left_, no_reg, &after_alloc_failure);
1124 __ pop(left_);
1125 }
1126 __ jmp(&do_op);
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00001127
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001128 __ bind(&right_smi);
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00001129 } else {
1130 if (FLAG_debug_code) __ AbortIfNotSmi(right_);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001131 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001132 __ SmiUntag(right_);
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00001133 __ cvtsi2sd(xmm1, Operand(right_));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001134 __ SmiTag(right_);
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00001135 if (mode_ == OVERWRITE_RIGHT || mode_ == NO_OVERWRITE) {
1136 Label alloc_failure;
1137 __ push(left_);
1138 __ AllocateHeapNumber(dst_, left_, no_reg, &after_alloc_failure);
1139 __ pop(left_);
1140 }
1141
1142 __ bind(&do_op);
1143 switch (op_) {
1144 case Token::ADD: __ addsd(xmm0, xmm1); break;
1145 case Token::SUB: __ subsd(xmm0, xmm1); break;
1146 case Token::MUL: __ mulsd(xmm0, xmm1); break;
1147 case Token::DIV: __ divsd(xmm0, xmm1); break;
1148 default: UNREACHABLE();
1149 }
1150 __ movdbl(FieldOperand(dst_, HeapNumber::kValueOffset), xmm0);
1151 __ jmp(&done);
1152
1153 __ bind(&after_alloc_failure);
1154 __ pop(left_);
1155 __ bind(&call_runtime);
1156 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001157 GenericBinaryOpStub stub(op_,
1158 mode_,
1159 NO_SMI_CODE_IN_STUB,
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00001160 TypeInfo::Combine(left_info_, right_info_));
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001161 stub.GenerateCall(masm_, left_, right_);
ager@chromium.orge2902be2009-06-08 12:21:35 +00001162 if (!dst_.is(eax)) __ mov(dst_, eax);
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00001163 __ bind(&done);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001164}
1165
1166
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00001167static TypeInfo CalculateTypeInfo(TypeInfo operands_type,
lrn@chromium.org25156de2010-04-06 13:10:27 +00001168 Token::Value op,
1169 const Result& right,
1170 const Result& left) {
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00001171 // Set TypeInfo of result according to the operation performed.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001172 // Rely on the fact that smis have a 31 bit payload on ia32.
1173 ASSERT(kSmiValueSize == 31);
1174 switch (op) {
1175 case Token::COMMA:
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00001176 return right.type_info();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001177 case Token::OR:
1178 case Token::AND:
1179 // Result type can be either of the two input types.
1180 return operands_type;
1181 case Token::BIT_AND: {
1182 // Anding with positive Smis will give you a Smi.
1183 if (right.is_constant() && right.handle()->IsSmi() &&
1184 Smi::cast(*right.handle())->value() >= 0) {
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00001185 return TypeInfo::Smi();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001186 } else if (left.is_constant() && left.handle()->IsSmi() &&
1187 Smi::cast(*left.handle())->value() >= 0) {
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00001188 return TypeInfo::Smi();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001189 }
1190 return (operands_type.IsSmi())
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00001191 ? TypeInfo::Smi()
1192 : TypeInfo::Integer32();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001193 }
1194 case Token::BIT_OR: {
1195 // Oring with negative Smis will give you a Smi.
1196 if (right.is_constant() && right.handle()->IsSmi() &&
1197 Smi::cast(*right.handle())->value() < 0) {
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00001198 return TypeInfo::Smi();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001199 } else if (left.is_constant() && left.handle()->IsSmi() &&
1200 Smi::cast(*left.handle())->value() < 0) {
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00001201 return TypeInfo::Smi();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001202 }
1203 return (operands_type.IsSmi())
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00001204 ? TypeInfo::Smi()
1205 : TypeInfo::Integer32();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001206 }
1207 case Token::BIT_XOR:
1208 // Result is always a 32 bit integer. Smi property of inputs is preserved.
1209 return (operands_type.IsSmi())
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00001210 ? TypeInfo::Smi()
1211 : TypeInfo::Integer32();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001212 case Token::SAR:
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00001213 if (left.is_smi()) return TypeInfo::Smi();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001214 // Result is a smi if we shift by a constant >= 1, otherwise an integer32.
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00001215 // Shift amount is masked with 0x1F (ECMA standard 11.7.2).
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001216 return (right.is_constant() && right.handle()->IsSmi()
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00001217 && (Smi::cast(*right.handle())->value() & 0x1F) >= 1)
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00001218 ? TypeInfo::Smi()
1219 : TypeInfo::Integer32();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001220 case Token::SHR:
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00001221 // Result is a smi if we shift by a constant >= 2, an integer32 if
1222 // we shift by 1, and an unsigned 32-bit integer if we shift by 0.
1223 if (right.is_constant() && right.handle()->IsSmi()) {
1224 int shift_amount = Smi::cast(*right.handle())->value() & 0x1F;
1225 if (shift_amount > 1) {
1226 return TypeInfo::Smi();
1227 } else if (shift_amount > 0) {
1228 return TypeInfo::Integer32();
1229 }
1230 }
1231 return TypeInfo::Number();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001232 case Token::ADD:
1233 if (operands_type.IsSmi()) {
1234 // The Integer32 range is big enough to take the sum of any two Smis.
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00001235 return TypeInfo::Integer32();
lrn@chromium.org25156de2010-04-06 13:10:27 +00001236 } else if (operands_type.IsNumber()) {
1237 return TypeInfo::Number();
1238 } else if (left.type_info().IsString() || right.type_info().IsString()) {
1239 return TypeInfo::String();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001240 } else {
lrn@chromium.org25156de2010-04-06 13:10:27 +00001241 return TypeInfo::Unknown();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001242 }
1243 case Token::SHL:
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00001244 return TypeInfo::Integer32();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001245 case Token::SUB:
1246 // The Integer32 range is big enough to take the difference of any two
1247 // Smis.
1248 return (operands_type.IsSmi()) ?
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00001249 TypeInfo::Integer32() :
1250 TypeInfo::Number();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001251 case Token::MUL:
1252 case Token::DIV:
1253 case Token::MOD:
1254 // Result is always a number.
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00001255 return TypeInfo::Number();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001256 default:
1257 UNREACHABLE();
1258 }
1259 UNREACHABLE();
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00001260 return TypeInfo::Unknown();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001261}
1262
1263
lrn@chromium.org25156de2010-04-06 13:10:27 +00001264void CodeGenerator::GenericBinaryOperation(BinaryOperation* expr,
1265 OverwriteMode overwrite_mode) {
kasper.lund7276f142008-07-30 08:49:36 +00001266 Comment cmnt(masm_, "[ BinaryOperation");
lrn@chromium.org25156de2010-04-06 13:10:27 +00001267 Token::Value op = expr->op();
kasper.lund7276f142008-07-30 08:49:36 +00001268 Comment cmnt_token(masm_, Token::String(op));
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001269
1270 if (op == Token::COMMA) {
1271 // Simply discard left value.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001272 frame_->Nip(1);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001273 return;
1274 }
1275
ager@chromium.org41826e72009-03-30 13:30:57 +00001276 Result right = frame_->Pop();
1277 Result left = frame_->Pop();
kasperl@chromium.org8ccb0be2009-04-07 07:21:39 +00001278
1279 if (op == Token::ADD) {
lrn@chromium.org25156de2010-04-06 13:10:27 +00001280 const bool left_is_string = left.type_info().IsString();
1281 const bool right_is_string = right.type_info().IsString();
1282 // Make sure constant strings have string type info.
1283 ASSERT(!(left.is_constant() && left.handle()->IsString()) ||
1284 left_is_string);
1285 ASSERT(!(right.is_constant() && right.handle()->IsString()) ||
1286 right_is_string);
kasperl@chromium.org8ccb0be2009-04-07 07:21:39 +00001287 if (left_is_string || right_is_string) {
1288 frame_->Push(&left);
1289 frame_->Push(&right);
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00001290 Result answer;
kasperl@chromium.org8ccb0be2009-04-07 07:21:39 +00001291 if (left_is_string) {
1292 if (right_is_string) {
lrn@chromium.org25156de2010-04-06 13:10:27 +00001293 StringAddStub stub(NO_STRING_CHECK_IN_STUB);
1294 answer = frame_->CallStub(&stub, 2);
kasperl@chromium.org8ccb0be2009-04-07 07:21:39 +00001295 } else {
1296 answer =
1297 frame_->InvokeBuiltin(Builtins::STRING_ADD_LEFT, CALL_FUNCTION, 2);
1298 }
1299 } else if (right_is_string) {
1300 answer =
1301 frame_->InvokeBuiltin(Builtins::STRING_ADD_RIGHT, CALL_FUNCTION, 2);
1302 }
lrn@chromium.org25156de2010-04-06 13:10:27 +00001303 answer.set_type_info(TypeInfo::String());
kasperl@chromium.org8ccb0be2009-04-07 07:21:39 +00001304 frame_->Push(&answer);
1305 return;
1306 }
1307 // Neither operand is known to be a string.
1308 }
1309
ager@chromium.org5c838252010-02-19 08:53:10 +00001310 bool left_is_smi_constant = left.is_constant() && left.handle()->IsSmi();
1311 bool left_is_non_smi_constant = left.is_constant() && !left.handle()->IsSmi();
1312 bool right_is_smi_constant = right.is_constant() && right.handle()->IsSmi();
1313 bool right_is_non_smi_constant =
1314 right.is_constant() && !right.handle()->IsSmi();
ager@chromium.org41826e72009-03-30 13:30:57 +00001315
ager@chromium.org5c838252010-02-19 08:53:10 +00001316 if (left_is_smi_constant && right_is_smi_constant) {
ager@chromium.org41826e72009-03-30 13:30:57 +00001317 // Compute the constant result at compile time, and leave it on the frame.
1318 int left_int = Smi::cast(*left.handle())->value();
1319 int right_int = Smi::cast(*right.handle())->value();
1320 if (FoldConstantSmis(op, left_int, right_int)) return;
1321 }
1322
ager@chromium.org5c838252010-02-19 08:53:10 +00001323 // Get number type of left and right sub-expressions.
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00001324 TypeInfo operands_type =
1325 TypeInfo::Combine(left.type_info(), right.type_info());
ager@chromium.org5c838252010-02-19 08:53:10 +00001326
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00001327 TypeInfo result_type = CalculateTypeInfo(operands_type, op, right, left);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001328
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001329 Result answer;
ager@chromium.org5c838252010-02-19 08:53:10 +00001330 if (left_is_non_smi_constant || right_is_non_smi_constant) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001331 // Go straight to the slow case, with no smi code.
ager@chromium.org5c838252010-02-19 08:53:10 +00001332 GenericBinaryOpStub stub(op,
1333 overwrite_mode,
1334 NO_SMI_CODE_IN_STUB,
1335 operands_type);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001336 answer = stub.GenerateCall(masm_, frame_, &left, &right);
ager@chromium.org5c838252010-02-19 08:53:10 +00001337 } else if (right_is_smi_constant) {
lrn@chromium.org25156de2010-04-06 13:10:27 +00001338 answer = ConstantSmiBinaryOperation(expr, &left, right.handle(),
1339 false, overwrite_mode);
ager@chromium.org5c838252010-02-19 08:53:10 +00001340 } else if (left_is_smi_constant) {
lrn@chromium.org25156de2010-04-06 13:10:27 +00001341 answer = ConstantSmiBinaryOperation(expr, &right, left.handle(),
1342 true, overwrite_mode);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001343 } else {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001344 // Set the flags based on the operation, type and loop nesting level.
1345 // Bit operations always assume they likely operate on Smis. Still only
1346 // generate the inline Smi check code if this operation is part of a loop.
1347 // For all other operations only inline the Smi check code for likely smis
1348 // if the operation is part of a loop.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001349 if (loop_nesting() > 0 &&
1350 (Token::IsBitOp(op) ||
1351 operands_type.IsInteger32() ||
lrn@chromium.org25156de2010-04-06 13:10:27 +00001352 expr->type()->IsLikelySmi())) {
1353 answer = LikelySmiBinaryOperation(expr, &left, &right, overwrite_mode);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001354 } else {
ager@chromium.org5c838252010-02-19 08:53:10 +00001355 GenericBinaryOpStub stub(op,
1356 overwrite_mode,
1357 NO_GENERIC_BINARY_FLAGS,
1358 operands_type);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001359 answer = stub.GenerateCall(masm_, frame_, &left, &right);
ager@chromium.org41826e72009-03-30 13:30:57 +00001360 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001361 }
ager@chromium.org5c838252010-02-19 08:53:10 +00001362
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00001363 answer.set_type_info(result_type);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001364 frame_->Push(&answer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001365}
1366
1367
ager@chromium.org41826e72009-03-30 13:30:57 +00001368bool CodeGenerator::FoldConstantSmis(Token::Value op, int left, int right) {
1369 Object* answer_object = Heap::undefined_value();
1370 switch (op) {
1371 case Token::ADD:
1372 if (Smi::IsValid(left + right)) {
1373 answer_object = Smi::FromInt(left + right);
1374 }
1375 break;
1376 case Token::SUB:
1377 if (Smi::IsValid(left - right)) {
1378 answer_object = Smi::FromInt(left - right);
1379 }
1380 break;
1381 case Token::MUL: {
1382 double answer = static_cast<double>(left) * right;
1383 if (answer >= Smi::kMinValue && answer <= Smi::kMaxValue) {
1384 // If the product is zero and the non-zero factor is negative,
1385 // the spec requires us to return floating point negative zero.
1386 if (answer != 0 || (left >= 0 && right >= 0)) {
1387 answer_object = Smi::FromInt(static_cast<int>(answer));
1388 }
1389 }
1390 }
1391 break;
1392 case Token::DIV:
1393 case Token::MOD:
1394 break;
1395 case Token::BIT_OR:
1396 answer_object = Smi::FromInt(left | right);
1397 break;
1398 case Token::BIT_AND:
1399 answer_object = Smi::FromInt(left & right);
1400 break;
1401 case Token::BIT_XOR:
1402 answer_object = Smi::FromInt(left ^ right);
1403 break;
1404
1405 case Token::SHL: {
1406 int shift_amount = right & 0x1F;
1407 if (Smi::IsValid(left << shift_amount)) {
1408 answer_object = Smi::FromInt(left << shift_amount);
1409 }
1410 break;
1411 }
1412 case Token::SHR: {
1413 int shift_amount = right & 0x1F;
1414 unsigned int unsigned_left = left;
1415 unsigned_left >>= shift_amount;
1416 if (unsigned_left <= static_cast<unsigned int>(Smi::kMaxValue)) {
1417 answer_object = Smi::FromInt(unsigned_left);
1418 }
1419 break;
1420 }
1421 case Token::SAR: {
1422 int shift_amount = right & 0x1F;
1423 unsigned int unsigned_left = left;
1424 if (left < 0) {
1425 // Perform arithmetic shift of a negative number by
1426 // complementing number, logical shifting, complementing again.
1427 unsigned_left = ~unsigned_left;
1428 unsigned_left >>= shift_amount;
1429 unsigned_left = ~unsigned_left;
1430 } else {
1431 unsigned_left >>= shift_amount;
1432 }
1433 ASSERT(Smi::IsValid(unsigned_left)); // Converted to signed.
1434 answer_object = Smi::FromInt(unsigned_left); // Converted to signed.
1435 break;
1436 }
1437 default:
1438 UNREACHABLE();
1439 break;
1440 }
1441 if (answer_object == Heap::undefined_value()) {
1442 return false;
1443 }
1444 frame_->Push(Handle<Object>(answer_object));
1445 return true;
1446}
1447
1448
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001449static void CheckTwoForSminess(MacroAssembler* masm,
1450 Register left, Register right, Register scratch,
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00001451 TypeInfo left_info, TypeInfo right_info,
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001452 DeferredInlineBinaryOperation* deferred);
1453
1454
ager@chromium.orge2902be2009-06-08 12:21:35 +00001455// Implements a binary operation using a deferred code object and some
1456// inline code to operate on smis quickly.
lrn@chromium.org25156de2010-04-06 13:10:27 +00001457Result CodeGenerator::LikelySmiBinaryOperation(BinaryOperation* expr,
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001458 Result* left,
1459 Result* right,
lrn@chromium.org25156de2010-04-06 13:10:27 +00001460 OverwriteMode overwrite_mode) {
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00001461 // Copy the type info because left and right may be overwritten.
1462 TypeInfo left_type_info = left->type_info();
1463 TypeInfo right_type_info = right->type_info();
lrn@chromium.org25156de2010-04-06 13:10:27 +00001464 Token::Value op = expr->op();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001465 Result answer;
ager@chromium.orge2902be2009-06-08 12:21:35 +00001466 // Special handling of div and mod because they use fixed registers.
1467 if (op == Token::DIV || op == Token::MOD) {
1468 // We need eax as the quotient register, edx as the remainder
1469 // register, neither left nor right in eax or edx, and left copied
1470 // to eax.
1471 Result quotient;
1472 Result remainder;
1473 bool left_is_in_eax = false;
1474 // Step 1: get eax for quotient.
1475 if ((left->is_register() && left->reg().is(eax)) ||
1476 (right->is_register() && right->reg().is(eax))) {
1477 // One or both is in eax. Use a fresh non-edx register for
1478 // them.
1479 Result fresh = allocator_->Allocate();
1480 ASSERT(fresh.is_valid());
1481 if (fresh.reg().is(edx)) {
1482 remainder = fresh;
1483 fresh = allocator_->Allocate();
1484 ASSERT(fresh.is_valid());
1485 }
1486 if (left->is_register() && left->reg().is(eax)) {
1487 quotient = *left;
1488 *left = fresh;
1489 left_is_in_eax = true;
1490 }
1491 if (right->is_register() && right->reg().is(eax)) {
1492 quotient = *right;
1493 *right = fresh;
1494 }
1495 __ mov(fresh.reg(), eax);
1496 } else {
1497 // Neither left nor right is in eax.
1498 quotient = allocator_->Allocate(eax);
1499 }
1500 ASSERT(quotient.is_register() && quotient.reg().is(eax));
1501 ASSERT(!(left->is_register() && left->reg().is(eax)));
1502 ASSERT(!(right->is_register() && right->reg().is(eax)));
1503
1504 // Step 2: get edx for remainder if necessary.
1505 if (!remainder.is_valid()) {
1506 if ((left->is_register() && left->reg().is(edx)) ||
1507 (right->is_register() && right->reg().is(edx))) {
1508 Result fresh = allocator_->Allocate();
1509 ASSERT(fresh.is_valid());
1510 if (left->is_register() && left->reg().is(edx)) {
1511 remainder = *left;
1512 *left = fresh;
1513 }
1514 if (right->is_register() && right->reg().is(edx)) {
1515 remainder = *right;
1516 *right = fresh;
1517 }
1518 __ mov(fresh.reg(), edx);
1519 } else {
1520 // Neither left nor right is in edx.
1521 remainder = allocator_->Allocate(edx);
1522 }
1523 }
1524 ASSERT(remainder.is_register() && remainder.reg().is(edx));
1525 ASSERT(!(left->is_register() && left->reg().is(edx)));
1526 ASSERT(!(right->is_register() && right->reg().is(edx)));
1527
1528 left->ToRegister();
1529 right->ToRegister();
1530 frame_->Spill(eax);
1531 frame_->Spill(edx);
1532
1533 // Check that left and right are smi tagged.
1534 DeferredInlineBinaryOperation* deferred =
1535 new DeferredInlineBinaryOperation(op,
1536 (op == Token::DIV) ? eax : edx,
1537 left->reg(),
1538 right->reg(),
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00001539 left_type_info,
1540 right_type_info,
ager@chromium.orge2902be2009-06-08 12:21:35 +00001541 overwrite_mode);
1542 if (left->reg().is(right->reg())) {
1543 __ test(left->reg(), Immediate(kSmiTagMask));
1544 } else {
1545 // Use the quotient register as a scratch for the tag check.
1546 if (!left_is_in_eax) __ mov(eax, left->reg());
1547 left_is_in_eax = false; // About to destroy the value in eax.
1548 __ or_(eax, Operand(right->reg()));
1549 ASSERT(kSmiTag == 0); // Adjust test if not the case.
1550 __ test(eax, Immediate(kSmiTagMask));
1551 }
1552 deferred->Branch(not_zero);
1553
1554 if (!left_is_in_eax) __ mov(eax, left->reg());
1555 // Sign extend eax into edx:eax.
1556 __ cdq();
1557 // Check for 0 divisor.
1558 __ test(right->reg(), Operand(right->reg()));
1559 deferred->Branch(zero);
1560 // Divide edx:eax by the right operand.
1561 __ idiv(right->reg());
1562
1563 // Complete the operation.
1564 if (op == Token::DIV) {
1565 // Check for negative zero result. If result is zero, and divisor
1566 // is negative, return a floating point negative zero. The
1567 // virtual frame is unchanged in this block, so local control flow
fschneider@chromium.org086aac62010-03-17 13:18:24 +00001568 // can use a Label rather than a JumpTarget. If the context of this
1569 // expression will treat -0 like 0, do not do this test.
lrn@chromium.org25156de2010-04-06 13:10:27 +00001570 if (!expr->no_negative_zero()) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00001571 Label non_zero_result;
1572 __ test(left->reg(), Operand(left->reg()));
1573 __ j(not_zero, &non_zero_result);
1574 __ test(right->reg(), Operand(right->reg()));
1575 deferred->Branch(negative);
1576 __ bind(&non_zero_result);
1577 }
ager@chromium.orge2902be2009-06-08 12:21:35 +00001578 // Check for the corner case of dividing the most negative smi by
1579 // -1. We cannot use the overflow flag, since it is not set by
1580 // idiv instruction.
1581 ASSERT(kSmiTag == 0 && kSmiTagSize == 1);
1582 __ cmp(eax, 0x40000000);
1583 deferred->Branch(equal);
1584 // Check that the remainder is zero.
1585 __ test(edx, Operand(edx));
1586 deferred->Branch(not_zero);
1587 // Tag the result and store it in the quotient register.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001588 __ SmiTag(eax);
ager@chromium.orge2902be2009-06-08 12:21:35 +00001589 deferred->BindExit();
1590 left->Unuse();
1591 right->Unuse();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001592 answer = quotient;
ager@chromium.orge2902be2009-06-08 12:21:35 +00001593 } else {
1594 ASSERT(op == Token::MOD);
1595 // Check for a negative zero result. If the result is zero, and
1596 // the dividend is negative, return a floating point negative
1597 // zero. The frame is unchanged in this block, so local control
1598 // flow can use a Label rather than a JumpTarget.
lrn@chromium.org25156de2010-04-06 13:10:27 +00001599 if (!expr->no_negative_zero()) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00001600 Label non_zero_result;
1601 __ test(edx, Operand(edx));
1602 __ j(not_zero, &non_zero_result, taken);
1603 __ test(left->reg(), Operand(left->reg()));
1604 deferred->Branch(negative);
1605 __ bind(&non_zero_result);
1606 }
ager@chromium.orge2902be2009-06-08 12:21:35 +00001607 deferred->BindExit();
1608 left->Unuse();
1609 right->Unuse();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001610 answer = remainder;
ager@chromium.orge2902be2009-06-08 12:21:35 +00001611 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001612 ASSERT(answer.is_valid());
1613 return answer;
ager@chromium.orge2902be2009-06-08 12:21:35 +00001614 }
1615
1616 // Special handling of shift operations because they use fixed
1617 // registers.
1618 if (op == Token::SHL || op == Token::SHR || op == Token::SAR) {
1619 // Move left out of ecx if necessary.
1620 if (left->is_register() && left->reg().is(ecx)) {
1621 *left = allocator_->Allocate();
1622 ASSERT(left->is_valid());
1623 __ mov(left->reg(), ecx);
1624 }
1625 right->ToRegister(ecx);
1626 left->ToRegister();
1627 ASSERT(left->is_register() && !left->reg().is(ecx));
1628 ASSERT(right->is_register() && right->reg().is(ecx));
1629
1630 // We will modify right, it must be spilled.
1631 frame_->Spill(ecx);
1632
1633 // Use a fresh answer register to avoid spilling the left operand.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001634 answer = allocator_->Allocate();
ager@chromium.orge2902be2009-06-08 12:21:35 +00001635 ASSERT(answer.is_valid());
1636 // Check that both operands are smis using the answer register as a
1637 // temporary.
1638 DeferredInlineBinaryOperation* deferred =
1639 new DeferredInlineBinaryOperation(op,
1640 answer.reg(),
1641 left->reg(),
1642 ecx,
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00001643 left_type_info,
1644 right_type_info,
ager@chromium.orge2902be2009-06-08 12:21:35 +00001645 overwrite_mode);
ager@chromium.orge2902be2009-06-08 12:21:35 +00001646
fschneider@chromium.org086aac62010-03-17 13:18:24 +00001647 Label do_op, left_nonsmi;
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00001648 // If right is a smi we make a fast case if left is either a smi
fschneider@chromium.org086aac62010-03-17 13:18:24 +00001649 // or a heapnumber.
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00001650 if (CpuFeatures::IsSupported(SSE2) && right_type_info.IsSmi()) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00001651 CpuFeatures::Scope use_sse2(SSE2);
1652 __ mov(answer.reg(), left->reg());
1653 // Fast case - both are actually smis.
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00001654 if (!left_type_info.IsSmi()) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00001655 __ test(answer.reg(), Immediate(kSmiTagMask));
1656 __ j(not_zero, &left_nonsmi);
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00001657 } else {
1658 if (FLAG_debug_code) __ AbortIfNotSmi(left->reg());
fschneider@chromium.org086aac62010-03-17 13:18:24 +00001659 }
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00001660 if (FLAG_debug_code) __ AbortIfNotSmi(right->reg());
fschneider@chromium.org086aac62010-03-17 13:18:24 +00001661 __ SmiUntag(answer.reg());
1662 __ jmp(&do_op);
1663
1664 __ bind(&left_nonsmi);
1665 // Branch if not a heapnumber.
1666 __ cmp(FieldOperand(answer.reg(), HeapObject::kMapOffset),
1667 Factory::heap_number_map());
1668 deferred->Branch(not_equal);
1669
1670 // Load integer value into answer register using truncation.
1671 __ cvttsd2si(answer.reg(),
1672 FieldOperand(answer.reg(), HeapNumber::kValueOffset));
1673 // Branch if we do not fit in a smi.
1674 __ cmp(answer.reg(), 0xc0000000);
1675 deferred->Branch(negative);
1676 } else {
1677 CheckTwoForSminess(masm_, left->reg(), right->reg(), answer.reg(),
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00001678 left_type_info, right_type_info, deferred);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00001679
1680 // Untag both operands.
1681 __ mov(answer.reg(), left->reg());
1682 __ SmiUntag(answer.reg());
1683 }
1684
1685 __ bind(&do_op);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001686 __ SmiUntag(ecx);
ager@chromium.orge2902be2009-06-08 12:21:35 +00001687 // Perform the operation.
1688 switch (op) {
1689 case Token::SAR:
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001690 __ sar_cl(answer.reg());
ager@chromium.orge2902be2009-06-08 12:21:35 +00001691 // No checks of result necessary
1692 break;
1693 case Token::SHR: {
1694 Label result_ok;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001695 __ shr_cl(answer.reg());
ager@chromium.orge2902be2009-06-08 12:21:35 +00001696 // Check that the *unsigned* result fits in a smi. Neither of
1697 // the two high-order bits can be set:
1698 // * 0x80000000: high bit would be lost when smi tagging.
1699 // * 0x40000000: this number would convert to negative when smi
1700 // tagging.
1701 // These two cases can only happen with shifts by 0 or 1 when
1702 // handed a valid smi. If the answer cannot be represented by a
1703 // smi, restore the left and right arguments, and jump to slow
1704 // case. The low bit of the left argument may be lost, but only
1705 // in a case where it is dropped anyway.
1706 __ test(answer.reg(), Immediate(0xc0000000));
1707 __ j(zero, &result_ok);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001708 __ SmiTag(ecx);
ager@chromium.orge2902be2009-06-08 12:21:35 +00001709 deferred->Jump();
1710 __ bind(&result_ok);
1711 break;
1712 }
1713 case Token::SHL: {
1714 Label result_ok;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001715 __ shl_cl(answer.reg());
ager@chromium.orge2902be2009-06-08 12:21:35 +00001716 // Check that the *signed* result fits in a smi.
1717 __ cmp(answer.reg(), 0xc0000000);
1718 __ j(positive, &result_ok);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001719 __ SmiTag(ecx);
ager@chromium.orge2902be2009-06-08 12:21:35 +00001720 deferred->Jump();
1721 __ bind(&result_ok);
1722 break;
1723 }
1724 default:
1725 UNREACHABLE();
1726 }
1727 // Smi-tag the result in answer.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001728 __ SmiTag(answer.reg());
ager@chromium.orge2902be2009-06-08 12:21:35 +00001729 deferred->BindExit();
1730 left->Unuse();
1731 right->Unuse();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001732 ASSERT(answer.is_valid());
1733 return answer;
ager@chromium.orge2902be2009-06-08 12:21:35 +00001734 }
1735
1736 // Handle the other binary operations.
1737 left->ToRegister();
1738 right->ToRegister();
1739 // A newly allocated register answer is used to hold the answer. The
1740 // registers containing left and right are not modified so they don't
1741 // need to be spilled in the fast case.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001742 answer = allocator_->Allocate();
ager@chromium.orge2902be2009-06-08 12:21:35 +00001743 ASSERT(answer.is_valid());
1744
1745 // Perform the smi tag check.
ager@chromium.org41826e72009-03-30 13:30:57 +00001746 DeferredInlineBinaryOperation* deferred =
ager@chromium.orge2902be2009-06-08 12:21:35 +00001747 new DeferredInlineBinaryOperation(op,
1748 answer.reg(),
1749 left->reg(),
1750 right->reg(),
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00001751 left_type_info,
1752 right_type_info,
ager@chromium.orge2902be2009-06-08 12:21:35 +00001753 overwrite_mode);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001754 CheckTwoForSminess(masm_, left->reg(), right->reg(), answer.reg(),
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00001755 left_type_info, right_type_info, deferred);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001756
ager@chromium.orge2902be2009-06-08 12:21:35 +00001757 __ mov(answer.reg(), left->reg());
1758 switch (op) {
1759 case Token::ADD:
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001760 __ add(answer.reg(), Operand(right->reg()));
ager@chromium.orge2902be2009-06-08 12:21:35 +00001761 deferred->Branch(overflow);
1762 break;
1763
1764 case Token::SUB:
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001765 __ sub(answer.reg(), Operand(right->reg()));
ager@chromium.orge2902be2009-06-08 12:21:35 +00001766 deferred->Branch(overflow);
1767 break;
1768
1769 case Token::MUL: {
1770 // If the smi tag is 0 we can just leave the tag on one operand.
1771 ASSERT(kSmiTag == 0); // Adjust code below if not the case.
1772 // Remove smi tag from the left operand (but keep sign).
1773 // Left-hand operand has been copied into answer.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001774 __ SmiUntag(answer.reg());
ager@chromium.orge2902be2009-06-08 12:21:35 +00001775 // Do multiplication of smis, leaving result in answer.
1776 __ imul(answer.reg(), Operand(right->reg()));
1777 // Go slow on overflows.
1778 deferred->Branch(overflow);
1779 // Check for negative zero result. If product is zero, and one
1780 // argument is negative, go to slow case. The frame is unchanged
1781 // in this block, so local control flow can use a Label rather
1782 // than a JumpTarget.
lrn@chromium.org25156de2010-04-06 13:10:27 +00001783 if (!expr->no_negative_zero()) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00001784 Label non_zero_result;
1785 __ test(answer.reg(), Operand(answer.reg()));
1786 __ j(not_zero, &non_zero_result, taken);
1787 __ mov(answer.reg(), left->reg());
1788 __ or_(answer.reg(), Operand(right->reg()));
1789 deferred->Branch(negative);
1790 __ xor_(answer.reg(), Operand(answer.reg())); // Positive 0 is correct.
1791 __ bind(&non_zero_result);
1792 }
ager@chromium.orge2902be2009-06-08 12:21:35 +00001793 break;
1794 }
1795
1796 case Token::BIT_OR:
1797 __ or_(answer.reg(), Operand(right->reg()));
1798 break;
1799
1800 case Token::BIT_AND:
1801 __ and_(answer.reg(), Operand(right->reg()));
1802 break;
1803
1804 case Token::BIT_XOR:
1805 __ xor_(answer.reg(), Operand(right->reg()));
1806 break;
1807
1808 default:
1809 UNREACHABLE();
1810 break;
1811 }
1812 deferred->BindExit();
1813 left->Unuse();
1814 right->Unuse();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001815 ASSERT(answer.is_valid());
1816 return answer;
ager@chromium.org41826e72009-03-30 13:30:57 +00001817}
1818
1819
ager@chromium.orge2902be2009-06-08 12:21:35 +00001820// Call the appropriate binary operation stub to compute src op value
1821// and leave the result in dst.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001822class DeferredInlineSmiOperation: public DeferredCode {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001823 public:
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00001824 DeferredInlineSmiOperation(Token::Value op,
ager@chromium.orge2902be2009-06-08 12:21:35 +00001825 Register dst,
1826 Register src,
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00001827 TypeInfo type_info,
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001828 Smi* value,
1829 OverwriteMode overwrite_mode)
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00001830 : op_(op),
ager@chromium.orge2902be2009-06-08 12:21:35 +00001831 dst_(dst),
1832 src_(src),
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00001833 type_info_(type_info),
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001834 value_(value),
1835 overwrite_mode_(overwrite_mode) {
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00001836 if (type_info.IsSmi()) overwrite_mode_ = NO_OVERWRITE;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001837 set_comment("[ DeferredInlineSmiOperation");
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001838 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001839
1840 virtual void Generate();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001841
1842 private:
1843 Token::Value op_;
ager@chromium.orge2902be2009-06-08 12:21:35 +00001844 Register dst_;
1845 Register src_;
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00001846 TypeInfo type_info_;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001847 Smi* value_;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001848 OverwriteMode overwrite_mode_;
1849};
1850
1851
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001852void DeferredInlineSmiOperation::Generate() {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001853 // For mod we don't generate all the Smi code inline.
1854 GenericBinaryOpStub stub(
1855 op_,
1856 overwrite_mode_,
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001857 (op_ == Token::MOD) ? NO_GENERIC_BINARY_FLAGS : NO_SMI_CODE_IN_STUB,
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00001858 TypeInfo::Combine(TypeInfo::Smi(), type_info_));
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001859 stub.GenerateCall(masm_, src_, value_);
ager@chromium.orge2902be2009-06-08 12:21:35 +00001860 if (!dst_.is(eax)) __ mov(dst_, eax);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001861}
1862
1863
ager@chromium.orge2902be2009-06-08 12:21:35 +00001864// Call the appropriate binary operation stub to compute value op src
1865// and leave the result in dst.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001866class DeferredInlineSmiOperationReversed: public DeferredCode {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001867 public:
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00001868 DeferredInlineSmiOperationReversed(Token::Value op,
ager@chromium.orge2902be2009-06-08 12:21:35 +00001869 Register dst,
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001870 Smi* value,
ager@chromium.orge2902be2009-06-08 12:21:35 +00001871 Register src,
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00001872 TypeInfo type_info,
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001873 OverwriteMode overwrite_mode)
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00001874 : op_(op),
ager@chromium.orge2902be2009-06-08 12:21:35 +00001875 dst_(dst),
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00001876 type_info_(type_info),
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001877 value_(value),
ager@chromium.orge2902be2009-06-08 12:21:35 +00001878 src_(src),
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001879 overwrite_mode_(overwrite_mode) {
1880 set_comment("[ DeferredInlineSmiOperationReversed");
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001881 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001882
1883 virtual void Generate();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001884
1885 private:
1886 Token::Value op_;
ager@chromium.orge2902be2009-06-08 12:21:35 +00001887 Register dst_;
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00001888 TypeInfo type_info_;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001889 Smi* value_;
ager@chromium.orge2902be2009-06-08 12:21:35 +00001890 Register src_;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001891 OverwriteMode overwrite_mode_;
1892};
1893
1894
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001895void DeferredInlineSmiOperationReversed::Generate() {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001896 GenericBinaryOpStub igostub(
1897 op_,
1898 overwrite_mode_,
1899 NO_SMI_CODE_IN_STUB,
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00001900 TypeInfo::Combine(TypeInfo::Smi(), type_info_));
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001901 igostub.GenerateCall(masm_, value_, src_);
ager@chromium.orge2902be2009-06-08 12:21:35 +00001902 if (!dst_.is(eax)) __ mov(dst_, eax);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001903}
1904
1905
ager@chromium.orge2902be2009-06-08 12:21:35 +00001906// The result of src + value is in dst. It either overflowed or was not
1907// smi tagged. Undo the speculative addition and call the appropriate
1908// specialized stub for add. The result is left in dst.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001909class DeferredInlineSmiAdd: public DeferredCode {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001910 public:
ager@chromium.orge2902be2009-06-08 12:21:35 +00001911 DeferredInlineSmiAdd(Register dst,
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00001912 TypeInfo type_info,
ager@chromium.orge2902be2009-06-08 12:21:35 +00001913 Smi* value,
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001914 OverwriteMode overwrite_mode)
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001915 : dst_(dst),
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00001916 type_info_(type_info),
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001917 value_(value),
1918 overwrite_mode_(overwrite_mode) {
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00001919 if (type_info_.IsSmi()) overwrite_mode_ = NO_OVERWRITE;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001920 set_comment("[ DeferredInlineSmiAdd");
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001921 }
1922
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001923 virtual void Generate();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001924
1925 private:
ager@chromium.orge2902be2009-06-08 12:21:35 +00001926 Register dst_;
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00001927 TypeInfo type_info_;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001928 Smi* value_;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001929 OverwriteMode overwrite_mode_;
1930};
1931
1932
ager@chromium.orge2902be2009-06-08 12:21:35 +00001933void DeferredInlineSmiAdd::Generate() {
1934 // Undo the optimistic add operation and call the shared stub.
1935 __ sub(Operand(dst_), Immediate(value_));
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001936 GenericBinaryOpStub igostub(
1937 Token::ADD,
1938 overwrite_mode_,
1939 NO_SMI_CODE_IN_STUB,
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00001940 TypeInfo::Combine(TypeInfo::Smi(), type_info_));
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001941 igostub.GenerateCall(masm_, dst_, value_);
ager@chromium.orge2902be2009-06-08 12:21:35 +00001942 if (!dst_.is(eax)) __ mov(dst_, eax);
1943}
1944
1945
1946// The result of value + src is in dst. It either overflowed or was not
1947// smi tagged. Undo the speculative addition and call the appropriate
1948// specialized stub for add. The result is left in dst.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001949class DeferredInlineSmiAddReversed: public DeferredCode {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001950 public:
ager@chromium.orge2902be2009-06-08 12:21:35 +00001951 DeferredInlineSmiAddReversed(Register dst,
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00001952 TypeInfo type_info,
ager@chromium.orge2902be2009-06-08 12:21:35 +00001953 Smi* value,
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001954 OverwriteMode overwrite_mode)
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001955 : dst_(dst),
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00001956 type_info_(type_info),
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001957 value_(value),
1958 overwrite_mode_(overwrite_mode) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001959 set_comment("[ DeferredInlineSmiAddReversed");
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001960 }
1961
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001962 virtual void Generate();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001963
1964 private:
ager@chromium.orge2902be2009-06-08 12:21:35 +00001965 Register dst_;
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00001966 TypeInfo type_info_;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001967 Smi* value_;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001968 OverwriteMode overwrite_mode_;
1969};
1970
1971
ager@chromium.orge2902be2009-06-08 12:21:35 +00001972void DeferredInlineSmiAddReversed::Generate() {
1973 // Undo the optimistic add operation and call the shared stub.
1974 __ sub(Operand(dst_), Immediate(value_));
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001975 GenericBinaryOpStub igostub(
1976 Token::ADD,
1977 overwrite_mode_,
1978 NO_SMI_CODE_IN_STUB,
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00001979 TypeInfo::Combine(TypeInfo::Smi(), type_info_));
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001980 igostub.GenerateCall(masm_, value_, dst_);
ager@chromium.orge2902be2009-06-08 12:21:35 +00001981 if (!dst_.is(eax)) __ mov(dst_, eax);
1982}
1983
1984
1985// The result of src - value is in dst. It either overflowed or was not
1986// smi tagged. Undo the speculative subtraction and call the
1987// appropriate specialized stub for subtract. The result is left in
1988// dst.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001989class DeferredInlineSmiSub: public DeferredCode {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001990 public:
ager@chromium.orge2902be2009-06-08 12:21:35 +00001991 DeferredInlineSmiSub(Register dst,
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00001992 TypeInfo type_info,
ager@chromium.orge2902be2009-06-08 12:21:35 +00001993 Smi* value,
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001994 OverwriteMode overwrite_mode)
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001995 : dst_(dst),
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00001996 type_info_(type_info),
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001997 value_(value),
1998 overwrite_mode_(overwrite_mode) {
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00001999 if (type_info.IsSmi()) overwrite_mode_ = NO_OVERWRITE;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002000 set_comment("[ DeferredInlineSmiSub");
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002001 }
2002
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002003 virtual void Generate();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002004
2005 private:
ager@chromium.orge2902be2009-06-08 12:21:35 +00002006 Register dst_;
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00002007 TypeInfo type_info_;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002008 Smi* value_;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002009 OverwriteMode overwrite_mode_;
2010};
2011
2012
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002013void DeferredInlineSmiSub::Generate() {
2014 // Undo the optimistic sub operation and call the shared stub.
ager@chromium.orge2902be2009-06-08 12:21:35 +00002015 __ add(Operand(dst_), Immediate(value_));
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00002016 GenericBinaryOpStub igostub(
2017 Token::SUB,
2018 overwrite_mode_,
2019 NO_SMI_CODE_IN_STUB,
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00002020 TypeInfo::Combine(TypeInfo::Smi(), type_info_));
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002021 igostub.GenerateCall(masm_, dst_, value_);
ager@chromium.orge2902be2009-06-08 12:21:35 +00002022 if (!dst_.is(eax)) __ mov(dst_, eax);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002023}
2024
2025
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00002026Result CodeGenerator::ConstantSmiBinaryOperation(BinaryOperation* expr,
2027 Result* operand,
2028 Handle<Object> value,
2029 bool reversed,
2030 OverwriteMode overwrite_mode) {
2031 // Generate inline code for a binary operation when one of the
2032 // operands is a constant smi. Consumes the argument "operand".
ager@chromium.org41826e72009-03-30 13:30:57 +00002033 if (IsUnsafeSmi(value)) {
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00002034 Result unsafe_operand(value);
ager@chromium.org41826e72009-03-30 13:30:57 +00002035 if (reversed) {
lrn@chromium.org25156de2010-04-06 13:10:27 +00002036 return LikelySmiBinaryOperation(expr, &unsafe_operand, operand,
2037 overwrite_mode);
ager@chromium.org41826e72009-03-30 13:30:57 +00002038 } else {
lrn@chromium.org25156de2010-04-06 13:10:27 +00002039 return LikelySmiBinaryOperation(expr, operand, &unsafe_operand,
2040 overwrite_mode);
ager@chromium.org41826e72009-03-30 13:30:57 +00002041 }
ager@chromium.org41826e72009-03-30 13:30:57 +00002042 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002043
2044 // Get the literal value.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002045 Smi* smi_value = Smi::cast(*value);
2046 int int_value = smi_value->value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002047
lrn@chromium.org25156de2010-04-06 13:10:27 +00002048 Token::Value op = expr->op();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002049 Result answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002050 switch (op) {
2051 case Token::ADD: {
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00002052 operand->ToRegister();
2053 frame_->Spill(operand->reg());
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00002054
ager@chromium.orge2902be2009-06-08 12:21:35 +00002055 // Optimistically add. Call the specialized add stub if the
2056 // result is not a smi or overflows.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002057 DeferredCode* deferred = NULL;
ager@chromium.org41826e72009-03-30 13:30:57 +00002058 if (reversed) {
ager@chromium.orge2902be2009-06-08 12:21:35 +00002059 deferred = new DeferredInlineSmiAddReversed(operand->reg(),
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00002060 operand->type_info(),
ager@chromium.orge2902be2009-06-08 12:21:35 +00002061 smi_value,
2062 overwrite_mode);
ager@chromium.org41826e72009-03-30 13:30:57 +00002063 } else {
ager@chromium.orge2902be2009-06-08 12:21:35 +00002064 deferred = new DeferredInlineSmiAdd(operand->reg(),
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00002065 operand->type_info(),
ager@chromium.orge2902be2009-06-08 12:21:35 +00002066 smi_value,
2067 overwrite_mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002068 }
ager@chromium.orge2902be2009-06-08 12:21:35 +00002069 __ add(Operand(operand->reg()), Immediate(value));
2070 deferred->Branch(overflow);
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00002071 if (!operand->type_info().IsSmi()) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00002072 __ test(operand->reg(), Immediate(kSmiTagMask));
2073 deferred->Branch(not_zero);
ager@chromium.orgac091b72010-05-05 07:34:42 +00002074 } else if (FLAG_debug_code) {
2075 __ AbortIfNotSmi(operand->reg());
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00002076 }
ager@chromium.orge2902be2009-06-08 12:21:35 +00002077 deferred->BindExit();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002078 answer = *operand;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002079 break;
2080 }
2081
2082 case Token::SUB: {
2083 DeferredCode* deferred = NULL;
ager@chromium.org41826e72009-03-30 13:30:57 +00002084 if (reversed) {
ager@chromium.orge2902be2009-06-08 12:21:35 +00002085 // The reversed case is only hit when the right operand is not a
2086 // constant.
2087 ASSERT(operand->is_register());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002088 answer = allocator()->Allocate();
2089 ASSERT(answer.is_valid());
ager@chromium.org41826e72009-03-30 13:30:57 +00002090 __ Set(answer.reg(), Immediate(value));
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00002091 deferred =
2092 new DeferredInlineSmiOperationReversed(op,
2093 answer.reg(),
2094 smi_value,
2095 operand->reg(),
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00002096 operand->type_info(),
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00002097 overwrite_mode);
ager@chromium.org65dad4b2009-04-23 08:48:43 +00002098 __ sub(answer.reg(), Operand(operand->reg()));
ager@chromium.org41826e72009-03-30 13:30:57 +00002099 } else {
2100 operand->ToRegister();
2101 frame_->Spill(operand->reg());
ager@chromium.org41826e72009-03-30 13:30:57 +00002102 answer = *operand;
ager@chromium.orge2902be2009-06-08 12:21:35 +00002103 deferred = new DeferredInlineSmiSub(operand->reg(),
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00002104 operand->type_info(),
ager@chromium.orge2902be2009-06-08 12:21:35 +00002105 smi_value,
2106 overwrite_mode);
2107 __ sub(Operand(operand->reg()), Immediate(value));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002108 }
ager@chromium.orge2902be2009-06-08 12:21:35 +00002109 deferred->Branch(overflow);
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00002110 if (!operand->type_info().IsSmi()) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00002111 __ test(answer.reg(), Immediate(kSmiTagMask));
2112 deferred->Branch(not_zero);
ager@chromium.orgac091b72010-05-05 07:34:42 +00002113 } else if (FLAG_debug_code) {
2114 __ AbortIfNotSmi(operand->reg());
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00002115 }
ager@chromium.orge2902be2009-06-08 12:21:35 +00002116 deferred->BindExit();
ager@chromium.org41826e72009-03-30 13:30:57 +00002117 operand->Unuse();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002118 break;
2119 }
2120
ager@chromium.orge2902be2009-06-08 12:21:35 +00002121 case Token::SAR:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002122 if (reversed) {
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00002123 Result constant_operand(value);
lrn@chromium.org25156de2010-04-06 13:10:27 +00002124 answer = LikelySmiBinaryOperation(expr, &constant_operand, operand,
2125 overwrite_mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002126 } else {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002127 // Only the least significant 5 bits of the shift value are used.
2128 // In the slow case, this masking is done inside the runtime call.
2129 int shift_value = int_value & 0x1f;
ager@chromium.org41826e72009-03-30 13:30:57 +00002130 operand->ToRegister();
ager@chromium.orge2902be2009-06-08 12:21:35 +00002131 frame_->Spill(operand->reg());
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00002132 if (!operand->type_info().IsSmi()) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00002133 DeferredInlineSmiOperation* deferred =
2134 new DeferredInlineSmiOperation(op,
2135 operand->reg(),
2136 operand->reg(),
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00002137 operand->type_info(),
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00002138 smi_value,
2139 overwrite_mode);
2140 __ test(operand->reg(), Immediate(kSmiTagMask));
2141 deferred->Branch(not_zero);
2142 if (shift_value > 0) {
2143 __ sar(operand->reg(), shift_value);
2144 __ and_(operand->reg(), ~kSmiTagMask);
2145 }
2146 deferred->BindExit();
2147 } else {
ager@chromium.orgac091b72010-05-05 07:34:42 +00002148 if (FLAG_debug_code) {
2149 __ AbortIfNotSmi(operand->reg());
2150 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00002151 if (shift_value > 0) {
2152 __ sar(operand->reg(), shift_value);
2153 __ and_(operand->reg(), ~kSmiTagMask);
2154 }
ager@chromium.org41826e72009-03-30 13:30:57 +00002155 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002156 answer = *operand;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002157 }
2158 break;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002159
ager@chromium.orge2902be2009-06-08 12:21:35 +00002160 case Token::SHR:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002161 if (reversed) {
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00002162 Result constant_operand(value);
lrn@chromium.org25156de2010-04-06 13:10:27 +00002163 answer = LikelySmiBinaryOperation(expr, &constant_operand, operand,
2164 overwrite_mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002165 } else {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002166 // Only the least significant 5 bits of the shift value are used.
2167 // In the slow case, this masking is done inside the runtime call.
2168 int shift_value = int_value & 0x1f;
ager@chromium.org41826e72009-03-30 13:30:57 +00002169 operand->ToRegister();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002170 answer = allocator()->Allocate();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002171 ASSERT(answer.is_valid());
ager@chromium.orge2902be2009-06-08 12:21:35 +00002172 DeferredInlineSmiOperation* deferred =
2173 new DeferredInlineSmiOperation(op,
2174 answer.reg(),
2175 operand->reg(),
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00002176 operand->type_info(),
ager@chromium.orge2902be2009-06-08 12:21:35 +00002177 smi_value,
2178 overwrite_mode);
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00002179 if (!operand->type_info().IsSmi()) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00002180 __ test(operand->reg(), Immediate(kSmiTagMask));
2181 deferred->Branch(not_zero);
ager@chromium.orgac091b72010-05-05 07:34:42 +00002182 } else if (FLAG_debug_code) {
2183 __ AbortIfNotSmi(operand->reg());
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00002184 }
ager@chromium.org41826e72009-03-30 13:30:57 +00002185 __ mov(answer.reg(), operand->reg());
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002186 __ SmiUntag(answer.reg());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002187 __ shr(answer.reg(), shift_value);
2188 // A negative Smi shifted right two is in the positive Smi range.
2189 if (shift_value < 2) {
2190 __ test(answer.reg(), Immediate(0xc0000000));
ager@chromium.orge2902be2009-06-08 12:21:35 +00002191 deferred->Branch(not_zero);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002192 }
ager@chromium.org41826e72009-03-30 13:30:57 +00002193 operand->Unuse();
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002194 __ SmiTag(answer.reg());
ager@chromium.orge2902be2009-06-08 12:21:35 +00002195 deferred->BindExit();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002196 }
2197 break;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002198
ager@chromium.orge2902be2009-06-08 12:21:35 +00002199 case Token::SHL:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002200 if (reversed) {
ager@chromium.orgac091b72010-05-05 07:34:42 +00002201 // Move operand into ecx and also into a second register.
2202 // If operand is already in a register, take advantage of that.
2203 // This lets us modify ecx, but still bail out to deferred code.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002204 Result right;
2205 Result right_copy_in_ecx;
ager@chromium.orgac091b72010-05-05 07:34:42 +00002206 TypeInfo right_type_info = operand->type_info();
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002207 operand->ToRegister();
2208 if (operand->reg().is(ecx)) {
2209 right = allocator()->Allocate();
2210 __ mov(right.reg(), ecx);
2211 frame_->Spill(ecx);
2212 right_copy_in_ecx = *operand;
2213 } else {
2214 right_copy_in_ecx = allocator()->Allocate(ecx);
2215 __ mov(ecx, operand->reg());
2216 right = *operand;
2217 }
2218 operand->Unuse();
2219
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002220 answer = allocator()->Allocate();
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002221 DeferredInlineSmiOperationReversed* deferred =
2222 new DeferredInlineSmiOperationReversed(op,
2223 answer.reg(),
2224 smi_value,
2225 right.reg(),
ager@chromium.orgac091b72010-05-05 07:34:42 +00002226 right_type_info,
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002227 overwrite_mode);
2228 __ mov(answer.reg(), Immediate(int_value));
2229 __ sar(ecx, kSmiTagSize);
ager@chromium.orgac091b72010-05-05 07:34:42 +00002230 if (!right_type_info.IsSmi()) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00002231 deferred->Branch(carry);
ager@chromium.orgac091b72010-05-05 07:34:42 +00002232 } else if (FLAG_debug_code) {
2233 __ AbortIfNotSmi(right.reg());
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00002234 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002235 __ shl_cl(answer.reg());
2236 __ cmp(answer.reg(), 0xc0000000);
2237 deferred->Branch(sign);
2238 __ SmiTag(answer.reg());
2239
2240 deferred->BindExit();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002241 } else {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002242 // Only the least significant 5 bits of the shift value are used.
2243 // In the slow case, this masking is done inside the runtime call.
2244 int shift_value = int_value & 0x1f;
ager@chromium.org41826e72009-03-30 13:30:57 +00002245 operand->ToRegister();
ager@chromium.orge2902be2009-06-08 12:21:35 +00002246 if (shift_value == 0) {
kasperl@chromium.orga1db2ce2009-06-10 12:48:11 +00002247 // Spill operand so it can be overwritten in the slow case.
2248 frame_->Spill(operand->reg());
ager@chromium.orge2902be2009-06-08 12:21:35 +00002249 DeferredInlineSmiOperation* deferred =
2250 new DeferredInlineSmiOperation(op,
2251 operand->reg(),
2252 operand->reg(),
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00002253 operand->type_info(),
ager@chromium.orge2902be2009-06-08 12:21:35 +00002254 smi_value,
2255 overwrite_mode);
2256 __ test(operand->reg(), Immediate(kSmiTagMask));
2257 deferred->Branch(not_zero);
2258 deferred->BindExit();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002259 answer = *operand;
ager@chromium.orge2902be2009-06-08 12:21:35 +00002260 } else {
2261 // Use a fresh temporary for nonzero shift values.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002262 answer = allocator()->Allocate();
ager@chromium.org65dad4b2009-04-23 08:48:43 +00002263 ASSERT(answer.is_valid());
ager@chromium.orge2902be2009-06-08 12:21:35 +00002264 DeferredInlineSmiOperation* deferred =
2265 new DeferredInlineSmiOperation(op,
2266 answer.reg(),
2267 operand->reg(),
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00002268 operand->type_info(),
ager@chromium.orge2902be2009-06-08 12:21:35 +00002269 smi_value,
2270 overwrite_mode);
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00002271 if (!operand->type_info().IsSmi()) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00002272 __ test(operand->reg(), Immediate(kSmiTagMask));
2273 deferred->Branch(not_zero);
ager@chromium.orgac091b72010-05-05 07:34:42 +00002274 } else if (FLAG_debug_code) {
2275 __ AbortIfNotSmi(operand->reg());
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00002276 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00002277 __ mov(answer.reg(), operand->reg());
2278 ASSERT(kSmiTag == 0); // adjust code if not the case
2279 // We do no shifts, only the Smi conversion, if shift_value is 1.
2280 if (shift_value > 1) {
2281 __ shl(answer.reg(), shift_value - 1);
2282 }
2283 // Convert int result to Smi, checking that it is in int range.
ager@chromium.orge2902be2009-06-08 12:21:35 +00002284 ASSERT(kSmiTagSize == 1); // adjust code if not the case
ager@chromium.org65dad4b2009-04-23 08:48:43 +00002285 __ add(answer.reg(), Operand(answer.reg()));
ager@chromium.orge2902be2009-06-08 12:21:35 +00002286 deferred->Branch(overflow);
2287 deferred->BindExit();
ager@chromium.org65dad4b2009-04-23 08:48:43 +00002288 operand->Unuse();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002289 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002290 }
2291 break;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002292
2293 case Token::BIT_OR:
2294 case Token::BIT_XOR:
2295 case Token::BIT_AND: {
ager@chromium.orge2902be2009-06-08 12:21:35 +00002296 operand->ToRegister();
2297 frame_->Spill(operand->reg());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002298 DeferredCode* deferred = NULL;
ager@chromium.org41826e72009-03-30 13:30:57 +00002299 if (reversed) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00002300 deferred =
2301 new DeferredInlineSmiOperationReversed(op,
2302 operand->reg(),
2303 smi_value,
2304 operand->reg(),
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00002305 operand->type_info(),
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00002306 overwrite_mode);
ager@chromium.org41826e72009-03-30 13:30:57 +00002307 } else {
ager@chromium.orge2902be2009-06-08 12:21:35 +00002308 deferred = new DeferredInlineSmiOperation(op,
2309 operand->reg(),
2310 operand->reg(),
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00002311 operand->type_info(),
ager@chromium.orge2902be2009-06-08 12:21:35 +00002312 smi_value,
ager@chromium.org41826e72009-03-30 13:30:57 +00002313 overwrite_mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002314 }
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00002315 if (!operand->type_info().IsSmi()) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00002316 __ test(operand->reg(), Immediate(kSmiTagMask));
2317 deferred->Branch(not_zero);
ager@chromium.orgac091b72010-05-05 07:34:42 +00002318 } else if (FLAG_debug_code) {
2319 __ AbortIfNotSmi(operand->reg());
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00002320 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002321 if (op == Token::BIT_AND) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00002322 __ and_(Operand(operand->reg()), Immediate(value));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002323 } else if (op == Token::BIT_XOR) {
ager@chromium.org381abbb2009-02-25 13:23:22 +00002324 if (int_value != 0) {
ager@chromium.org41826e72009-03-30 13:30:57 +00002325 __ xor_(Operand(operand->reg()), Immediate(value));
ager@chromium.org381abbb2009-02-25 13:23:22 +00002326 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002327 } else {
2328 ASSERT(op == Token::BIT_OR);
ager@chromium.org381abbb2009-02-25 13:23:22 +00002329 if (int_value != 0) {
ager@chromium.org41826e72009-03-30 13:30:57 +00002330 __ or_(Operand(operand->reg()), Immediate(value));
ager@chromium.org381abbb2009-02-25 13:23:22 +00002331 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002332 }
ager@chromium.orge2902be2009-06-08 12:21:35 +00002333 deferred->BindExit();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002334 answer = *operand;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002335 break;
2336 }
2337
ager@chromium.org5c838252010-02-19 08:53:10 +00002338 case Token::DIV:
2339 if (!reversed && int_value == 2) {
2340 operand->ToRegister();
2341 frame_->Spill(operand->reg());
2342
2343 DeferredInlineSmiOperation* deferred =
2344 new DeferredInlineSmiOperation(op,
2345 operand->reg(),
2346 operand->reg(),
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00002347 operand->type_info(),
ager@chromium.org5c838252010-02-19 08:53:10 +00002348 smi_value,
2349 overwrite_mode);
2350 // Check that lowest log2(value) bits of operand are zero, and test
2351 // smi tag at the same time.
2352 ASSERT_EQ(0, kSmiTag);
2353 ASSERT_EQ(1, kSmiTagSize);
2354 __ test(operand->reg(), Immediate(3));
2355 deferred->Branch(not_zero); // Branch if non-smi or odd smi.
2356 __ sar(operand->reg(), 1);
2357 deferred->BindExit();
2358 answer = *operand;
2359 } else {
2360 // Cannot fall through MOD to default case, so we duplicate the
2361 // default case here.
2362 Result constant_operand(value);
2363 if (reversed) {
lrn@chromium.org25156de2010-04-06 13:10:27 +00002364 answer = LikelySmiBinaryOperation(expr, &constant_operand, operand,
2365 overwrite_mode);
ager@chromium.org5c838252010-02-19 08:53:10 +00002366 } else {
lrn@chromium.org25156de2010-04-06 13:10:27 +00002367 answer = LikelySmiBinaryOperation(expr, operand, &constant_operand,
2368 overwrite_mode);
ager@chromium.org5c838252010-02-19 08:53:10 +00002369 }
2370 }
2371 break;
ager@chromium.orgac091b72010-05-05 07:34:42 +00002372
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002373 // Generate inline code for mod of powers of 2 and negative powers of 2.
2374 case Token::MOD:
2375 if (!reversed &&
2376 int_value != 0 &&
2377 (IsPowerOf2(int_value) || IsPowerOf2(-int_value))) {
2378 operand->ToRegister();
2379 frame_->Spill(operand->reg());
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00002380 DeferredCode* deferred =
2381 new DeferredInlineSmiOperation(op,
2382 operand->reg(),
2383 operand->reg(),
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00002384 operand->type_info(),
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00002385 smi_value,
2386 overwrite_mode);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002387 // Check for negative or non-Smi left hand side.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00002388 __ test(operand->reg(), Immediate(kSmiTagMask | kSmiSignMask));
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002389 deferred->Branch(not_zero);
2390 if (int_value < 0) int_value = -int_value;
2391 if (int_value == 1) {
2392 __ mov(operand->reg(), Immediate(Smi::FromInt(0)));
2393 } else {
2394 __ and_(operand->reg(), (int_value << kSmiTagSize) - 1);
2395 }
2396 deferred->BindExit();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002397 answer = *operand;
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002398 break;
2399 }
2400 // Fall through if we did not find a power of 2 on the right hand side!
2401
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002402 default: {
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00002403 Result constant_operand(value);
ager@chromium.org41826e72009-03-30 13:30:57 +00002404 if (reversed) {
lrn@chromium.org25156de2010-04-06 13:10:27 +00002405 answer = LikelySmiBinaryOperation(expr, &constant_operand, operand,
2406 overwrite_mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002407 } else {
lrn@chromium.org25156de2010-04-06 13:10:27 +00002408 answer = LikelySmiBinaryOperation(expr, operand, &constant_operand,
2409 overwrite_mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002410 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002411 break;
2412 }
2413 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002414 ASSERT(answer.is_valid());
2415 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002416}
2417
2418
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002419static bool CouldBeNaN(const Result& result) {
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00002420 if (result.type_info().IsSmi()) return false;
2421 if (result.type_info().IsInteger32()) return false;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002422 if (!result.is_constant()) return true;
2423 if (!result.handle()->IsHeapNumber()) return false;
2424 return isnan(HeapNumber::cast(*result.handle())->value());
2425}
2426
2427
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00002428// Convert from signed to unsigned comparison to match the way EFLAGS are set
2429// by FPU and XMM compare instructions.
2430static Condition DoubleCondition(Condition cc) {
2431 switch (cc) {
2432 case less: return below;
2433 case equal: return equal;
2434 case less_equal: return below_equal;
2435 case greater: return above;
2436 case greater_equal: return above_equal;
2437 default: UNREACHABLE();
2438 }
2439 UNREACHABLE();
2440 return equal;
2441}
2442
2443
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00002444void CodeGenerator::Comparison(AstNode* node,
2445 Condition cc,
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002446 bool strict,
2447 ControlDestination* dest) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002448 // Strict only makes sense for equality comparisons.
2449 ASSERT(!strict || cc == equal);
2450
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00002451 Result left_side;
2452 Result right_side;
mads.s.ager@gmail.com769cc962008-08-06 10:02:49 +00002453 // Implement '>' and '<=' by reversal to obtain ECMA-262 conversion order.
2454 if (cc == greater || cc == less_equal) {
2455 cc = ReverseCondition(cc);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002456 left_side = frame_->Pop();
2457 right_side = frame_->Pop();
mads.s.ager@gmail.com769cc962008-08-06 10:02:49 +00002458 } else {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002459 right_side = frame_->Pop();
2460 left_side = frame_->Pop();
mads.s.ager@gmail.com769cc962008-08-06 10:02:49 +00002461 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002462 ASSERT(cc == less || cc == equal || cc == greater_equal);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002463
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002464 // If either side is a constant of some sort, we can probably optimize the
2465 // comparison.
2466 bool left_side_constant_smi = false;
2467 bool left_side_constant_null = false;
2468 bool left_side_constant_1_char_string = false;
2469 if (left_side.is_constant()) {
2470 left_side_constant_smi = left_side.handle()->IsSmi();
2471 left_side_constant_null = left_side.handle()->IsNull();
2472 left_side_constant_1_char_string =
2473 (left_side.handle()->IsString() &&
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00002474 String::cast(*left_side.handle())->length() == 1 &&
2475 String::cast(*left_side.handle())->IsAsciiRepresentation());
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002476 }
2477 bool right_side_constant_smi = false;
2478 bool right_side_constant_null = false;
2479 bool right_side_constant_1_char_string = false;
2480 if (right_side.is_constant()) {
2481 right_side_constant_smi = right_side.handle()->IsSmi();
2482 right_side_constant_null = right_side.handle()->IsNull();
2483 right_side_constant_1_char_string =
2484 (right_side.handle()->IsString() &&
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00002485 String::cast(*right_side.handle())->length() == 1 &&
2486 String::cast(*right_side.handle())->IsAsciiRepresentation());
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002487 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002488
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002489 if (left_side_constant_smi || right_side_constant_smi) {
2490 if (left_side_constant_smi && right_side_constant_smi) {
2491 // Trivial case, comparing two constants.
2492 int left_value = Smi::cast(*left_side.handle())->value();
2493 int right_value = Smi::cast(*right_side.handle())->value();
2494 switch (cc) {
2495 case less:
2496 dest->Goto(left_value < right_value);
2497 break;
2498 case equal:
2499 dest->Goto(left_value == right_value);
2500 break;
2501 case greater_equal:
2502 dest->Goto(left_value >= right_value);
2503 break;
2504 default:
2505 UNREACHABLE();
2506 }
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00002507 } else {
2508 // Only one side is a constant Smi.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002509 // If left side is a constant Smi, reverse the operands.
2510 // Since one side is a constant Smi, conversion order does not matter.
2511 if (left_side_constant_smi) {
2512 Result temp = left_side;
2513 left_side = right_side;
2514 right_side = temp;
2515 cc = ReverseCondition(cc);
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00002516 // This may re-introduce greater or less_equal as the value of cc.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002517 // CompareStub and the inline code both support all values of cc.
2518 }
2519 // Implement comparison against a constant Smi, inlining the case
2520 // where both sides are Smis.
2521 left_side.ToRegister();
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00002522 Register left_reg = left_side.reg();
2523 Handle<Object> right_val = right_side.handle();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002524
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002525 // Here we split control flow to the stub call and inlined cases
2526 // before finally splitting it to the control destination. We use
2527 // a jump target and branching to duplicate the virtual frame at
2528 // the first split. We manually handle the off-frame references
2529 // by reconstituting them on the non-fall-through path.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002530
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00002531 if (left_side.is_smi()) {
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00002532 if (FLAG_debug_code) {
2533 __ AbortIfNotSmi(left_side.reg());
2534 }
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00002535 } else {
2536 JumpTarget is_smi;
2537 __ test(left_side.reg(), Immediate(kSmiTagMask));
2538 is_smi.Branch(zero, taken);
2539
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00002540 bool is_loop_condition = (node->AsExpression() != NULL) &&
2541 node->AsExpression()->is_loop_condition();
2542 if (!is_loop_condition &&
2543 CpuFeatures::IsSupported(SSE2) &&
2544 right_val->IsSmi()) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00002545 // Right side is a constant smi and left side has been checked
2546 // not to be a smi.
2547 CpuFeatures::Scope use_sse2(SSE2);
2548 JumpTarget not_number;
2549 __ cmp(FieldOperand(left_reg, HeapObject::kMapOffset),
2550 Immediate(Factory::heap_number_map()));
2551 not_number.Branch(not_equal, &left_side);
2552 __ movdbl(xmm1,
2553 FieldOperand(left_reg, HeapNumber::kValueOffset));
2554 int value = Smi::cast(*right_val)->value();
2555 if (value == 0) {
2556 __ xorpd(xmm0, xmm0);
2557 } else {
2558 Result temp = allocator()->Allocate();
2559 __ mov(temp.reg(), Immediate(value));
2560 __ cvtsi2sd(xmm0, Operand(temp.reg()));
2561 temp.Unuse();
2562 }
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00002563 __ ucomisd(xmm1, xmm0);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00002564 // Jump to builtin for NaN.
2565 not_number.Branch(parity_even, &left_side);
2566 left_side.Unuse();
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00002567 dest->true_target()->Branch(DoubleCondition(cc));
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00002568 dest->false_target()->Jump();
2569 not_number.Bind(&left_side);
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00002570 }
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00002571
2572 // Setup and call the compare stub.
2573 CompareStub stub(cc, strict, kCantBothBeNaN);
2574 Result result = frame_->CallStub(&stub, &left_side, &right_side);
2575 result.ToRegister();
2576 __ cmp(result.reg(), 0);
2577 result.Unuse();
2578 dest->true_target()->Branch(cc);
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00002579 dest->false_target()->Jump();
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00002580
2581 is_smi.Bind();
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00002582 }
2583
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002584 left_side = Result(left_reg);
2585 right_side = Result(right_val);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002586 // Test smi equality and comparison by signed int comparison.
2587 if (IsUnsafeSmi(right_side.handle())) {
2588 right_side.ToRegister();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002589 __ cmp(left_side.reg(), Operand(right_side.reg()));
2590 } else {
2591 __ cmp(Operand(left_side.reg()), Immediate(right_side.handle()));
2592 }
2593 left_side.Unuse();
2594 right_side.Unuse();
2595 dest->Split(cc);
2596 }
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00002597
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002598 } else if (cc == equal &&
2599 (left_side_constant_null || right_side_constant_null)) {
2600 // To make null checks efficient, we check if either the left side or
2601 // the right side is the constant 'null'.
2602 // If so, we optimize the code by inlining a null check instead of
2603 // calling the (very) general runtime routine for checking equality.
2604 Result operand = left_side_constant_null ? right_side : left_side;
2605 right_side.Unuse();
2606 left_side.Unuse();
2607 operand.ToRegister();
2608 __ cmp(operand.reg(), Factory::null_value());
2609 if (strict) {
2610 operand.Unuse();
2611 dest->Split(equal);
2612 } else {
2613 // The 'null' value is only equal to 'undefined' if using non-strict
2614 // comparisons.
2615 dest->true_target()->Branch(equal);
2616 __ cmp(operand.reg(), Factory::undefined_value());
2617 dest->true_target()->Branch(equal);
2618 __ test(operand.reg(), Immediate(kSmiTagMask));
2619 dest->false_target()->Branch(equal);
2620
2621 // It can be an undetectable object.
2622 // Use a scratch register in preference to spilling operand.reg().
2623 Result temp = allocator()->Allocate();
2624 ASSERT(temp.is_valid());
2625 __ mov(temp.reg(),
2626 FieldOperand(operand.reg(), HeapObject::kMapOffset));
2627 __ movzx_b(temp.reg(),
2628 FieldOperand(temp.reg(), Map::kBitFieldOffset));
2629 __ test(temp.reg(), Immediate(1 << Map::kIsUndetectable));
2630 temp.Unuse();
2631 operand.Unuse();
2632 dest->Split(not_zero);
2633 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002634 } else if (left_side_constant_1_char_string ||
2635 right_side_constant_1_char_string) {
2636 if (left_side_constant_1_char_string && right_side_constant_1_char_string) {
2637 // Trivial case, comparing two constants.
2638 int left_value = String::cast(*left_side.handle())->Get(0);
2639 int right_value = String::cast(*right_side.handle())->Get(0);
2640 switch (cc) {
2641 case less:
2642 dest->Goto(left_value < right_value);
2643 break;
2644 case equal:
2645 dest->Goto(left_value == right_value);
2646 break;
2647 case greater_equal:
2648 dest->Goto(left_value >= right_value);
2649 break;
2650 default:
2651 UNREACHABLE();
2652 }
2653 } else {
2654 // Only one side is a constant 1 character string.
2655 // If left side is a constant 1-character string, reverse the operands.
2656 // Since one side is a constant string, conversion order does not matter.
2657 if (left_side_constant_1_char_string) {
2658 Result temp = left_side;
2659 left_side = right_side;
2660 right_side = temp;
2661 cc = ReverseCondition(cc);
2662 // This may reintroduce greater or less_equal as the value of cc.
2663 // CompareStub and the inline code both support all values of cc.
2664 }
2665 // Implement comparison against a constant string, inlining the case
2666 // where both sides are strings.
2667 left_side.ToRegister();
2668
2669 // Here we split control flow to the stub call and inlined cases
2670 // before finally splitting it to the control destination. We use
2671 // a jump target and branching to duplicate the virtual frame at
2672 // the first split. We manually handle the off-frame references
2673 // by reconstituting them on the non-fall-through path.
2674 JumpTarget is_not_string, is_string;
2675 Register left_reg = left_side.reg();
2676 Handle<Object> right_val = right_side.handle();
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00002677 ASSERT(StringShape(String::cast(*right_val)).IsSymbol());
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002678 __ test(left_side.reg(), Immediate(kSmiTagMask));
2679 is_not_string.Branch(zero, &left_side);
2680 Result temp = allocator_->Allocate();
2681 ASSERT(temp.is_valid());
2682 __ mov(temp.reg(),
2683 FieldOperand(left_side.reg(), HeapObject::kMapOffset));
2684 __ movzx_b(temp.reg(),
2685 FieldOperand(temp.reg(), Map::kInstanceTypeOffset));
2686 // If we are testing for equality then make use of the symbol shortcut.
2687 // Check if the right left hand side has the same type as the left hand
2688 // side (which is always a symbol).
2689 if (cc == equal) {
2690 Label not_a_symbol;
2691 ASSERT(kSymbolTag != 0);
2692 // Ensure that no non-strings have the symbol bit set.
2693 ASSERT(kNotStringTag + kIsSymbolMask > LAST_TYPE);
2694 __ test(temp.reg(), Immediate(kIsSymbolMask)); // Test the symbol bit.
2695 __ j(zero, &not_a_symbol);
2696 // They are symbols, so do identity compare.
2697 __ cmp(left_side.reg(), right_side.handle());
2698 dest->true_target()->Branch(equal);
2699 dest->false_target()->Branch(not_equal);
2700 __ bind(&not_a_symbol);
2701 }
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00002702 // Call the compare stub if the left side is not a flat ascii string.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002703 __ and_(temp.reg(),
2704 kIsNotStringMask | kStringRepresentationMask | kStringEncodingMask);
2705 __ cmp(temp.reg(), kStringTag | kSeqStringTag | kAsciiStringTag);
2706 temp.Unuse();
2707 is_string.Branch(equal, &left_side);
2708
2709 // Setup and call the compare stub.
2710 is_not_string.Bind(&left_side);
2711 CompareStub stub(cc, strict, kCantBothBeNaN);
2712 Result result = frame_->CallStub(&stub, &left_side, &right_side);
2713 result.ToRegister();
2714 __ cmp(result.reg(), 0);
2715 result.Unuse();
2716 dest->true_target()->Branch(cc);
2717 dest->false_target()->Jump();
2718
2719 is_string.Bind(&left_side);
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00002720 // left_side is a sequential ASCII string.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002721 left_side = Result(left_reg);
2722 right_side = Result(right_val);
2723 Result temp2 = allocator_->Allocate();
2724 ASSERT(temp2.is_valid());
2725 // Test string equality and comparison.
2726 if (cc == equal) {
2727 Label comparison_done;
2728 __ cmp(FieldOperand(left_side.reg(), String::kLengthOffset),
ager@chromium.orgac091b72010-05-05 07:34:42 +00002729 Immediate(Smi::FromInt(1)));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002730 __ j(not_equal, &comparison_done);
2731 uint8_t char_value =
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00002732 static_cast<uint8_t>(String::cast(*right_val)->Get(0));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002733 __ cmpb(FieldOperand(left_side.reg(), SeqAsciiString::kHeaderSize),
2734 char_value);
2735 __ bind(&comparison_done);
2736 } else {
2737 __ mov(temp2.reg(),
2738 FieldOperand(left_side.reg(), String::kLengthOffset));
ager@chromium.orgac091b72010-05-05 07:34:42 +00002739 __ SmiUntag(temp2.reg());
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002740 __ sub(Operand(temp2.reg()), Immediate(1));
2741 Label comparison;
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00002742 // If the length is 0 then the subtraction gave -1 which compares less
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002743 // than any character.
2744 __ j(negative, &comparison);
2745 // Otherwise load the first character.
2746 __ movzx_b(temp2.reg(),
2747 FieldOperand(left_side.reg(), SeqAsciiString::kHeaderSize));
2748 __ bind(&comparison);
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00002749 // Compare the first character of the string with the
2750 // constant 1-character string.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002751 uint8_t char_value =
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00002752 static_cast<uint8_t>(String::cast(*right_val)->Get(0));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002753 __ cmp(Operand(temp2.reg()), Immediate(char_value));
2754 Label characters_were_different;
2755 __ j(not_equal, &characters_were_different);
2756 // If the first character is the same then the long string sorts after
2757 // the short one.
2758 __ cmp(FieldOperand(left_side.reg(), String::kLengthOffset),
ager@chromium.orgac091b72010-05-05 07:34:42 +00002759 Immediate(Smi::FromInt(1)));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002760 __ bind(&characters_were_different);
2761 }
2762 temp2.Unuse();
2763 left_side.Unuse();
2764 right_side.Unuse();
2765 dest->Split(cc);
2766 }
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00002767 } else {
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00002768 // Neither side is a constant Smi, constant 1-char string or constant null.
2769 // If either side is a non-smi constant, or known to be a heap number skip
2770 // the smi check.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002771 bool known_non_smi =
2772 (left_side.is_constant() && !left_side.handle()->IsSmi()) ||
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00002773 (right_side.is_constant() && !right_side.handle()->IsSmi()) ||
2774 left_side.type_info().IsDouble() ||
2775 right_side.type_info().IsDouble();
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002776 NaNInformation nan_info =
2777 (CouldBeNaN(left_side) && CouldBeNaN(right_side)) ?
2778 kBothCouldBeNaN :
2779 kCantBothBeNaN;
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00002780
2781 // Inline number comparison handling any combination of smi's and heap
2782 // numbers if:
2783 // code is in a loop
2784 // the compare operation is different from equal
2785 // compare is not a for-loop comparison
2786 // The reason for excluding equal is that it will most likely be done
2787 // with smi's (not heap numbers) and the code to comparing smi's is inlined
2788 // separately. The same reason applies for for-loop comparison which will
2789 // also most likely be smi comparisons.
2790 bool is_loop_condition = (node->AsExpression() != NULL)
2791 && node->AsExpression()->is_loop_condition();
2792 bool inline_number_compare =
2793 loop_nesting() > 0 && cc != equal && !is_loop_condition;
2794
2795 // Left and right needed in registers for the following code.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002796 left_side.ToRegister();
2797 right_side.ToRegister();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002798
2799 if (known_non_smi) {
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00002800 // Inline the equality check if both operands can't be a NaN. If both
2801 // objects are the same they are equal.
2802 if (nan_info == kCantBothBeNaN && cc == equal) {
2803 __ cmp(left_side.reg(), Operand(right_side.reg()));
2804 dest->true_target()->Branch(equal);
2805 }
2806
2807 // Inline number comparison.
2808 if (inline_number_compare) {
2809 GenerateInlineNumberComparison(&left_side, &right_side, cc, dest);
2810 }
2811
2812 // End of in-line compare, call out to the compare stub. Don't include
2813 // number comparison in the stub if it was inlined.
2814 CompareStub stub(cc, strict, nan_info, !inline_number_compare);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002815 Result answer = frame_->CallStub(&stub, &left_side, &right_side);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00002816 __ test(answer.reg(), Operand(answer.reg()));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002817 answer.Unuse();
2818 dest->Split(cc);
2819 } else {
2820 // Here we split control flow to the stub call and inlined cases
2821 // before finally splitting it to the control destination. We use
2822 // a jump target and branching to duplicate the virtual frame at
2823 // the first split. We manually handle the off-frame references
2824 // by reconstituting them on the non-fall-through path.
2825 JumpTarget is_smi;
2826 Register left_reg = left_side.reg();
2827 Register right_reg = right_side.reg();
2828
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00002829 // In-line check for comparing two smis.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002830 Result temp = allocator_->Allocate();
2831 ASSERT(temp.is_valid());
2832 __ mov(temp.reg(), left_side.reg());
2833 __ or_(temp.reg(), Operand(right_side.reg()));
2834 __ test(temp.reg(), Immediate(kSmiTagMask));
2835 temp.Unuse();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002836 is_smi.Branch(zero, taken);
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00002837
2838 // Inline the equality check if both operands can't be a NaN. If both
2839 // objects are the same they are equal.
2840 if (nan_info == kCantBothBeNaN && cc == equal) {
2841 __ cmp(left_side.reg(), Operand(right_side.reg()));
2842 dest->true_target()->Branch(equal);
2843 }
2844
2845 // Inline number comparison.
2846 if (inline_number_compare) {
2847 GenerateInlineNumberComparison(&left_side, &right_side, cc, dest);
2848 }
2849
2850 // End of in-line compare, call out to the compare stub. Don't include
2851 // number comparison in the stub if it was inlined.
2852 CompareStub stub(cc, strict, nan_info, !inline_number_compare);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002853 Result answer = frame_->CallStub(&stub, &left_side, &right_side);
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00002854 __ test(answer.reg(), Operand(answer.reg()));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002855 answer.Unuse();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002856 dest->true_target()->Branch(cc);
2857 dest->false_target()->Jump();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002858
2859 is_smi.Bind();
2860 left_side = Result(left_reg);
2861 right_side = Result(right_reg);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002862 __ cmp(left_side.reg(), Operand(right_side.reg()));
2863 right_side.Unuse();
2864 left_side.Unuse();
2865 dest->Split(cc);
2866 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002867 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002868}
2869
2870
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00002871// Check that the comparison operand is a number. Jump to not_numbers jump
2872// target passing the left and right result if the operand is not a number.
2873static void CheckComparisonOperand(MacroAssembler* masm_,
2874 Result* operand,
2875 Result* left_side,
2876 Result* right_side,
2877 JumpTarget* not_numbers) {
2878 // Perform check if operand is not known to be a number.
2879 if (!operand->type_info().IsNumber()) {
2880 Label done;
2881 __ test(operand->reg(), Immediate(kSmiTagMask));
2882 __ j(zero, &done);
2883 __ cmp(FieldOperand(operand->reg(), HeapObject::kMapOffset),
2884 Immediate(Factory::heap_number_map()));
2885 not_numbers->Branch(not_equal, left_side, right_side, not_taken);
2886 __ bind(&done);
2887 }
2888}
2889
2890
2891// Load a comparison operand to the FPU stack. This assumes that the operand has
2892// already been checked and is a number.
2893static void LoadComparisonOperand(MacroAssembler* masm_,
2894 Result* operand) {
2895 Label done;
2896 if (operand->type_info().IsDouble()) {
2897 // Operand is known to be a heap number, just load it.
2898 __ fld_d(FieldOperand(operand->reg(), HeapNumber::kValueOffset));
2899 } else if (operand->type_info().IsSmi()) {
2900 // Operand is known to be a smi. Convert it to double and keep the original
2901 // smi.
2902 __ SmiUntag(operand->reg());
2903 __ push(operand->reg());
2904 __ fild_s(Operand(esp, 0));
2905 __ pop(operand->reg());
2906 __ SmiTag(operand->reg());
2907 } else {
2908 // Operand type not known, check for smi otherwise assume heap number.
2909 Label smi;
2910 __ test(operand->reg(), Immediate(kSmiTagMask));
2911 __ j(zero, &smi);
2912 __ fld_d(FieldOperand(operand->reg(), HeapNumber::kValueOffset));
2913 __ jmp(&done);
2914 __ bind(&smi);
2915 __ SmiUntag(operand->reg());
2916 __ push(operand->reg());
2917 __ fild_s(Operand(esp, 0));
2918 __ pop(operand->reg());
2919 __ SmiTag(operand->reg());
2920 __ jmp(&done);
2921 }
2922 __ bind(&done);
2923}
2924
2925
2926// Load a comparison operand into into a XMM register. Jump to not_numbers jump
2927// target passing the left and right result if the operand is not a number.
2928static void LoadComparisonOperandSSE2(MacroAssembler* masm_,
2929 Result* operand,
2930 XMMRegister reg,
2931 Result* left_side,
2932 Result* right_side,
2933 JumpTarget* not_numbers) {
2934 Label done;
2935 if (operand->type_info().IsDouble()) {
2936 // Operand is known to be a heap number, just load it.
2937 __ movdbl(reg, FieldOperand(operand->reg(), HeapNumber::kValueOffset));
2938 } else if (operand->type_info().IsSmi()) {
2939 // Operand is known to be a smi. Convert it to double and keep the original
2940 // smi.
2941 __ SmiUntag(operand->reg());
2942 __ cvtsi2sd(reg, Operand(operand->reg()));
2943 __ SmiTag(operand->reg());
2944 } else {
2945 // Operand type not known, check for smi or heap number.
2946 Label smi;
2947 __ test(operand->reg(), Immediate(kSmiTagMask));
2948 __ j(zero, &smi);
2949 if (!operand->type_info().IsNumber()) {
2950 __ cmp(FieldOperand(operand->reg(), HeapObject::kMapOffset),
2951 Immediate(Factory::heap_number_map()));
2952 not_numbers->Branch(not_equal, left_side, right_side, taken);
2953 }
2954 __ movdbl(reg, FieldOperand(operand->reg(), HeapNumber::kValueOffset));
2955 __ jmp(&done);
2956
2957 __ bind(&smi);
2958 // Comvert smi to float and keep the original smi.
2959 __ SmiUntag(operand->reg());
2960 __ cvtsi2sd(reg, Operand(operand->reg()));
2961 __ SmiTag(operand->reg());
2962 __ jmp(&done);
2963 }
2964 __ bind(&done);
2965}
2966
2967
2968void CodeGenerator::GenerateInlineNumberComparison(Result* left_side,
2969 Result* right_side,
2970 Condition cc,
2971 ControlDestination* dest) {
2972 ASSERT(left_side->is_register());
2973 ASSERT(right_side->is_register());
2974
2975 JumpTarget not_numbers;
2976 if (CpuFeatures::IsSupported(SSE2)) {
2977 CpuFeatures::Scope use_sse2(SSE2);
2978
2979 // Load left and right operand into registers xmm0 and xmm1 and compare.
2980 LoadComparisonOperandSSE2(masm_, left_side, xmm0, left_side, right_side,
2981 &not_numbers);
2982 LoadComparisonOperandSSE2(masm_, right_side, xmm1, left_side, right_side,
2983 &not_numbers);
2984 __ comisd(xmm0, xmm1);
2985 } else {
2986 Label check_right, compare;
2987
2988 // Make sure that both comparison operands are numbers.
2989 CheckComparisonOperand(masm_, left_side, left_side, right_side,
2990 &not_numbers);
2991 CheckComparisonOperand(masm_, right_side, left_side, right_side,
2992 &not_numbers);
2993
2994 // Load right and left operand to FPU stack and compare.
2995 LoadComparisonOperand(masm_, right_side);
2996 LoadComparisonOperand(masm_, left_side);
2997 __ FCmp();
2998 }
2999
3000 // Bail out if a NaN is involved.
3001 not_numbers.Branch(parity_even, left_side, right_side, not_taken);
3002
3003 // Split to destination targets based on comparison.
3004 left_side->Unuse();
3005 right_side->Unuse();
3006 dest->true_target()->Branch(DoubleCondition(cc));
3007 dest->false_target()->Jump();
3008
3009 not_numbers.Bind(left_side, right_side);
3010}
3011
3012
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003013// Call the function just below TOS on the stack with the given
3014// arguments. The receiver is the TOS.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003015void CodeGenerator::CallWithArguments(ZoneList<Expression*>* args,
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003016 CallFunctionFlags flags,
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00003017 int position) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003018 // Push the arguments ("left-to-right") on the stack.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003019 int arg_count = args->length();
3020 for (int i = 0; i < arg_count; i++) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00003021 Load(args->at(i));
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00003022 frame_->SpillTop();
ager@chromium.org7c537e22008-10-16 08:43:32 +00003023 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003024
3025 // Record the position for debugging purposes.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00003026 CodeForSourcePosition(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003027
3028 // Use the shared code stub to call the function.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00003029 InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003030 CallFunctionStub call_function(arg_count, in_loop, flags);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003031 Result answer = frame_->CallStub(&call_function, arg_count + 1);
3032 // Restore context and replace function on the stack with the
3033 // result of the stub invocation.
3034 frame_->RestoreContextRegister();
3035 frame_->SetElementAt(0, &answer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003036}
3037
3038
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003039void CodeGenerator::CallApplyLazy(Expression* applicand,
ager@chromium.org3e875802009-06-29 08:26:34 +00003040 Expression* receiver,
3041 VariableProxy* arguments,
3042 int position) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003043 // An optimized implementation of expressions of the form
3044 // x.apply(y, arguments).
3045 // If the arguments object of the scope has not been allocated,
3046 // and x.apply is Function.prototype.apply, this optimization
3047 // just copies y and the arguments of the current function on the
3048 // stack, as receiver and arguments, and calls x.
3049 // In the implementation comments, we call x the applicand
3050 // and y the receiver.
ager@chromium.org3e875802009-06-29 08:26:34 +00003051 ASSERT(ArgumentsMode() == LAZY_ARGUMENTS_ALLOCATION);
3052 ASSERT(arguments->IsArguments());
3053
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003054 // Load applicand.apply onto the stack. This will usually
ager@chromium.org3e875802009-06-29 08:26:34 +00003055 // give us a megamorphic load site. Not super, but it works.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003056 Load(applicand);
ager@chromium.org5c838252010-02-19 08:53:10 +00003057 frame()->Dup();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003058 Handle<String> name = Factory::LookupAsciiSymbol("apply");
3059 frame()->Push(name);
3060 Result answer = frame()->CallLoadIC(RelocInfo::CODE_TARGET);
3061 __ nop();
3062 frame()->Push(&answer);
ager@chromium.org3e875802009-06-29 08:26:34 +00003063
3064 // Load the receiver and the existing arguments object onto the
3065 // expression stack. Avoid allocating the arguments object here.
3066 Load(receiver);
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00003067 LoadFromSlot(scope()->arguments()->var()->slot(), NOT_INSIDE_TYPEOF);
ager@chromium.org3e875802009-06-29 08:26:34 +00003068
3069 // Emit the source position information after having loaded the
3070 // receiver and the arguments.
3071 CodeForSourcePosition(position);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003072 // Contents of frame at this point:
3073 // Frame[0]: arguments object of the current function or the hole.
3074 // Frame[1]: receiver
3075 // Frame[2]: applicand.apply
3076 // Frame[3]: applicand.
ager@chromium.org3e875802009-06-29 08:26:34 +00003077
3078 // Check if the arguments object has been lazily allocated
3079 // already. If so, just use that instead of copying the arguments
3080 // from the stack. This also deals with cases where a local variable
3081 // named 'arguments' has been introduced.
3082 frame_->Dup();
3083 Result probe = frame_->Pop();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003084 { VirtualFrame::SpilledScope spilled_scope;
3085 Label slow, done;
3086 bool try_lazy = true;
3087 if (probe.is_constant()) {
3088 try_lazy = probe.handle()->IsTheHole();
3089 } else {
3090 __ cmp(Operand(probe.reg()), Immediate(Factory::the_hole_value()));
3091 probe.Unuse();
3092 __ j(not_equal, &slow);
3093 }
ager@chromium.org3e875802009-06-29 08:26:34 +00003094
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003095 if (try_lazy) {
3096 Label build_args;
3097 // Get rid of the arguments object probe.
3098 frame_->Drop(); // Can be called on a spilled frame.
3099 // Stack now has 3 elements on it.
3100 // Contents of stack at this point:
3101 // esp[0]: receiver
3102 // esp[1]: applicand.apply
3103 // esp[2]: applicand.
ager@chromium.org3e875802009-06-29 08:26:34 +00003104
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003105 // Check that the receiver really is a JavaScript object.
3106 __ mov(eax, Operand(esp, 0));
3107 __ test(eax, Immediate(kSmiTagMask));
3108 __ j(zero, &build_args);
ager@chromium.org3e875802009-06-29 08:26:34 +00003109 // We allow all JSObjects including JSFunctions. As long as
3110 // JS_FUNCTION_TYPE is the last instance type and it is right
3111 // after LAST_JS_OBJECT_TYPE, we do not have to check the upper
3112 // bound.
3113 ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
3114 ASSERT(JS_FUNCTION_TYPE == LAST_JS_OBJECT_TYPE + 1);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003115 __ CmpObjectType(eax, FIRST_JS_OBJECT_TYPE, ecx);
3116 __ j(below, &build_args);
ager@chromium.org3e875802009-06-29 08:26:34 +00003117
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003118 // Check that applicand.apply is Function.prototype.apply.
3119 __ mov(eax, Operand(esp, kPointerSize));
3120 __ test(eax, Immediate(kSmiTagMask));
3121 __ j(zero, &build_args);
3122 __ CmpObjectType(eax, JS_FUNCTION_TYPE, ecx);
3123 __ j(not_equal, &build_args);
3124 __ mov(ecx, FieldOperand(eax, JSFunction::kSharedFunctionInfoOffset));
ager@chromium.org3e875802009-06-29 08:26:34 +00003125 Handle<Code> apply_code(Builtins::builtin(Builtins::FunctionApply));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003126 __ cmp(FieldOperand(ecx, SharedFunctionInfo::kCodeOffset),
ager@chromium.org3e875802009-06-29 08:26:34 +00003127 Immediate(apply_code));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003128 __ j(not_equal, &build_args);
3129
3130 // Check that applicand is a function.
3131 __ mov(edi, Operand(esp, 2 * kPointerSize));
3132 __ test(edi, Immediate(kSmiTagMask));
3133 __ j(zero, &build_args);
3134 __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx);
3135 __ j(not_equal, &build_args);
3136
3137 // Copy the arguments to this function possibly from the
3138 // adaptor frame below it.
3139 Label invoke, adapted;
3140 __ mov(edx, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
3141 __ mov(ecx, Operand(edx, StandardFrameConstants::kContextOffset));
3142 __ cmp(Operand(ecx),
3143 Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
3144 __ j(equal, &adapted);
3145
3146 // No arguments adaptor frame. Copy fixed number of arguments.
ager@chromium.org5c838252010-02-19 08:53:10 +00003147 __ mov(eax, Immediate(scope()->num_parameters()));
3148 for (int i = 0; i < scope()->num_parameters(); i++) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003149 __ push(frame_->ParameterAt(i));
3150 }
3151 __ jmp(&invoke);
3152
3153 // Arguments adaptor frame present. Copy arguments from there, but
3154 // avoid copying too many arguments to avoid stack overflows.
3155 __ bind(&adapted);
3156 static const uint32_t kArgumentsLimit = 1 * KB;
3157 __ mov(eax, Operand(edx, ArgumentsAdaptorFrameConstants::kLengthOffset));
3158 __ SmiUntag(eax);
3159 __ mov(ecx, Operand(eax));
3160 __ cmp(eax, kArgumentsLimit);
3161 __ j(above, &build_args);
3162
3163 // Loop through the arguments pushing them onto the execution
3164 // stack. We don't inform the virtual frame of the push, so we don't
3165 // have to worry about getting rid of the elements from the virtual
3166 // frame.
3167 Label loop;
3168 // ecx is a small non-negative integer, due to the test above.
3169 __ test(ecx, Operand(ecx));
3170 __ j(zero, &invoke);
3171 __ bind(&loop);
3172 __ push(Operand(edx, ecx, times_pointer_size, 1 * kPointerSize));
3173 __ dec(ecx);
3174 __ j(not_zero, &loop);
3175
3176 // Invoke the function.
3177 __ bind(&invoke);
3178 ParameterCount actual(eax);
3179 __ InvokeFunction(edi, actual, CALL_FUNCTION);
3180 // Drop applicand.apply and applicand from the stack, and push
3181 // the result of the function call, but leave the spilled frame
3182 // unchanged, with 3 elements, so it is correct when we compile the
3183 // slow-case code.
3184 __ add(Operand(esp), Immediate(2 * kPointerSize));
3185 __ push(eax);
3186 // Stack now has 1 element:
3187 // esp[0]: result
3188 __ jmp(&done);
3189
3190 // Slow-case: Allocate the arguments object since we know it isn't
3191 // there, and fall-through to the slow-case where we call
3192 // applicand.apply.
3193 __ bind(&build_args);
3194 // Stack now has 3 elements, because we have jumped from where:
3195 // esp[0]: receiver
3196 // esp[1]: applicand.apply
3197 // esp[2]: applicand.
3198
3199 // StoreArgumentsObject requires a correct frame, and may modify it.
3200 Result arguments_object = StoreArgumentsObject(false);
3201 frame_->SpillAll();
3202 arguments_object.ToRegister();
3203 frame_->EmitPush(arguments_object.reg());
3204 arguments_object.Unuse();
3205 // Stack and frame now have 4 elements.
3206 __ bind(&slow);
ager@chromium.org3e875802009-06-29 08:26:34 +00003207 }
3208
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003209 // Generic computation of x.apply(y, args) with no special optimization.
3210 // Flip applicand.apply and applicand on the stack, so
3211 // applicand looks like the receiver of the applicand.apply call.
3212 // Then process it as a normal function call.
3213 __ mov(eax, Operand(esp, 3 * kPointerSize));
3214 __ mov(ebx, Operand(esp, 2 * kPointerSize));
3215 __ mov(Operand(esp, 2 * kPointerSize), eax);
3216 __ mov(Operand(esp, 3 * kPointerSize), ebx);
ager@chromium.org3e875802009-06-29 08:26:34 +00003217
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003218 CallFunctionStub call_function(2, NOT_IN_LOOP, NO_CALL_FUNCTION_FLAGS);
3219 Result res = frame_->CallStub(&call_function, 3);
3220 // The function and its two arguments have been dropped.
3221 frame_->Drop(1); // Drop the receiver as well.
3222 res.ToRegister();
3223 frame_->EmitPush(res.reg());
3224 // Stack now has 1 element:
3225 // esp[0]: result
3226 if (try_lazy) __ bind(&done);
3227 } // End of spilled scope.
3228 // Restore the context register after a call.
ager@chromium.org3e875802009-06-29 08:26:34 +00003229 frame_->RestoreContextRegister();
3230}
3231
3232
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003233class DeferredStackCheck: public DeferredCode {
3234 public:
ager@chromium.orge2902be2009-06-08 12:21:35 +00003235 DeferredStackCheck() {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003236 set_comment("[ DeferredStackCheck");
3237 }
3238
3239 virtual void Generate();
3240};
3241
3242
3243void DeferredStackCheck::Generate() {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003244 StackCheckStub stub;
ager@chromium.orge2902be2009-06-08 12:21:35 +00003245 __ CallStub(&stub);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003246}
3247
3248
ager@chromium.org7c537e22008-10-16 08:43:32 +00003249void CodeGenerator::CheckStack() {
ager@chromium.org3811b432009-10-28 14:53:37 +00003250 DeferredStackCheck* deferred = new DeferredStackCheck;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003251 ExternalReference stack_limit =
3252 ExternalReference::address_of_stack_limit();
3253 __ cmp(esp, Operand::StaticVariable(stack_limit));
ager@chromium.org3811b432009-10-28 14:53:37 +00003254 deferred->Branch(below);
3255 deferred->BindExit();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003256}
3257
3258
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003259void CodeGenerator::VisitAndSpill(Statement* statement) {
3260 ASSERT(in_spilled_code());
3261 set_in_spilled_code(false);
3262 Visit(statement);
3263 if (frame_ != NULL) {
3264 frame_->SpillAll();
3265 }
3266 set_in_spilled_code(true);
3267}
3268
3269
3270void CodeGenerator::VisitStatementsAndSpill(ZoneList<Statement*>* statements) {
3271 ASSERT(in_spilled_code());
3272 set_in_spilled_code(false);
3273 VisitStatements(statements);
3274 if (frame_ != NULL) {
3275 frame_->SpillAll();
3276 }
3277 set_in_spilled_code(true);
3278}
3279
3280
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003281void CodeGenerator::VisitStatements(ZoneList<Statement*>* statements) {
3282 ASSERT(!in_spilled_code());
3283 for (int i = 0; has_valid_frame() && i < statements->length(); i++) {
3284 Visit(statements->at(i));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003285 }
3286}
3287
3288
ager@chromium.org7c537e22008-10-16 08:43:32 +00003289void CodeGenerator::VisitBlock(Block* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003290 ASSERT(!in_spilled_code());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003291 Comment cmnt(masm_, "[ Block");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003292 CodeForStatementPosition(node);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00003293 node->break_target()->set_direction(JumpTarget::FORWARD_ONLY);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003294 VisitStatements(node->statements());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003295 if (node->break_target()->is_linked()) {
3296 node->break_target()->Bind();
3297 }
3298 node->break_target()->Unuse();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003299}
3300
3301
ager@chromium.org7c537e22008-10-16 08:43:32 +00003302void CodeGenerator::DeclareGlobals(Handle<FixedArray> pairs) {
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00003303 // Call the runtime to declare the globals. The inevitable call
3304 // will sync frame elements to memory anyway, so we do it eagerly to
3305 // allow us to push the arguments directly into place.
3306 frame_->SyncRange(0, frame_->element_count() - 1);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003307
ager@chromium.org3811b432009-10-28 14:53:37 +00003308 frame_->EmitPush(esi); // The context is the first argument.
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00003309 frame_->EmitPush(Immediate(pairs));
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00003310 frame_->EmitPush(Immediate(Smi::FromInt(is_eval() ? 1 : 0)));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003311 Result ignored = frame_->CallRuntime(Runtime::kDeclareGlobals, 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003312 // Return value is ignored.
3313}
3314
3315
ager@chromium.org7c537e22008-10-16 08:43:32 +00003316void CodeGenerator::VisitDeclaration(Declaration* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003317 Comment cmnt(masm_, "[ Declaration");
3318 Variable* var = node->proxy()->var();
3319 ASSERT(var != NULL); // must have been resolved
3320 Slot* slot = var->slot();
3321
3322 // If it was not possible to allocate the variable at compile time,
3323 // we need to "declare" it at runtime to make sure it actually
3324 // exists in the local context.
3325 if (slot != NULL && slot->type() == Slot::LOOKUP) {
3326 // Variables with a "LOOKUP" slot were introduced as non-locals
3327 // during variable resolution and must have mode DYNAMIC.
ager@chromium.org381abbb2009-02-25 13:23:22 +00003328 ASSERT(var->is_dynamic());
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00003329 // For now, just do a runtime call. Sync the virtual frame eagerly
3330 // so we can simply push the arguments into place.
3331 frame_->SyncRange(0, frame_->element_count() - 1);
3332 frame_->EmitPush(esi);
3333 frame_->EmitPush(Immediate(var->name()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003334 // Declaration nodes are always introduced in one of two modes.
3335 ASSERT(node->mode() == Variable::VAR || node->mode() == Variable::CONST);
3336 PropertyAttributes attr = node->mode() == Variable::VAR ? NONE : READ_ONLY;
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00003337 frame_->EmitPush(Immediate(Smi::FromInt(attr)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003338 // Push initial value, if any.
3339 // Note: For variables we must not push an initial value (such as
3340 // 'undefined') because we may have a (legal) redeclaration and we
3341 // must not destroy the current value.
3342 if (node->mode() == Variable::CONST) {
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00003343 frame_->EmitPush(Immediate(Factory::the_hole_value()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003344 } else if (node->fun() != NULL) {
3345 Load(node->fun());
3346 } else {
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00003347 frame_->EmitPush(Immediate(Smi::FromInt(0))); // no initial value!
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003348 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003349 Result ignored = frame_->CallRuntime(Runtime::kDeclareContextSlot, 4);
ager@chromium.org7c537e22008-10-16 08:43:32 +00003350 // Ignore the return value (declarations are statements).
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003351 return;
3352 }
3353
3354 ASSERT(!var->is_global());
3355
3356 // If we have a function or a constant, we need to initialize the variable.
3357 Expression* val = NULL;
3358 if (node->mode() == Variable::CONST) {
3359 val = new Literal(Factory::the_hole_value());
3360 } else {
3361 val = node->fun(); // NULL if we don't have a function
3362 }
3363
3364 if (val != NULL) {
iposva@chromium.org245aa852009-02-10 00:49:54 +00003365 {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003366 // Set the initial value.
iposva@chromium.org245aa852009-02-10 00:49:54 +00003367 Reference target(this, node->proxy());
3368 Load(val);
3369 target.SetValue(NOT_CONST_INIT);
3370 // The reference is removed from the stack (preserving TOS) when
3371 // it goes out of scope.
3372 }
3373 // Get rid of the assigned value (declarations are statements).
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003374 frame_->Drop();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003375 }
3376}
3377
3378
ager@chromium.org7c537e22008-10-16 08:43:32 +00003379void CodeGenerator::VisitExpressionStatement(ExpressionStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003380 ASSERT(!in_spilled_code());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003381 Comment cmnt(masm_, "[ ExpressionStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003382 CodeForStatementPosition(node);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003383 Expression* expression = node->expression();
3384 expression->MarkAsStatement();
3385 Load(expression);
ager@chromium.org7c537e22008-10-16 08:43:32 +00003386 // Remove the lingering expression result from the top of stack.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003387 frame_->Drop();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003388}
3389
3390
ager@chromium.org7c537e22008-10-16 08:43:32 +00003391void CodeGenerator::VisitEmptyStatement(EmptyStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003392 ASSERT(!in_spilled_code());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003393 Comment cmnt(masm_, "// EmptyStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003394 CodeForStatementPosition(node);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003395 // nothing to do
3396}
3397
3398
ager@chromium.org7c537e22008-10-16 08:43:32 +00003399void CodeGenerator::VisitIfStatement(IfStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003400 ASSERT(!in_spilled_code());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003401 Comment cmnt(masm_, "[ IfStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003402 // Generate different code depending on which parts of the if statement
3403 // are present or not.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003404 bool has_then_stm = node->HasThenStatement();
3405 bool has_else_stm = node->HasElseStatement();
3406
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003407 CodeForStatementPosition(node);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00003408 JumpTarget exit;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003409 if (has_then_stm && has_else_stm) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00003410 JumpTarget then;
3411 JumpTarget else_;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003412 ControlDestination dest(&then, &else_, true);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003413 LoadCondition(node->condition(), &dest, true);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003414
3415 if (dest.false_was_fall_through()) {
3416 // The else target was bound, so we compile the else part first.
3417 Visit(node->else_statement());
3418
3419 // We may have dangling jumps to the then part.
3420 if (then.is_linked()) {
3421 if (has_valid_frame()) exit.Jump();
3422 then.Bind();
3423 Visit(node->then_statement());
3424 }
3425 } else {
3426 // The then target was bound, so we compile the then part first.
3427 Visit(node->then_statement());
3428
3429 if (else_.is_linked()) {
3430 if (has_valid_frame()) exit.Jump();
3431 else_.Bind();
3432 Visit(node->else_statement());
3433 }
3434 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003435
3436 } else if (has_then_stm) {
3437 ASSERT(!has_else_stm);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00003438 JumpTarget then;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003439 ControlDestination dest(&then, &exit, true);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003440 LoadCondition(node->condition(), &dest, true);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003441
3442 if (dest.false_was_fall_through()) {
3443 // The exit label was bound. We may have dangling jumps to the
3444 // then part.
3445 if (then.is_linked()) {
3446 exit.Unuse();
3447 exit.Jump();
3448 then.Bind();
3449 Visit(node->then_statement());
3450 }
3451 } else {
3452 // The then label was bound.
3453 Visit(node->then_statement());
3454 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003455
3456 } else if (has_else_stm) {
3457 ASSERT(!has_then_stm);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00003458 JumpTarget else_;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003459 ControlDestination dest(&exit, &else_, false);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003460 LoadCondition(node->condition(), &dest, true);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003461
3462 if (dest.true_was_fall_through()) {
3463 // The exit label was bound. We may have dangling jumps to the
3464 // else part.
3465 if (else_.is_linked()) {
3466 exit.Unuse();
3467 exit.Jump();
3468 else_.Bind();
3469 Visit(node->else_statement());
3470 }
3471 } else {
3472 // The else label was bound.
3473 Visit(node->else_statement());
3474 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003475
3476 } else {
3477 ASSERT(!has_then_stm && !has_else_stm);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003478 // We only care about the condition's side effects (not its value
3479 // or control flow effect). LoadCondition is called without
3480 // forcing control flow.
3481 ControlDestination dest(&exit, &exit, true);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003482 LoadCondition(node->condition(), &dest, false);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003483 if (!dest.is_used()) {
3484 // We got a value on the frame rather than (or in addition to)
3485 // control flow.
3486 frame_->Drop();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003487 }
3488 }
3489
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003490 if (exit.is_linked()) {
3491 exit.Bind();
3492 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003493}
3494
3495
ager@chromium.org7c537e22008-10-16 08:43:32 +00003496void CodeGenerator::VisitContinueStatement(ContinueStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003497 ASSERT(!in_spilled_code());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003498 Comment cmnt(masm_, "[ ContinueStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003499 CodeForStatementPosition(node);
3500 node->target()->continue_target()->Jump();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003501}
3502
3503
ager@chromium.org7c537e22008-10-16 08:43:32 +00003504void CodeGenerator::VisitBreakStatement(BreakStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003505 ASSERT(!in_spilled_code());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003506 Comment cmnt(masm_, "[ BreakStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003507 CodeForStatementPosition(node);
3508 node->target()->break_target()->Jump();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003509}
3510
3511
ager@chromium.org7c537e22008-10-16 08:43:32 +00003512void CodeGenerator::VisitReturnStatement(ReturnStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003513 ASSERT(!in_spilled_code());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003514 Comment cmnt(masm_, "[ ReturnStatement");
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003515
kasperl@chromium.org8ccb0be2009-04-07 07:21:39 +00003516 CodeForStatementPosition(node);
3517 Load(node->expression());
3518 Result return_value = frame_->Pop();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003519 masm()->WriteRecordedPositions();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003520 if (function_return_is_shadowed_) {
kasperl@chromium.org8ccb0be2009-04-07 07:21:39 +00003521 function_return_.Jump(&return_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003522 } else {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003523 frame_->PrepareForReturn();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003524 if (function_return_.is_bound()) {
kasperl@chromium.org8ccb0be2009-04-07 07:21:39 +00003525 // If the function return label is already bound we reuse the
3526 // code by jumping to the return site.
3527 function_return_.Jump(&return_value);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003528 } else {
ager@chromium.orgeadaf222009-06-16 09:43:10 +00003529 function_return_.Bind(&return_value);
kasperl@chromium.org8ccb0be2009-04-07 07:21:39 +00003530 GenerateReturnSequence(&return_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003531 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003532 }
3533}
3534
3535
kasperl@chromium.org8ccb0be2009-04-07 07:21:39 +00003536void CodeGenerator::GenerateReturnSequence(Result* return_value) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003537 // The return value is a live (but not currently reference counted)
3538 // reference to eax. This is safe because the current frame does not
3539 // contain a reference to eax (it is prepared for the return by spilling
3540 // all registers).
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003541 if (FLAG_trace) {
kasperl@chromium.org8ccb0be2009-04-07 07:21:39 +00003542 frame_->Push(return_value);
3543 *return_value = frame_->CallRuntime(Runtime::kTraceExit, 1);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003544 }
kasperl@chromium.org8ccb0be2009-04-07 07:21:39 +00003545 return_value->ToRegister(eax);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003546
3547 // Add a label for checking the size of the code used for returning.
3548 Label check_exit_codesize;
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003549 masm_->bind(&check_exit_codesize);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003550
3551 // Leave the frame and return popping the arguments and the
3552 // receiver.
3553 frame_->Exit();
ager@chromium.org5c838252010-02-19 08:53:10 +00003554 masm_->ret((scope()->num_parameters() + 1) * kPointerSize);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003555 DeleteFrame();
3556
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003557#ifdef ENABLE_DEBUGGER_SUPPORT
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003558 // Check that the size of the code used for returning matches what is
3559 // expected by the debugger.
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00003560 ASSERT_EQ(Assembler::kJSReturnSequenceLength,
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003561 masm_->SizeOfCodeGeneratedSince(&check_exit_codesize));
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003562#endif
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003563}
3564
3565
ager@chromium.org7c537e22008-10-16 08:43:32 +00003566void CodeGenerator::VisitWithEnterStatement(WithEnterStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003567 ASSERT(!in_spilled_code());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003568 Comment cmnt(masm_, "[ WithEnterStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003569 CodeForStatementPosition(node);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003570 Load(node->expression());
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00003571 Result context;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00003572 if (node->is_catch_block()) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003573 context = frame_->CallRuntime(Runtime::kPushCatchContext, 1);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00003574 } else {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003575 context = frame_->CallRuntime(Runtime::kPushContext, 1);
kasper.lund7276f142008-07-30 08:49:36 +00003576 }
3577
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003578 // Update context local.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003579 frame_->SaveContextRegister();
3580
3581 // Verify that the runtime call result and esi agree.
3582 if (FLAG_debug_code) {
3583 __ cmp(context.reg(), Operand(esi));
3584 __ Assert(equal, "Runtime::NewContext should end up in esi");
3585 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003586}
3587
3588
ager@chromium.org7c537e22008-10-16 08:43:32 +00003589void CodeGenerator::VisitWithExitStatement(WithExitStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003590 ASSERT(!in_spilled_code());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003591 Comment cmnt(masm_, "[ WithExitStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003592 CodeForStatementPosition(node);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003593 // Pop context.
3594 __ mov(esi, ContextOperand(esi, Context::PREVIOUS_INDEX));
3595 // Update context local.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003596 frame_->SaveContextRegister();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003597}
3598
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003599
ager@chromium.org7c537e22008-10-16 08:43:32 +00003600void CodeGenerator::VisitSwitchStatement(SwitchStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003601 ASSERT(!in_spilled_code());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003602 Comment cmnt(masm_, "[ SwitchStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003603 CodeForStatementPosition(node);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00003604 node->break_target()->set_direction(JumpTarget::FORWARD_ONLY);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003605
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003606 // Compile the switch value.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003607 Load(node->tag());
3608
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003609 ZoneList<CaseClause*>* cases = node->cases();
3610 int length = cases->length();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003611 CaseClause* default_clause = NULL;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003612
kasperl@chromium.org71affb52009-05-26 05:44:31 +00003613 JumpTarget next_test;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003614 // Compile the case label expressions and comparisons. Exit early
3615 // if a comparison is unconditionally true. The target next_test is
3616 // bound before the loop in order to indicate control flow to the
3617 // first comparison.
3618 next_test.Bind();
3619 for (int i = 0; i < length && !next_test.is_unused(); i++) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003620 CaseClause* clause = cases->at(i);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003621 // The default is not a test, but remember it for later.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003622 if (clause->is_default()) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003623 default_clause = clause;
3624 continue;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003625 }
3626
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003627 Comment cmnt(masm_, "[ Case comparison");
3628 // We recycle the same target next_test for each test. Bind it if
3629 // the previous test has not done so and then unuse it for the
3630 // loop.
3631 if (next_test.is_linked()) {
3632 next_test.Bind();
3633 }
3634 next_test.Unuse();
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00003635
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003636 // Duplicate the switch value.
3637 frame_->Dup();
3638
3639 // Compile the label expression.
3640 Load(clause->label());
3641
3642 // Compare and branch to the body if true or the next test if
3643 // false. Prefer the next test as a fall through.
3644 ControlDestination dest(clause->body_target(), &next_test, false);
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00003645 Comparison(node, equal, true, &dest);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003646
3647 // If the comparison fell through to the true target, jump to the
3648 // actual body.
3649 if (dest.true_was_fall_through()) {
3650 clause->body_target()->Unuse();
3651 clause->body_target()->Jump();
3652 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003653 }
3654
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003655 // If there was control flow to a next test from the last one
3656 // compiled, compile a jump to the default or break target.
3657 if (!next_test.is_unused()) {
3658 if (next_test.is_linked()) {
3659 next_test.Bind();
3660 }
3661 // Drop the switch value.
3662 frame_->Drop();
3663 if (default_clause != NULL) {
3664 default_clause->body_target()->Jump();
3665 } else {
3666 node->break_target()->Jump();
3667 }
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00003668 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003669
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003670
3671 // The last instruction emitted was a jump, either to the default
3672 // clause or the break target, or else to a case body from the loop
3673 // that compiles the tests.
3674 ASSERT(!has_valid_frame());
3675 // Compile case bodies as needed.
3676 for (int i = 0; i < length; i++) {
3677 CaseClause* clause = cases->at(i);
3678
3679 // There are two ways to reach the body: from the corresponding
3680 // test or as the fall through of the previous body.
3681 if (clause->body_target()->is_linked() || has_valid_frame()) {
3682 if (clause->body_target()->is_linked()) {
3683 if (has_valid_frame()) {
3684 // If we have both a jump to the test and a fall through, put
3685 // a jump on the fall through path to avoid the dropping of
3686 // the switch value on the test path. The exception is the
3687 // default which has already had the switch value dropped.
3688 if (clause->is_default()) {
3689 clause->body_target()->Bind();
3690 } else {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00003691 JumpTarget body;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003692 body.Jump();
3693 clause->body_target()->Bind();
3694 frame_->Drop();
3695 body.Bind();
3696 }
3697 } else {
3698 // No fall through to worry about.
3699 clause->body_target()->Bind();
3700 if (!clause->is_default()) {
3701 frame_->Drop();
3702 }
3703 }
3704 } else {
3705 // Otherwise, we have only fall through.
3706 ASSERT(has_valid_frame());
3707 }
3708
3709 // We are now prepared to compile the body.
3710 Comment cmnt(masm_, "[ Case body");
3711 VisitStatements(clause->statements());
3712 }
3713 clause->body_target()->Unuse();
3714 }
3715
3716 // We may not have a valid frame here so bind the break target only
3717 // if needed.
3718 if (node->break_target()->is_linked()) {
3719 node->break_target()->Bind();
3720 }
3721 node->break_target()->Unuse();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003722}
3723
3724
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00003725void CodeGenerator::VisitDoWhileStatement(DoWhileStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003726 ASSERT(!in_spilled_code());
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00003727 Comment cmnt(masm_, "[ DoWhileStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003728 CodeForStatementPosition(node);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00003729 node->break_target()->set_direction(JumpTarget::FORWARD_ONLY);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00003730 JumpTarget body(JumpTarget::BIDIRECTIONAL);
3731 IncrementLoopNesting();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003732
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00003733 ConditionAnalysis info = AnalyzeCondition(node->cond());
3734 // Label the top of the loop for the backward jump if necessary.
3735 switch (info) {
3736 case ALWAYS_TRUE:
3737 // Use the continue target.
3738 node->continue_target()->set_direction(JumpTarget::BIDIRECTIONAL);
3739 node->continue_target()->Bind();
3740 break;
3741 case ALWAYS_FALSE:
3742 // No need to label it.
3743 node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY);
3744 break;
3745 case DONT_KNOW:
3746 // Continue is the test, so use the backward body target.
3747 node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY);
3748 body.Bind();
3749 break;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003750 }
3751
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00003752 CheckStack(); // TODO(1222600): ignore if body contains calls.
3753 Visit(node->body());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003754
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00003755 // Compile the test.
3756 switch (info) {
3757 case ALWAYS_TRUE:
3758 // If control flow can fall off the end of the body, jump back to
3759 // the top and bind the break target at the exit.
3760 if (has_valid_frame()) {
3761 node->continue_target()->Jump();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003762 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003763 if (node->break_target()->is_linked()) {
3764 node->break_target()->Bind();
3765 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003766 break;
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00003767 case ALWAYS_FALSE:
3768 // We may have had continues or breaks in the body.
3769 if (node->continue_target()->is_linked()) {
3770 node->continue_target()->Bind();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003771 }
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00003772 if (node->break_target()->is_linked()) {
3773 node->break_target()->Bind();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003774 }
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00003775 break;
3776 case DONT_KNOW:
3777 // We have to compile the test expression if it can be reached by
3778 // control flow falling out of the body or via continue.
3779 if (node->continue_target()->is_linked()) {
3780 node->continue_target()->Bind();
3781 }
3782 if (has_valid_frame()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003783 Comment cmnt(masm_, "[ DoWhileCondition");
3784 CodeForDoWhileConditionPosition(node);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00003785 ControlDestination dest(&body, node->break_target(), false);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003786 LoadCondition(node->cond(), &dest, true);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003787 }
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00003788 if (node->break_target()->is_linked()) {
3789 node->break_target()->Bind();
3790 }
3791 break;
3792 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003793
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00003794 DecrementLoopNesting();
3795}
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003796
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00003797
3798void CodeGenerator::VisitWhileStatement(WhileStatement* node) {
3799 ASSERT(!in_spilled_code());
3800 Comment cmnt(masm_, "[ WhileStatement");
3801 CodeForStatementPosition(node);
3802
3803 // If the condition is always false and has no side effects, we do not
3804 // need to compile anything.
3805 ConditionAnalysis info = AnalyzeCondition(node->cond());
3806 if (info == ALWAYS_FALSE) return;
3807
3808 // Do not duplicate conditions that may have function literal
3809 // subexpressions. This can cause us to compile the function literal
3810 // twice.
3811 bool test_at_bottom = !node->may_have_function_literal();
3812 node->break_target()->set_direction(JumpTarget::FORWARD_ONLY);
3813 IncrementLoopNesting();
3814 JumpTarget body;
3815 if (test_at_bottom) {
3816 body.set_direction(JumpTarget::BIDIRECTIONAL);
3817 }
3818
3819 // Based on the condition analysis, compile the test as necessary.
3820 switch (info) {
3821 case ALWAYS_TRUE:
3822 // We will not compile the test expression. Label the top of the
3823 // loop with the continue target.
3824 node->continue_target()->set_direction(JumpTarget::BIDIRECTIONAL);
3825 node->continue_target()->Bind();
3826 break;
3827 case DONT_KNOW: {
3828 if (test_at_bottom) {
3829 // Continue is the test at the bottom, no need to label the test
3830 // at the top. The body is a backward target.
3831 node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY);
3832 } else {
3833 // Label the test at the top as the continue target. The body
3834 // is a forward-only target.
3835 node->continue_target()->set_direction(JumpTarget::BIDIRECTIONAL);
3836 node->continue_target()->Bind();
3837 }
3838 // Compile the test with the body as the true target and preferred
3839 // fall-through and with the break target as the false target.
3840 ControlDestination dest(&body, node->break_target(), true);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003841 LoadCondition(node->cond(), &dest, true);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00003842
3843 if (dest.false_was_fall_through()) {
3844 // If we got the break target as fall-through, the test may have
3845 // been unconditionally false (if there are no jumps to the
3846 // body).
3847 if (!body.is_linked()) {
3848 DecrementLoopNesting();
3849 return;
3850 }
3851
3852 // Otherwise, jump around the body on the fall through and then
3853 // bind the body target.
3854 node->break_target()->Unuse();
3855 node->break_target()->Jump();
3856 body.Bind();
3857 }
3858 break;
3859 }
3860 case ALWAYS_FALSE:
3861 UNREACHABLE();
3862 break;
3863 }
3864
3865 CheckStack(); // TODO(1222600): ignore if body contains calls.
3866 Visit(node->body());
3867
3868 // Based on the condition analysis, compile the backward jump as
3869 // necessary.
3870 switch (info) {
3871 case ALWAYS_TRUE:
3872 // The loop body has been labeled with the continue target.
3873 if (has_valid_frame()) {
3874 node->continue_target()->Jump();
3875 }
3876 break;
3877 case DONT_KNOW:
3878 if (test_at_bottom) {
3879 // If we have chosen to recompile the test at the bottom, then
3880 // it is the continue target.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003881 if (node->continue_target()->is_linked()) {
3882 node->continue_target()->Bind();
3883 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003884 if (has_valid_frame()) {
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00003885 // The break target is the fall-through (body is a backward
3886 // jump from here and thus an invalid fall-through).
3887 ControlDestination dest(&body, node->break_target(), false);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003888 LoadCondition(node->cond(), &dest, true);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00003889 }
3890 } else {
3891 // If we have chosen not to recompile the test at the bottom,
3892 // jump back to the one at the top.
3893 if (has_valid_frame()) {
3894 node->continue_target()->Jump();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003895 }
3896 }
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00003897 break;
3898 case ALWAYS_FALSE:
3899 UNREACHABLE();
3900 break;
3901 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003902
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00003903 // The break target may be already bound (by the condition), or there
3904 // may not be a valid frame. Bind it only if needed.
3905 if (node->break_target()->is_linked()) {
3906 node->break_target()->Bind();
3907 }
3908 DecrementLoopNesting();
3909}
3910
3911
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00003912void CodeGenerator::SetTypeForStackSlot(Slot* slot, TypeInfo info) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00003913 ASSERT(slot->type() == Slot::LOCAL || slot->type() == Slot::PARAMETER);
3914 if (slot->type() == Slot::LOCAL) {
3915 frame_->SetTypeForLocalAt(slot->index(), info);
3916 } else {
3917 frame_->SetTypeForParamAt(slot->index(), info);
3918 }
3919 if (FLAG_debug_code && info.IsSmi()) {
3920 if (slot->type() == Slot::LOCAL) {
3921 frame_->PushLocalAt(slot->index());
3922 } else {
3923 frame_->PushParameterAt(slot->index());
3924 }
3925 Result var = frame_->Pop();
3926 var.ToRegister();
3927 __ AbortIfNotSmi(var.reg());
3928 }
3929}
3930
3931
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00003932void CodeGenerator::VisitForStatement(ForStatement* node) {
3933 ASSERT(!in_spilled_code());
3934 Comment cmnt(masm_, "[ ForStatement");
3935 CodeForStatementPosition(node);
3936
3937 // Compile the init expression if present.
3938 if (node->init() != NULL) {
3939 Visit(node->init());
3940 }
3941
3942 // If the condition is always false and has no side effects, we do not
3943 // need to compile anything else.
3944 ConditionAnalysis info = AnalyzeCondition(node->cond());
3945 if (info == ALWAYS_FALSE) return;
3946
3947 // Do not duplicate conditions that may have function literal
3948 // subexpressions. This can cause us to compile the function literal
3949 // twice.
3950 bool test_at_bottom = !node->may_have_function_literal();
3951 node->break_target()->set_direction(JumpTarget::FORWARD_ONLY);
3952 IncrementLoopNesting();
3953
3954 // Target for backward edge if no test at the bottom, otherwise
3955 // unused.
3956 JumpTarget loop(JumpTarget::BIDIRECTIONAL);
3957
3958 // Target for backward edge if there is a test at the bottom,
3959 // otherwise used as target for test at the top.
3960 JumpTarget body;
3961 if (test_at_bottom) {
3962 body.set_direction(JumpTarget::BIDIRECTIONAL);
3963 }
3964
3965 // Based on the condition analysis, compile the test as necessary.
3966 switch (info) {
3967 case ALWAYS_TRUE:
3968 // We will not compile the test expression. Label the top of the
3969 // loop.
3970 if (node->next() == NULL) {
3971 // Use the continue target if there is no update expression.
3972 node->continue_target()->set_direction(JumpTarget::BIDIRECTIONAL);
3973 node->continue_target()->Bind();
3974 } else {
3975 // Otherwise use the backward loop target.
3976 node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY);
3977 loop.Bind();
3978 }
3979 break;
3980 case DONT_KNOW: {
3981 if (test_at_bottom) {
3982 // Continue is either the update expression or the test at the
3983 // bottom, no need to label the test at the top.
3984 node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY);
3985 } else if (node->next() == NULL) {
3986 // We are not recompiling the test at the bottom and there is no
3987 // update expression.
3988 node->continue_target()->set_direction(JumpTarget::BIDIRECTIONAL);
3989 node->continue_target()->Bind();
3990 } else {
3991 // We are not recompiling the test at the bottom and there is an
3992 // update expression.
3993 node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY);
3994 loop.Bind();
3995 }
3996 // Compile the test with the body as the true target and preferred
3997 // fall-through and with the break target as the false target.
3998 ControlDestination dest(&body, node->break_target(), true);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003999 LoadCondition(node->cond(), &dest, true);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004000
4001 if (dest.false_was_fall_through()) {
4002 // If we got the break target as fall-through, the test may have
4003 // been unconditionally false (if there are no jumps to the
4004 // body).
4005 if (!body.is_linked()) {
4006 DecrementLoopNesting();
4007 return;
4008 }
4009
4010 // Otherwise, jump around the body on the fall through and then
4011 // bind the body target.
4012 node->break_target()->Unuse();
4013 node->break_target()->Jump();
4014 body.Bind();
4015 }
4016 break;
4017 }
4018 case ALWAYS_FALSE:
4019 UNREACHABLE();
4020 break;
4021 }
4022
4023 CheckStack(); // TODO(1222600): ignore if body contains calls.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00004024
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00004025 // We know that the loop index is a smi if it is not modified in the
4026 // loop body and it is checked against a constant limit in the loop
4027 // condition. In this case, we reset the static type information of the
4028 // loop index to smi before compiling the body, the update expression, and
4029 // the bottom check of the loop condition.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00004030 if (node->is_fast_smi_loop()) {
4031 // Set number type of the loop variable to smi.
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00004032 SetTypeForStackSlot(node->loop_variable()->slot(), TypeInfo::Smi());
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00004033 }
4034
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004035 Visit(node->body());
4036
4037 // If there is an update expression, compile it if necessary.
4038 if (node->next() != NULL) {
4039 if (node->continue_target()->is_linked()) {
4040 node->continue_target()->Bind();
4041 }
4042
4043 // Control can reach the update by falling out of the body or by a
4044 // continue.
4045 if (has_valid_frame()) {
4046 // Record the source position of the statement as this code which
4047 // is after the code for the body actually belongs to the loop
4048 // statement and not the body.
4049 CodeForStatementPosition(node);
4050 Visit(node->next());
4051 }
4052 }
4053
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00004054 // Set the type of the loop variable to smi before compiling the test
4055 // expression if we are in a fast smi loop condition.
fschneider@chromium.orgaae9ef22010-03-18 14:38:11 +00004056 if (node->is_fast_smi_loop() && has_valid_frame()) {
fschneider@chromium.org5a49b7a2010-03-18 10:06:01 +00004057 // Set number type of the loop variable to smi.
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00004058 SetTypeForStackSlot(node->loop_variable()->slot(), TypeInfo::Smi());
fschneider@chromium.org5a49b7a2010-03-18 10:06:01 +00004059 }
4060
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004061 // Based on the condition analysis, compile the backward jump as
4062 // necessary.
4063 switch (info) {
4064 case ALWAYS_TRUE:
4065 if (has_valid_frame()) {
4066 if (node->next() == NULL) {
4067 node->continue_target()->Jump();
4068 } else {
4069 loop.Jump();
4070 }
4071 }
4072 break;
4073 case DONT_KNOW:
4074 if (test_at_bottom) {
4075 if (node->continue_target()->is_linked()) {
4076 // We can have dangling jumps to the continue target if there
4077 // was no update expression.
4078 node->continue_target()->Bind();
4079 }
4080 // Control can reach the test at the bottom by falling out of
4081 // the body, by a continue in the body, or from the update
4082 // expression.
4083 if (has_valid_frame()) {
4084 // The break target is the fall-through (body is a backward
4085 // jump from here).
4086 ControlDestination dest(&body, node->break_target(), false);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004087 LoadCondition(node->cond(), &dest, true);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004088 }
4089 } else {
4090 // Otherwise, jump back to the test at the top.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004091 if (has_valid_frame()) {
4092 if (node->next() == NULL) {
4093 node->continue_target()->Jump();
4094 } else {
4095 loop.Jump();
4096 }
4097 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004098 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004099 break;
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004100 case ALWAYS_FALSE:
4101 UNREACHABLE();
4102 break;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004103 }
4104
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004105 // The break target may be already bound (by the condition), or
4106 // there may not be a valid frame. Bind it only if needed.
4107 if (node->break_target()->is_linked()) {
4108 node->break_target()->Bind();
4109 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00004110 DecrementLoopNesting();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004111}
4112
4113
ager@chromium.org7c537e22008-10-16 08:43:32 +00004114void CodeGenerator::VisitForInStatement(ForInStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004115 ASSERT(!in_spilled_code());
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004116 VirtualFrame::SpilledScope spilled_scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004117 Comment cmnt(masm_, "[ ForInStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004118 CodeForStatementPosition(node);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004119
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004120 JumpTarget primitive;
4121 JumpTarget jsobject;
4122 JumpTarget fixed_array;
4123 JumpTarget entry(JumpTarget::BIDIRECTIONAL);
4124 JumpTarget end_del_check;
4125 JumpTarget exit;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004126
4127 // Get the object to enumerate over (converted to JSObject).
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004128 LoadAndSpill(node->enumerable());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004129
4130 // Both SpiderMonkey and kjs ignore null and undefined in contrast
4131 // to the specification. 12.6.4 mandates a call to ToObject.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004132 frame_->EmitPop(eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004133
4134 // eax: value to be iterated over
4135 __ cmp(eax, Factory::undefined_value());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004136 exit.Branch(equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004137 __ cmp(eax, Factory::null_value());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004138 exit.Branch(equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004139
4140 // Stack layout in body:
kasper.lund7276f142008-07-30 08:49:36 +00004141 // [iteration counter (smi)] <- slot 0
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004142 // [length of array] <- slot 1
4143 // [FixedArray] <- slot 2
4144 // [Map or 0] <- slot 3
4145 // [Object] <- slot 4
4146
4147 // Check if enumerable is already a JSObject
4148 // eax: value to be iterated over
4149 __ test(eax, Immediate(kSmiTagMask));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004150 primitive.Branch(zero);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004151 __ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset));
4152 __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
ager@chromium.orgc27e4e72008-09-04 13:52:27 +00004153 __ cmp(ecx, FIRST_JS_OBJECT_TYPE);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004154 jsobject.Branch(above_equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004155
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004156 primitive.Bind();
4157 frame_->EmitPush(eax);
4158 frame_->InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004159 // function call returns the value in eax, which is where we want it below
4160
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004161 jsobject.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004162 // Get the set of properties (as a FixedArray or Map).
4163 // eax: value to be iterated over
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00004164 frame_->EmitPush(eax); // Push the object being iterated over.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004165
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00004166 // Check cache validity in generated code. This is a fast case for
4167 // the JSObject::IsSimpleEnum cache validity checks. If we cannot
4168 // guarantee cache validity, call the runtime system to check cache
4169 // validity or get the property names in a fixed array.
4170 JumpTarget call_runtime;
4171 JumpTarget loop(JumpTarget::BIDIRECTIONAL);
4172 JumpTarget check_prototype;
4173 JumpTarget use_cache;
4174 __ mov(ecx, eax);
4175 loop.Bind();
4176 // Check that there are no elements.
4177 __ mov(edx, FieldOperand(ecx, JSObject::kElementsOffset));
4178 __ cmp(Operand(edx), Immediate(Factory::empty_fixed_array()));
4179 call_runtime.Branch(not_equal);
4180 // Check that instance descriptors are not empty so that we can
4181 // check for an enum cache. Leave the map in ebx for the subsequent
4182 // prototype load.
4183 __ mov(ebx, FieldOperand(ecx, HeapObject::kMapOffset));
4184 __ mov(edx, FieldOperand(ebx, Map::kInstanceDescriptorsOffset));
4185 __ cmp(Operand(edx), Immediate(Factory::empty_descriptor_array()));
4186 call_runtime.Branch(equal);
4187 // Check that there in an enum cache in the non-empty instance
4188 // descriptors. This is the case if the next enumeration index
4189 // field does not contain a smi.
4190 __ mov(edx, FieldOperand(edx, DescriptorArray::kEnumerationIndexOffset));
4191 __ test(edx, Immediate(kSmiTagMask));
4192 call_runtime.Branch(zero);
4193 // For all objects but the receiver, check that the cache is empty.
4194 __ cmp(ecx, Operand(eax));
4195 check_prototype.Branch(equal);
4196 __ mov(edx, FieldOperand(edx, DescriptorArray::kEnumCacheBridgeCacheOffset));
4197 __ cmp(Operand(edx), Immediate(Factory::empty_fixed_array()));
4198 call_runtime.Branch(not_equal);
4199 check_prototype.Bind();
4200 // Load the prototype from the map and loop if non-null.
4201 __ mov(ecx, FieldOperand(ebx, Map::kPrototypeOffset));
4202 __ cmp(Operand(ecx), Immediate(Factory::null_value()));
4203 loop.Branch(not_equal);
4204 // The enum cache is valid. Load the map of the object being
4205 // iterated over and use the cache for the iteration.
4206 __ mov(eax, FieldOperand(eax, HeapObject::kMapOffset));
4207 use_cache.Jump();
4208
4209 call_runtime.Bind();
4210 // Call the runtime to get the property names for the object.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004211 frame_->EmitPush(eax); // push the Object (slot 4) for the runtime call
4212 frame_->CallRuntime(Runtime::kGetPropertyNamesFast, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004213
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00004214 // If we got a map from the runtime call, we can do a fast
4215 // modification check. Otherwise, we got a fixed array, and we have
4216 // to do a slow check.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004217 // eax: map or fixed array (result from call to
4218 // Runtime::kGetPropertyNamesFast)
4219 __ mov(edx, Operand(eax));
4220 __ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset));
4221 __ cmp(ecx, Factory::meta_map());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004222 fixed_array.Branch(not_equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004223
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00004224 use_cache.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004225 // Get enum cache
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00004226 // eax: map (either the result from a call to
4227 // Runtime::kGetPropertyNamesFast or has been fetched directly from
4228 // the object)
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004229 __ mov(ecx, Operand(eax));
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00004230
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004231 __ mov(ecx, FieldOperand(ecx, Map::kInstanceDescriptorsOffset));
4232 // Get the bridge array held in the enumeration index field.
4233 __ mov(ecx, FieldOperand(ecx, DescriptorArray::kEnumerationIndexOffset));
4234 // Get the cache from the bridge array.
4235 __ mov(edx, FieldOperand(ecx, DescriptorArray::kEnumCacheBridgeCacheOffset));
4236
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004237 frame_->EmitPush(eax); // <- slot 3
4238 frame_->EmitPush(edx); // <- slot 2
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004239 __ mov(eax, FieldOperand(edx, FixedArray::kLengthOffset));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004240 frame_->EmitPush(eax); // <- slot 1
4241 frame_->EmitPush(Immediate(Smi::FromInt(0))); // <- slot 0
4242 entry.Jump();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004243
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004244 fixed_array.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004245 // eax: fixed array (result from call to Runtime::kGetPropertyNamesFast)
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004246 frame_->EmitPush(Immediate(Smi::FromInt(0))); // <- slot 3
4247 frame_->EmitPush(eax); // <- slot 2
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004248
4249 // Push the length of the array and the initial index onto the stack.
4250 __ mov(eax, FieldOperand(eax, FixedArray::kLengthOffset));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004251 frame_->EmitPush(eax); // <- slot 1
4252 frame_->EmitPush(Immediate(Smi::FromInt(0))); // <- slot 0
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004253
4254 // Condition.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004255 entry.Bind();
4256 // Grab the current frame's height for the break and continue
4257 // targets only after all the state is pushed on the frame.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004258 node->break_target()->set_direction(JumpTarget::FORWARD_ONLY);
4259 node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004260
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004261 __ mov(eax, frame_->ElementAt(0)); // load the current count
4262 __ cmp(eax, frame_->ElementAt(1)); // compare to the array length
4263 node->break_target()->Branch(above_equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004264
4265 // Get the i'th entry of the array.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004266 __ mov(edx, frame_->ElementAt(2));
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00004267 __ mov(ebx, FixedArrayElementOperand(edx, eax));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004268
4269 // Get the expected map from the stack or a zero map in the
4270 // permanent slow case eax: current iteration count ebx: i'th entry
4271 // of the enum cache
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004272 __ mov(edx, frame_->ElementAt(3));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004273 // Check if the expected map still matches that of the enumerable.
4274 // If not, we have to filter the key.
4275 // eax: current iteration count
4276 // ebx: i'th entry of the enum cache
4277 // edx: expected map value
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004278 __ mov(ecx, frame_->ElementAt(4));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004279 __ mov(ecx, FieldOperand(ecx, HeapObject::kMapOffset));
4280 __ cmp(ecx, Operand(edx));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004281 end_del_check.Branch(equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004282
4283 // Convert the entry to a string (or null if it isn't a property anymore).
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004284 frame_->EmitPush(frame_->ElementAt(4)); // push enumerable
4285 frame_->EmitPush(ebx); // push entry
4286 frame_->InvokeBuiltin(Builtins::FILTER_KEY, CALL_FUNCTION, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004287 __ mov(ebx, Operand(eax));
4288
4289 // If the property has been removed while iterating, we just skip it.
4290 __ cmp(ebx, Factory::null_value());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004291 node->continue_target()->Branch(equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004292
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004293 end_del_check.Bind();
4294 // Store the entry in the 'each' expression and take another spin in the
4295 // loop. edx: i'th entry of the enum cache (or string there of)
4296 frame_->EmitPush(ebx);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004297 { Reference each(this, node->each());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004298 // Loading a reference may leave the frame in an unspilled state.
4299 frame_->SpillAll();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004300 if (!each.is_illegal()) {
4301 if (each.size() > 0) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004302 frame_->EmitPush(frame_->ElementAt(each.size()));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004303 each.SetValue(NOT_CONST_INIT);
4304 frame_->Drop(2);
4305 } else {
4306 // If the reference was to a slot we rely on the convenient property
4307 // that it doesn't matter whether a value (eg, ebx pushed above) is
4308 // right on top of or right underneath a zero-sized reference.
4309 each.SetValue(NOT_CONST_INIT);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004310 frame_->Drop();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004311 }
4312 }
4313 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004314 // Unloading a reference may leave the frame in an unspilled state.
4315 frame_->SpillAll();
4316
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004317 // Body.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004318 CheckStack(); // TODO(1222600): ignore if body contains calls.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004319 VisitAndSpill(node->body());
4320
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004321 // Next. Reestablish a spilled frame in case we are coming here via
4322 // a continue in the body.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004323 node->continue_target()->Bind();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004324 frame_->SpillAll();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004325 frame_->EmitPop(eax);
4326 __ add(Operand(eax), Immediate(Smi::FromInt(1)));
4327 frame_->EmitPush(eax);
4328 entry.Jump();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004329
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004330 // Cleanup. No need to spill because VirtualFrame::Drop is safe for
4331 // any frame.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004332 node->break_target()->Bind();
ager@chromium.org7c537e22008-10-16 08:43:32 +00004333 frame_->Drop(5);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004334
4335 // Exit.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004336 exit.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004337
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004338 node->continue_target()->Unuse();
4339 node->break_target()->Unuse();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004340}
4341
4342
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004343void CodeGenerator::VisitTryCatchStatement(TryCatchStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004344 ASSERT(!in_spilled_code());
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004345 VirtualFrame::SpilledScope spilled_scope;
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004346 Comment cmnt(masm_, "[ TryCatchStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004347 CodeForStatementPosition(node);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004348
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004349 JumpTarget try_block;
4350 JumpTarget exit;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004351
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004352 try_block.Call();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004353 // --- Catch block ---
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004354 frame_->EmitPush(eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004355
4356 // Store the caught exception in the catch variable.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004357 Variable* catch_var = node->catch_var()->var();
4358 ASSERT(catch_var != NULL && catch_var->slot() != NULL);
4359 StoreToSlot(catch_var->slot(), NOT_CONST_INIT);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004360
4361 // Remove the exception from the stack.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004362 frame_->Drop();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004363
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004364 VisitStatementsAndSpill(node->catch_block()->statements());
4365 if (has_valid_frame()) {
4366 exit.Jump();
4367 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004368
4369
4370 // --- Try block ---
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004371 try_block.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004372
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004373 frame_->PushTryHandler(TRY_CATCH_HANDLER);
4374 int handler_height = frame_->height();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004375
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004376 // Shadow the jump targets for all escapes from the try block, including
4377 // returns. During shadowing, the original target is hidden as the
4378 // ShadowTarget and operations on the original actually affect the
4379 // shadowing target.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004380 //
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004381 // We should probably try to unify the escaping targets and the return
4382 // target.
4383 int nof_escapes = node->escaping_targets()->length();
4384 List<ShadowTarget*> shadows(1 + nof_escapes);
4385
4386 // Add the shadow target for the function return.
4387 static const int kReturnShadowIndex = 0;
4388 shadows.Add(new ShadowTarget(&function_return_));
4389 bool function_return_was_shadowed = function_return_is_shadowed_;
4390 function_return_is_shadowed_ = true;
4391 ASSERT(shadows[kReturnShadowIndex]->other_target() == &function_return_);
4392
4393 // Add the remaining shadow targets.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004394 for (int i = 0; i < nof_escapes; i++) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004395 shadows.Add(new ShadowTarget(node->escaping_targets()->at(i)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004396 }
4397
4398 // Generate code for the statements in the try block.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004399 VisitStatementsAndSpill(node->try_block()->statements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004400
4401 // Stop the introduced shadowing and count the number of required unlinks.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004402 // After shadowing stops, the original targets are unshadowed and the
4403 // ShadowTargets represent the formerly shadowing targets.
4404 bool has_unlinks = false;
4405 for (int i = 0; i < shadows.length(); i++) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004406 shadows[i]->StopShadowing();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004407 has_unlinks = has_unlinks || shadows[i]->is_linked();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004408 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004409 function_return_is_shadowed_ = function_return_was_shadowed;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004410
kasper.lund7276f142008-07-30 08:49:36 +00004411 // Get an external reference to the handler address.
4412 ExternalReference handler_address(Top::k_handler_address);
4413
4414 // Make sure that there's nothing left on the stack above the
4415 // handler structure.
4416 if (FLAG_debug_code) {
4417 __ mov(eax, Operand::StaticVariable(handler_address));
kasper.lund7276f142008-07-30 08:49:36 +00004418 __ cmp(esp, Operand(eax));
4419 __ Assert(equal, "stack pointer should point to top handler");
4420 }
4421
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004422 // If we can fall off the end of the try block, unlink from try chain.
4423 if (has_valid_frame()) {
4424 // The next handler address is on top of the frame. Unlink from
4425 // the handler list and drop the rest of this handler from the
4426 // frame.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00004427 ASSERT(StackHandlerConstants::kNextOffset == 0);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004428 frame_->EmitPop(Operand::StaticVariable(handler_address));
4429 frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1);
4430 if (has_unlinks) {
4431 exit.Jump();
4432 }
4433 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004434
kasperl@chromium.org8ccb0be2009-04-07 07:21:39 +00004435 // Generate unlink code for the (formerly) shadowing targets that
4436 // have been jumped to. Deallocate each shadow target.
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00004437 Result return_value;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004438 for (int i = 0; i < shadows.length(); i++) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004439 if (shadows[i]->is_linked()) {
kasperl@chromium.org8ccb0be2009-04-07 07:21:39 +00004440 // Unlink from try chain; be careful not to destroy the TOS if
4441 // there is one.
4442 if (i == kReturnShadowIndex) {
4443 shadows[i]->Bind(&return_value);
4444 return_value.ToRegister(eax);
4445 } else {
4446 shadows[i]->Bind();
4447 }
4448 // Because we can be jumping here (to spilled code) from
4449 // unspilled code, we need to reestablish a spilled frame at
4450 // this block.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004451 frame_->SpillAll();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004452
4453 // Reload sp from the top handler, because some statements that we
4454 // break from (eg, for...in) may have left stuff on the stack.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00004455 __ mov(esp, Operand::StaticVariable(handler_address));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004456 frame_->Forget(frame_->height() - handler_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004457
ager@chromium.orgeadaf222009-06-16 09:43:10 +00004458 ASSERT(StackHandlerConstants::kNextOffset == 0);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004459 frame_->EmitPop(Operand::StaticVariable(handler_address));
ager@chromium.org7c537e22008-10-16 08:43:32 +00004460 frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004461
kasperl@chromium.org8ccb0be2009-04-07 07:21:39 +00004462 if (i == kReturnShadowIndex) {
4463 if (!function_return_is_shadowed_) frame_->PrepareForReturn();
4464 shadows[i]->other_target()->Jump(&return_value);
4465 } else {
4466 shadows[i]->other_target()->Jump();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004467 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004468 }
4469 }
4470
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004471 exit.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004472}
4473
4474
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004475void CodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004476 ASSERT(!in_spilled_code());
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004477 VirtualFrame::SpilledScope spilled_scope;
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004478 Comment cmnt(masm_, "[ TryFinallyStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004479 CodeForStatementPosition(node);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004480
4481 // State: Used to keep track of reason for entering the finally
4482 // block. Should probably be extended to hold information for
4483 // break/continue from within the try block.
4484 enum { FALLING, THROWING, JUMPING };
4485
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004486 JumpTarget try_block;
4487 JumpTarget finally_block;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004488
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004489 try_block.Call();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004490
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004491 frame_->EmitPush(eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004492 // In case of thrown exceptions, this is where we continue.
4493 __ Set(ecx, Immediate(Smi::FromInt(THROWING)));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004494 finally_block.Jump();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004495
4496 // --- Try block ---
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004497 try_block.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004498
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004499 frame_->PushTryHandler(TRY_FINALLY_HANDLER);
4500 int handler_height = frame_->height();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004501
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004502 // Shadow the jump targets for all escapes from the try block, including
4503 // returns. During shadowing, the original target is hidden as the
4504 // ShadowTarget and operations on the original actually affect the
4505 // shadowing target.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004506 //
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004507 // We should probably try to unify the escaping targets and the return
4508 // target.
4509 int nof_escapes = node->escaping_targets()->length();
4510 List<ShadowTarget*> shadows(1 + nof_escapes);
4511
4512 // Add the shadow target for the function return.
4513 static const int kReturnShadowIndex = 0;
4514 shadows.Add(new ShadowTarget(&function_return_));
4515 bool function_return_was_shadowed = function_return_is_shadowed_;
4516 function_return_is_shadowed_ = true;
4517 ASSERT(shadows[kReturnShadowIndex]->other_target() == &function_return_);
4518
4519 // Add the remaining shadow targets.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004520 for (int i = 0; i < nof_escapes; i++) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004521 shadows.Add(new ShadowTarget(node->escaping_targets()->at(i)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004522 }
4523
4524 // Generate code for the statements in the try block.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004525 VisitStatementsAndSpill(node->try_block()->statements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004526
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004527 // Stop the introduced shadowing and count the number of required unlinks.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004528 // After shadowing stops, the original targets are unshadowed and the
4529 // ShadowTargets represent the formerly shadowing targets.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004530 int nof_unlinks = 0;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004531 for (int i = 0; i < shadows.length(); i++) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004532 shadows[i]->StopShadowing();
4533 if (shadows[i]->is_linked()) nof_unlinks++;
4534 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004535 function_return_is_shadowed_ = function_return_was_shadowed;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004536
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004537 // Get an external reference to the handler address.
4538 ExternalReference handler_address(Top::k_handler_address);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004539
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004540 // If we can fall off the end of the try block, unlink from the try
4541 // chain and set the state on the frame to FALLING.
4542 if (has_valid_frame()) {
4543 // The next handler address is on top of the frame.
4544 ASSERT(StackHandlerConstants::kNextOffset == 0);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00004545 frame_->EmitPop(Operand::StaticVariable(handler_address));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004546 frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1);
4547
4548 // Fake a top of stack value (unneeded when FALLING) and set the
4549 // state in ecx, then jump around the unlink blocks if any.
4550 frame_->EmitPush(Immediate(Factory::undefined_value()));
4551 __ Set(ecx, Immediate(Smi::FromInt(FALLING)));
4552 if (nof_unlinks > 0) {
4553 finally_block.Jump();
4554 }
4555 }
4556
4557 // Generate code to unlink and set the state for the (formerly)
4558 // shadowing targets that have been jumped to.
4559 for (int i = 0; i < shadows.length(); i++) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004560 if (shadows[i]->is_linked()) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004561 // If we have come from the shadowed return, the return value is
kasperl@chromium.org8ccb0be2009-04-07 07:21:39 +00004562 // on the virtual frame. We must preserve it until it is
4563 // pushed.
4564 if (i == kReturnShadowIndex) {
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00004565 Result return_value;
kasperl@chromium.org8ccb0be2009-04-07 07:21:39 +00004566 shadows[i]->Bind(&return_value);
4567 return_value.ToRegister(eax);
4568 } else {
4569 shadows[i]->Bind();
4570 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004571 // Because we can be jumping here (to spilled code) from
4572 // unspilled code, we need to reestablish a spilled frame at
4573 // this block.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004574 frame_->SpillAll();
4575
4576 // Reload sp from the top handler, because some statements that
4577 // we break from (eg, for...in) may have left stuff on the
4578 // stack.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00004579 __ mov(esp, Operand::StaticVariable(handler_address));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004580 frame_->Forget(frame_->height() - handler_height);
4581
4582 // Unlink this handler and drop it from the frame.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00004583 ASSERT(StackHandlerConstants::kNextOffset == 0);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004584 frame_->EmitPop(Operand::StaticVariable(handler_address));
4585 frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1);
4586
4587 if (i == kReturnShadowIndex) {
4588 // If this target shadowed the function return, materialize
4589 // the return value on the stack.
4590 frame_->EmitPush(eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004591 } else {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004592 // Fake TOS for targets that shadowed breaks and continues.
4593 frame_->EmitPush(Immediate(Factory::undefined_value()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004594 }
4595 __ Set(ecx, Immediate(Smi::FromInt(JUMPING + i)));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004596 if (--nof_unlinks > 0) {
4597 // If this is not the last unlink block, jump around the next.
4598 finally_block.Jump();
4599 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004600 }
4601 }
4602
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004603 // --- Finally block ---
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004604 finally_block.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004605
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004606 // Push the state on the stack.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004607 frame_->EmitPush(ecx);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004608
4609 // We keep two elements on the stack - the (possibly faked) result
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004610 // and the state - while evaluating the finally block.
4611 //
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004612 // Generate code for the statements in the finally block.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004613 VisitStatementsAndSpill(node->finally_block()->statements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004614
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004615 if (has_valid_frame()) {
4616 // Restore state and return value or faked TOS.
4617 frame_->EmitPop(ecx);
4618 frame_->EmitPop(eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004619 }
4620
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004621 // Generate code to jump to the right destination for all used
4622 // formerly shadowing targets. Deallocate each shadow target.
4623 for (int i = 0; i < shadows.length(); i++) {
4624 if (has_valid_frame() && shadows[i]->is_bound()) {
kasperl@chromium.org8ccb0be2009-04-07 07:21:39 +00004625 BreakTarget* original = shadows[i]->other_target();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004626 __ cmp(Operand(ecx), Immediate(Smi::FromInt(JUMPING + i)));
kasperl@chromium.org8ccb0be2009-04-07 07:21:39 +00004627 if (i == kReturnShadowIndex) {
4628 // The return value is (already) in eax.
4629 Result return_value = allocator_->Allocate(eax);
4630 ASSERT(return_value.is_valid());
4631 if (function_return_is_shadowed_) {
4632 original->Branch(equal, &return_value);
4633 } else {
4634 // Branch around the preparation for return which may emit
4635 // code.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004636 JumpTarget skip;
kasperl@chromium.org8ccb0be2009-04-07 07:21:39 +00004637 skip.Branch(not_equal);
4638 frame_->PrepareForReturn();
4639 original->Jump(&return_value);
4640 skip.Bind();
4641 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004642 } else {
4643 original->Branch(equal);
4644 }
4645 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004646 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004647
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004648 if (has_valid_frame()) {
4649 // Check if we need to rethrow the exception.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004650 JumpTarget exit;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004651 __ cmp(Operand(ecx), Immediate(Smi::FromInt(THROWING)));
4652 exit.Branch(not_equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004653
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004654 // Rethrow exception.
4655 frame_->EmitPush(eax); // undo pop from above
4656 frame_->CallRuntime(Runtime::kReThrow, 1);
4657
4658 // Done.
4659 exit.Bind();
4660 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004661}
4662
4663
ager@chromium.org7c537e22008-10-16 08:43:32 +00004664void CodeGenerator::VisitDebuggerStatement(DebuggerStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004665 ASSERT(!in_spilled_code());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004666 Comment cmnt(masm_, "[ DebuggerStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004667 CodeForStatementPosition(node);
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004668#ifdef ENABLE_DEBUGGER_SUPPORT
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004669 // Spill everything, even constants, to the frame.
4670 frame_->SpillAll();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004671
ager@chromium.org5c838252010-02-19 08:53:10 +00004672 frame_->DebugBreak();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004673 // Ignore the return value.
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004674#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004675}
4676
4677
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00004678Result CodeGenerator::InstantiateFunction(
4679 Handle<SharedFunctionInfo> function_info) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004680 // The inevitable call will sync frame elements to memory anyway, so
4681 // we do it eagerly to allow us to push the arguments directly into
4682 // place.
ager@chromium.org5c838252010-02-19 08:53:10 +00004683 frame()->SyncRange(0, frame()->element_count() - 1);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004684
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00004685 // Use the fast case closure allocation code that allocates in new
4686 // space for nested functions that don't need literals cloning.
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00004687 if (scope()->is_function_scope() && function_info->num_literals() == 0) {
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00004688 FastNewClosureStub stub;
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00004689 frame()->EmitPush(Immediate(function_info));
ager@chromium.org5c838252010-02-19 08:53:10 +00004690 return frame()->CallStub(&stub, 1);
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00004691 } else {
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00004692 // Call the runtime to instantiate the function based on the
4693 // shared function info.
ager@chromium.org5c838252010-02-19 08:53:10 +00004694 frame()->EmitPush(esi);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00004695 frame()->EmitPush(Immediate(function_info));
ager@chromium.org5c838252010-02-19 08:53:10 +00004696 return frame()->CallRuntime(Runtime::kNewClosure, 2);
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00004697 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004698}
4699
4700
ager@chromium.org7c537e22008-10-16 08:43:32 +00004701void CodeGenerator::VisitFunctionLiteral(FunctionLiteral* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004702 Comment cmnt(masm_, "[ FunctionLiteral");
fschneider@chromium.org086aac62010-03-17 13:18:24 +00004703 ASSERT(!in_safe_int32_mode());
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00004704 // Build the function info and instantiate it.
4705 Handle<SharedFunctionInfo> function_info =
4706 Compiler::BuildFunctionInfo(node, script(), this);
kasper.lund212ac232008-07-16 07:07:30 +00004707 // Check for stack-overflow exception.
4708 if (HasStackOverflow()) return;
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00004709 Result result = InstantiateFunction(function_info);
ager@chromium.org5c838252010-02-19 08:53:10 +00004710 frame()->Push(&result);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004711}
4712
4713
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00004714void CodeGenerator::VisitSharedFunctionInfoLiteral(
4715 SharedFunctionInfoLiteral* node) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00004716 ASSERT(!in_safe_int32_mode());
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00004717 Comment cmnt(masm_, "[ SharedFunctionInfoLiteral");
4718 Result result = InstantiateFunction(node->shared_function_info());
ager@chromium.org5c838252010-02-19 08:53:10 +00004719 frame()->Push(&result);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004720}
4721
4722
ager@chromium.org7c537e22008-10-16 08:43:32 +00004723void CodeGenerator::VisitConditional(Conditional* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004724 Comment cmnt(masm_, "[ Conditional");
fschneider@chromium.org086aac62010-03-17 13:18:24 +00004725 ASSERT(!in_safe_int32_mode());
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004726 JumpTarget then;
4727 JumpTarget else_;
4728 JumpTarget exit;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004729 ControlDestination dest(&then, &else_, true);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004730 LoadCondition(node->condition(), &dest, true);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004731
4732 if (dest.false_was_fall_through()) {
4733 // The else target was bound, so we compile the else part first.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004734 Load(node->else_expression());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004735
4736 if (then.is_linked()) {
4737 exit.Jump();
4738 then.Bind();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004739 Load(node->then_expression());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004740 }
4741 } else {
4742 // The then target was bound, so we compile the then part first.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004743 Load(node->then_expression());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004744
4745 if (else_.is_linked()) {
4746 exit.Jump();
4747 else_.Bind();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004748 Load(node->else_expression());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004749 }
4750 }
4751
4752 exit.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004753}
4754
4755
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004756void CodeGenerator::LoadFromSlot(Slot* slot, TypeofState typeof_state) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00004757 if (slot->type() == Slot::LOOKUP) {
ager@chromium.org381abbb2009-02-25 13:23:22 +00004758 ASSERT(slot->var()->is_dynamic());
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004759 JumpTarget slow;
4760 JumpTarget done;
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004761 Result value;
ager@chromium.org381abbb2009-02-25 13:23:22 +00004762
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00004763 // Generate fast case for loading from slots that correspond to
4764 // local/global variables or arguments unless they are shadowed by
4765 // eval-introduced bindings.
4766 EmitDynamicLoadFromSlotFastCase(slot,
4767 typeof_state,
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004768 &value,
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00004769 &slow,
4770 &done);
ager@chromium.org381abbb2009-02-25 13:23:22 +00004771
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004772 slow.Bind();
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00004773 // A runtime call is inevitable. We eagerly sync frame elements
4774 // to memory so that we can push the arguments directly into place
4775 // on top of the frame.
ager@chromium.org5c838252010-02-19 08:53:10 +00004776 frame()->SyncRange(0, frame()->element_count() - 1);
4777 frame()->EmitPush(esi);
4778 frame()->EmitPush(Immediate(slot->var()->name()));
ager@chromium.org7c537e22008-10-16 08:43:32 +00004779 if (typeof_state == INSIDE_TYPEOF) {
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004780 value =
ager@chromium.org5c838252010-02-19 08:53:10 +00004781 frame()->CallRuntime(Runtime::kLoadContextSlotNoReferenceError, 2);
ager@chromium.org7c537e22008-10-16 08:43:32 +00004782 } else {
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004783 value = frame()->CallRuntime(Runtime::kLoadContextSlot, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004784 }
ager@chromium.org381abbb2009-02-25 13:23:22 +00004785
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004786 done.Bind(&value);
4787 frame_->Push(&value);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004788
4789 } else if (slot->var()->mode() == Variable::CONST) {
4790 // Const slots may contain 'the hole' value (the constant hasn't been
4791 // initialized yet) which needs to be converted into the 'undefined'
4792 // value.
4793 //
4794 // We currently spill the virtual frame because constants use the
4795 // potentially unsafe direct-frame access of SlotOperand.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004796 VirtualFrame::SpilledScope spilled_scope;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004797 Comment cmnt(masm_, "[ Load const");
ager@chromium.org5c838252010-02-19 08:53:10 +00004798 Label exit;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004799 __ mov(ecx, SlotOperand(slot, ecx));
4800 __ cmp(ecx, Factory::the_hole_value());
ager@chromium.org5c838252010-02-19 08:53:10 +00004801 __ j(not_equal, &exit);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004802 __ mov(ecx, Factory::undefined_value());
ager@chromium.org5c838252010-02-19 08:53:10 +00004803 __ bind(&exit);
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004804 frame()->EmitPush(ecx);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004805
4806 } else if (slot->type() == Slot::PARAMETER) {
ager@chromium.org5c838252010-02-19 08:53:10 +00004807 frame()->PushParameterAt(slot->index());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004808
4809 } else if (slot->type() == Slot::LOCAL) {
ager@chromium.org5c838252010-02-19 08:53:10 +00004810 frame()->PushLocalAt(slot->index());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004811
4812 } else {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004813 // The other remaining slot types (LOOKUP and GLOBAL) cannot reach
4814 // here.
4815 //
4816 // The use of SlotOperand below is safe for an unspilled frame
4817 // because it will always be a context slot.
4818 ASSERT(slot->type() == Slot::CONTEXT);
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004819 Result temp = allocator()->Allocate();
4820 ASSERT(temp.is_valid());
4821 __ mov(temp.reg(), SlotOperand(slot, temp.reg()));
4822 frame()->Push(&temp);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004823 }
4824}
4825
4826
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004827void CodeGenerator::LoadFromSlotCheckForArguments(Slot* slot,
ager@chromium.org5c838252010-02-19 08:53:10 +00004828 TypeofState state) {
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004829 LoadFromSlot(slot, state);
ager@chromium.org3e875802009-06-29 08:26:34 +00004830
4831 // Bail out quickly if we're not using lazy arguments allocation.
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004832 if (ArgumentsMode() != LAZY_ARGUMENTS_ALLOCATION) return;
ager@chromium.org3e875802009-06-29 08:26:34 +00004833
4834 // ... or if the slot isn't a non-parameter arguments slot.
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004835 if (slot->type() == Slot::PARAMETER || !slot->is_arguments()) return;
ager@chromium.org3e875802009-06-29 08:26:34 +00004836
4837 // If the loaded value is a constant, we know if the arguments
4838 // object has been lazily loaded yet.
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004839 Result result = frame()->Pop();
ager@chromium.org5c838252010-02-19 08:53:10 +00004840 if (result.is_constant()) {
4841 if (result.handle()->IsTheHole()) {
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004842 result = StoreArgumentsObject(false);
ager@chromium.org3e875802009-06-29 08:26:34 +00004843 }
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004844 frame()->Push(&result);
4845 return;
ager@chromium.org3e875802009-06-29 08:26:34 +00004846 }
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004847 ASSERT(result.is_register());
ager@chromium.org3e875802009-06-29 08:26:34 +00004848 // The loaded value is in a register. If it is the sentinel that
4849 // indicates that we haven't loaded the arguments object yet, we
4850 // need to do it now.
4851 JumpTarget exit;
ager@chromium.org5c838252010-02-19 08:53:10 +00004852 __ cmp(Operand(result.reg()), Immediate(Factory::the_hole_value()));
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004853 frame()->Push(&result);
4854 exit.Branch(not_equal);
ager@chromium.org5c838252010-02-19 08:53:10 +00004855
ager@chromium.org5c838252010-02-19 08:53:10 +00004856 result = StoreArgumentsObject(false);
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004857 frame()->SetElementAt(0, &result);
4858 result.Unuse();
4859 exit.Bind();
4860 return;
ager@chromium.org3e875802009-06-29 08:26:34 +00004861}
4862
4863
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004864Result CodeGenerator::LoadFromGlobalSlotCheckExtensions(
4865 Slot* slot,
4866 TypeofState typeof_state,
4867 JumpTarget* slow) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00004868 ASSERT(!in_safe_int32_mode());
ager@chromium.org381abbb2009-02-25 13:23:22 +00004869 // Check that no extension objects have been created by calls to
4870 // eval from the current scope to the global scope.
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00004871 Register context = esi;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004872 Result tmp = allocator_->Allocate();
4873 ASSERT(tmp.is_valid()); // All non-reserved registers were available.
4874
ager@chromium.org381abbb2009-02-25 13:23:22 +00004875 Scope* s = scope();
4876 while (s != NULL) {
4877 if (s->num_heap_slots() > 0) {
4878 if (s->calls_eval()) {
4879 // Check that extension is NULL.
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00004880 __ cmp(ContextOperand(context, Context::EXTENSION_INDEX),
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004881 Immediate(0));
4882 slow->Branch(not_equal, not_taken);
ager@chromium.org381abbb2009-02-25 13:23:22 +00004883 }
4884 // Load next context in chain.
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00004885 __ mov(tmp.reg(), ContextOperand(context, Context::CLOSURE_INDEX));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004886 __ mov(tmp.reg(), FieldOperand(tmp.reg(), JSFunction::kContextOffset));
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00004887 context = tmp.reg();
ager@chromium.org381abbb2009-02-25 13:23:22 +00004888 }
4889 // If no outer scope calls eval, we do not need to check more
4890 // context extensions. If we have reached an eval scope, we check
4891 // all extensions from this point.
4892 if (!s->outer_scope_calls_eval() || s->is_eval_scope()) break;
4893 s = s->outer_scope();
4894 }
4895
kasperl@chromium.orge959c182009-07-27 08:59:04 +00004896 if (s != NULL && s->is_eval_scope()) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004897 // Loop up the context chain. There is no frame effect so it is
4898 // safe to use raw labels here.
ager@chromium.org381abbb2009-02-25 13:23:22 +00004899 Label next, fast;
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00004900 if (!context.is(tmp.reg())) {
4901 __ mov(tmp.reg(), context);
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004902 }
ager@chromium.org381abbb2009-02-25 13:23:22 +00004903 __ bind(&next);
4904 // Terminate at global context.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004905 __ cmp(FieldOperand(tmp.reg(), HeapObject::kMapOffset),
ager@chromium.org381abbb2009-02-25 13:23:22 +00004906 Immediate(Factory::global_context_map()));
4907 __ j(equal, &fast);
4908 // Check that extension is NULL.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004909 __ cmp(ContextOperand(tmp.reg(), Context::EXTENSION_INDEX), Immediate(0));
4910 slow->Branch(not_equal, not_taken);
ager@chromium.org381abbb2009-02-25 13:23:22 +00004911 // Load next context in chain.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004912 __ mov(tmp.reg(), ContextOperand(tmp.reg(), Context::CLOSURE_INDEX));
4913 __ mov(tmp.reg(), FieldOperand(tmp.reg(), JSFunction::kContextOffset));
ager@chromium.org381abbb2009-02-25 13:23:22 +00004914 __ jmp(&next);
4915 __ bind(&fast);
4916 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004917 tmp.Unuse();
ager@chromium.org381abbb2009-02-25 13:23:22 +00004918
4919 // All extension objects were empty and it is safe to use a global
4920 // load IC call.
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00004921 // The register allocator prefers eax if it is free, so the code generator
4922 // will load the global object directly into eax, which is where the LoadIC
4923 // expects it.
4924 frame_->Spill(eax);
ager@chromium.org381abbb2009-02-25 13:23:22 +00004925 LoadGlobal();
ager@chromium.org41826e72009-03-30 13:30:57 +00004926 frame_->Push(slot->var()->name());
4927 RelocInfo::Mode mode = (typeof_state == INSIDE_TYPEOF)
4928 ? RelocInfo::CODE_TARGET
4929 : RelocInfo::CODE_TARGET_CONTEXT;
4930 Result answer = frame_->CallLoadIC(mode);
ager@chromium.org5ec48922009-05-05 07:25:34 +00004931 // A test eax instruction following the call signals that the inobject
4932 // property case was inlined. Ensure that there is not a test eax
4933 // instruction here.
4934 __ nop();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004935 return answer;
4936}
4937
4938
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00004939void CodeGenerator::EmitDynamicLoadFromSlotFastCase(Slot* slot,
4940 TypeofState typeof_state,
4941 Result* result,
4942 JumpTarget* slow,
4943 JumpTarget* done) {
4944 // Generate fast-case code for variables that might be shadowed by
4945 // eval-introduced variables. Eval is used a lot without
4946 // introducing variables. In those cases, we do not want to
4947 // perform a runtime call for all variables in the scope
4948 // containing the eval.
4949 if (slot->var()->mode() == Variable::DYNAMIC_GLOBAL) {
4950 *result = LoadFromGlobalSlotCheckExtensions(slot, typeof_state, slow);
4951 done->Jump(result);
4952
4953 } else if (slot->var()->mode() == Variable::DYNAMIC_LOCAL) {
4954 Slot* potential_slot = slot->var()->local_if_not_shadowed()->slot();
4955 Expression* rewrite = slot->var()->local_if_not_shadowed()->rewrite();
4956 if (potential_slot != NULL) {
4957 // Generate fast case for locals that rewrite to slots.
4958 // Allocate a fresh register to use as a temp in
4959 // ContextSlotOperandCheckExtensions and to hold the result
4960 // value.
4961 *result = allocator()->Allocate();
4962 ASSERT(result->is_valid());
4963 __ mov(result->reg(),
4964 ContextSlotOperandCheckExtensions(potential_slot, *result, slow));
4965 if (potential_slot->var()->mode() == Variable::CONST) {
4966 __ cmp(result->reg(), Factory::the_hole_value());
4967 done->Branch(not_equal, result);
4968 __ mov(result->reg(), Factory::undefined_value());
4969 }
4970 done->Jump(result);
4971 } else if (rewrite != NULL) {
4972 // Generate fast case for calls of an argument function.
4973 Property* property = rewrite->AsProperty();
4974 if (property != NULL) {
4975 VariableProxy* obj_proxy = property->obj()->AsVariableProxy();
4976 Literal* key_literal = property->key()->AsLiteral();
4977 if (obj_proxy != NULL &&
4978 key_literal != NULL &&
4979 obj_proxy->IsArguments() &&
4980 key_literal->handle()->IsSmi()) {
4981 // Load arguments object if there are no eval-introduced
4982 // variables. Then load the argument from the arguments
4983 // object using keyed load.
4984 Result arguments = allocator()->Allocate();
4985 ASSERT(arguments.is_valid());
4986 __ mov(arguments.reg(),
4987 ContextSlotOperandCheckExtensions(obj_proxy->var()->slot(),
4988 arguments,
4989 slow));
4990 frame_->Push(&arguments);
4991 frame_->Push(key_literal->handle());
4992 *result = EmitKeyedLoad();
4993 done->Jump(result);
4994 }
4995 }
4996 }
4997 }
4998}
4999
5000
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005001void CodeGenerator::StoreToSlot(Slot* slot, InitState init_state) {
5002 if (slot->type() == Slot::LOOKUP) {
5003 ASSERT(slot->var()->is_dynamic());
5004
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00005005 // For now, just do a runtime call. Since the call is inevitable,
5006 // we eagerly sync the virtual frame so we can directly push the
5007 // arguments into place.
5008 frame_->SyncRange(0, frame_->element_count() - 1);
5009
5010 frame_->EmitPush(esi);
5011 frame_->EmitPush(Immediate(slot->var()->name()));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005012
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00005013 Result value;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005014 if (init_state == CONST_INIT) {
5015 // Same as the case for a normal store, but ignores attribute
5016 // (e.g. READ_ONLY) of context slot so that we can initialize const
5017 // properties (introduced via eval("const foo = (some expr);")). Also,
5018 // uses the current function context instead of the top context.
5019 //
5020 // Note that we must declare the foo upon entry of eval(), via a
5021 // context slot declaration, but we cannot initialize it at the same
5022 // time, because the const declaration may be at the end of the eval
5023 // code (sigh...) and the const variable may have been used before
5024 // (where its value is 'undefined'). Thus, we can only do the
5025 // initialization when we actually encounter the expression and when
5026 // the expression operands are defined and valid, and thus we need the
5027 // split into 2 operations: declaration of the context slot followed
5028 // by initialization.
5029 value = frame_->CallRuntime(Runtime::kInitializeConstContextSlot, 3);
5030 } else {
5031 value = frame_->CallRuntime(Runtime::kStoreContextSlot, 3);
5032 }
5033 // Storing a variable must keep the (new) value on the expression
5034 // stack. This is necessary for compiling chained assignment
5035 // expressions.
5036 frame_->Push(&value);
5037
5038 } else {
5039 ASSERT(!slot->var()->is_dynamic());
5040
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005041 JumpTarget exit;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005042 if (init_state == CONST_INIT) {
5043 ASSERT(slot->var()->mode() == Variable::CONST);
5044 // Only the first const initialization must be executed (the slot
5045 // still contains 'the hole' value). When the assignment is executed,
5046 // the code is identical to a normal store (see below).
5047 //
5048 // We spill the frame in the code below because the direct-frame
5049 // access of SlotOperand is potentially unsafe with an unspilled
5050 // frame.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005051 VirtualFrame::SpilledScope spilled_scope;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005052 Comment cmnt(masm_, "[ Init const");
5053 __ mov(ecx, SlotOperand(slot, ecx));
5054 __ cmp(ecx, Factory::the_hole_value());
5055 exit.Branch(not_equal);
5056 }
5057
5058 // We must execute the store. Storing a variable must keep the (new)
5059 // value on the stack. This is necessary for compiling assignment
5060 // expressions.
5061 //
5062 // Note: We will reach here even with slot->var()->mode() ==
5063 // Variable::CONST because of const declarations which will initialize
5064 // consts to 'the hole' value and by doing so, end up calling this code.
5065 if (slot->type() == Slot::PARAMETER) {
5066 frame_->StoreToParameterAt(slot->index());
5067 } else if (slot->type() == Slot::LOCAL) {
5068 frame_->StoreToLocalAt(slot->index());
5069 } else {
5070 // The other slot types (LOOKUP and GLOBAL) cannot reach here.
5071 //
5072 // The use of SlotOperand below is safe for an unspilled frame
5073 // because the slot is a context slot.
5074 ASSERT(slot->type() == Slot::CONTEXT);
5075 frame_->Dup();
5076 Result value = frame_->Pop();
5077 value.ToRegister();
5078 Result start = allocator_->Allocate();
5079 ASSERT(start.is_valid());
5080 __ mov(SlotOperand(slot, start.reg()), value.reg());
5081 // RecordWrite may destroy the value registers.
5082 //
5083 // TODO(204): Avoid actually spilling when the value is not
5084 // needed (probably the common case).
5085 frame_->Spill(value.reg());
5086 int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize;
5087 Result temp = allocator_->Allocate();
5088 ASSERT(temp.is_valid());
5089 __ RecordWrite(start.reg(), offset, value.reg(), temp.reg());
5090 // The results start, value, and temp are unused by going out of
5091 // scope.
5092 }
5093
5094 exit.Bind();
5095 }
ager@chromium.org381abbb2009-02-25 13:23:22 +00005096}
5097
5098
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005099void CodeGenerator::VisitSlot(Slot* slot) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00005100 Comment cmnt(masm_, "[ Slot");
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005101 if (in_safe_int32_mode()) {
5102 if ((slot->type() == Slot::LOCAL && !slot->is_arguments())) {
5103 frame()->UntaggedPushLocalAt(slot->index());
5104 } else if (slot->type() == Slot::PARAMETER) {
5105 frame()->UntaggedPushParameterAt(slot->index());
5106 } else {
5107 UNREACHABLE();
5108 }
5109 } else {
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00005110 LoadFromSlotCheckForArguments(slot, NOT_INSIDE_TYPEOF);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005111 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00005112}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005113
ager@chromium.org7c537e22008-10-16 08:43:32 +00005114
5115void CodeGenerator::VisitVariableProxy(VariableProxy* node) {
5116 Comment cmnt(masm_, "[ VariableProxy");
5117 Variable* var = node->var();
5118 Expression* expr = var->rewrite();
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00005119 if (expr != NULL) {
5120 Visit(expr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005121 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +00005122 ASSERT(var->is_global());
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005123 ASSERT(!in_safe_int32_mode());
ager@chromium.org7c537e22008-10-16 08:43:32 +00005124 Reference ref(this, node);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005125 ref.GetValue();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005126 }
5127}
5128
5129
ager@chromium.org7c537e22008-10-16 08:43:32 +00005130void CodeGenerator::VisitLiteral(Literal* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005131 Comment cmnt(masm_, "[ Literal");
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005132 if (in_safe_int32_mode()) {
5133 frame_->PushUntaggedElement(node->handle());
5134 } else {
5135 frame_->Push(node->handle());
5136 }
kasperl@chromium.org8ccb0be2009-04-07 07:21:39 +00005137}
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005138
5139
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005140void CodeGenerator::PushUnsafeSmi(Handle<Object> value) {
5141 ASSERT(value->IsSmi());
5142 int bits = reinterpret_cast<int>(*value);
5143 __ push(Immediate(bits & 0x0000FFFF));
5144 __ or_(Operand(esp, 0), Immediate(bits & 0xFFFF0000));
5145}
5146
5147
5148void CodeGenerator::StoreUnsafeSmiToLocal(int offset, Handle<Object> value) {
5149 ASSERT(value->IsSmi());
5150 int bits = reinterpret_cast<int>(*value);
5151 __ mov(Operand(ebp, offset), Immediate(bits & 0x0000FFFF));
5152 __ or_(Operand(ebp, offset), Immediate(bits & 0xFFFF0000));
5153}
5154
5155
5156void CodeGenerator::MoveUnsafeSmi(Register target, Handle<Object> value) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005157 ASSERT(target.is_valid());
5158 ASSERT(value->IsSmi());
5159 int bits = reinterpret_cast<int>(*value);
5160 __ Set(target, Immediate(bits & 0x0000FFFF));
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005161 __ or_(target, bits & 0xFFFF0000);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005162}
5163
5164
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005165bool CodeGenerator::IsUnsafeSmi(Handle<Object> value) {
5166 if (!value->IsSmi()) return false;
5167 int int_value = Smi::cast(*value)->value();
5168 return !is_intn(int_value, kMaxSmiInlinedBits);
5169}
5170
5171
ager@chromium.orge2902be2009-06-08 12:21:35 +00005172// Materialize the regexp literal 'node' in the literals array
5173// 'literals' of the function. Leave the regexp boilerplate in
5174// 'boilerplate'.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005175class DeferredRegExpLiteral: public DeferredCode {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005176 public:
ager@chromium.orge2902be2009-06-08 12:21:35 +00005177 DeferredRegExpLiteral(Register boilerplate,
5178 Register literals,
5179 RegExpLiteral* node)
5180 : boilerplate_(boilerplate), literals_(literals), node_(node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005181 set_comment("[ DeferredRegExpLiteral");
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005182 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005183
ager@chromium.orge2902be2009-06-08 12:21:35 +00005184 void Generate();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005185
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005186 private:
ager@chromium.orge2902be2009-06-08 12:21:35 +00005187 Register boilerplate_;
5188 Register literals_;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005189 RegExpLiteral* node_;
5190};
5191
5192
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005193void DeferredRegExpLiteral::Generate() {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005194 // Since the entry is undefined we call the runtime system to
5195 // compute the literal.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005196 // Literal array (0).
ager@chromium.orge2902be2009-06-08 12:21:35 +00005197 __ push(literals_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005198 // Literal index (1).
ager@chromium.orge2902be2009-06-08 12:21:35 +00005199 __ push(Immediate(Smi::FromInt(node_->literal_index())));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005200 // RegExp pattern (2).
ager@chromium.orge2902be2009-06-08 12:21:35 +00005201 __ push(Immediate(node_->pattern()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005202 // RegExp flags (3).
ager@chromium.orge2902be2009-06-08 12:21:35 +00005203 __ push(Immediate(node_->flags()));
5204 __ CallRuntime(Runtime::kMaterializeRegExpLiteral, 4);
5205 if (!boilerplate_.is(eax)) __ mov(boilerplate_, eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005206}
5207
5208
ager@chromium.org7c537e22008-10-16 08:43:32 +00005209void CodeGenerator::VisitRegExpLiteral(RegExpLiteral* node) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005210 ASSERT(!in_safe_int32_mode());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005211 Comment cmnt(masm_, "[ RegExp Literal");
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005212
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005213 // Retrieve the literals array and check the allocated entry. Begin
5214 // with a writable copy of the function of this activation in a
5215 // register.
5216 frame_->PushFunction();
5217 Result literals = frame_->Pop();
5218 literals.ToRegister();
5219 frame_->Spill(literals.reg());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005220
5221 // Load the literals array of the function.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005222 __ mov(literals.reg(),
5223 FieldOperand(literals.reg(), JSFunction::kLiteralsOffset));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005224
5225 // Load the literal at the ast saved index.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005226 Result boilerplate = allocator_->Allocate();
5227 ASSERT(boilerplate.is_valid());
ager@chromium.orge2902be2009-06-08 12:21:35 +00005228 int literal_offset =
5229 FixedArray::kHeaderSize + node->literal_index() * kPointerSize;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005230 __ mov(boilerplate.reg(), FieldOperand(literals.reg(), literal_offset));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005231
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005232 // Check whether we need to materialize the RegExp object. If so,
5233 // jump to the deferred code passing the literals array.
ager@chromium.orge2902be2009-06-08 12:21:35 +00005234 DeferredRegExpLiteral* deferred =
5235 new DeferredRegExpLiteral(boilerplate.reg(), literals.reg(), node);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005236 __ cmp(boilerplate.reg(), Factory::undefined_value());
ager@chromium.orge2902be2009-06-08 12:21:35 +00005237 deferred->Branch(equal);
5238 deferred->BindExit();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005239 literals.Unuse();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005240
5241 // Push the boilerplate object.
5242 frame_->Push(&boilerplate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005243}
5244
5245
ager@chromium.org7c537e22008-10-16 08:43:32 +00005246void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005247 ASSERT(!in_safe_int32_mode());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005248 Comment cmnt(masm_, "[ ObjectLiteral");
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005249
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005250 // Load a writable copy of the function of this activation in a
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005251 // register.
5252 frame_->PushFunction();
5253 Result literals = frame_->Pop();
5254 literals.ToRegister();
5255 frame_->Spill(literals.reg());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005256
5257 // Load the literals array of the function.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005258 __ mov(literals.reg(),
5259 FieldOperand(literals.reg(), JSFunction::kLiteralsOffset));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005260 // Literal array.
5261 frame_->Push(&literals);
5262 // Literal index.
5263 frame_->Push(Smi::FromInt(node->literal_index()));
5264 // Constant properties.
5265 frame_->Push(node->constant_properties());
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00005266 // Should the object literal have fast elements?
5267 frame_->Push(Smi::FromInt(node->fast_elements() ? 1 : 0));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005268 Result clone;
5269 if (node->depth() > 1) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00005270 clone = frame_->CallRuntime(Runtime::kCreateObjectLiteral, 4);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005271 } else {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00005272 clone = frame_->CallRuntime(Runtime::kCreateObjectLiteralShallow, 4);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005273 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005274 frame_->Push(&clone);
ager@chromium.org236ad962008-09-25 09:45:57 +00005275
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005276 for (int i = 0; i < node->properties()->length(); i++) {
ager@chromium.org32912102009-01-16 10:38:43 +00005277 ObjectLiteral::Property* property = node->properties()->at(i);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005278 switch (property->kind()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005279 case ObjectLiteral::Property::CONSTANT:
5280 break;
5281 case ObjectLiteral::Property::MATERIALIZED_LITERAL:
5282 if (CompileTimeValue::IsCompileTimeValue(property->value())) break;
5283 // else fall through.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005284 case ObjectLiteral::Property::COMPUTED: {
5285 Handle<Object> key(property->key()->handle());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005286 if (key->IsSymbol()) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005287 // Duplicate the object as the IC receiver.
5288 frame_->Dup();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005289 Load(property->value());
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00005290 Result dummy = frame_->CallStoreIC(Handle<String>::cast(key), false);
5291 dummy.Unuse();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005292 break;
5293 }
5294 // Fall through
5295 }
5296 case ObjectLiteral::Property::PROTOTYPE: {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005297 // Duplicate the object as an argument to the runtime call.
5298 frame_->Dup();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005299 Load(property->key());
5300 Load(property->value());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005301 Result ignored = frame_->CallRuntime(Runtime::kSetProperty, 3);
5302 // Ignore the result.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005303 break;
5304 }
5305 case ObjectLiteral::Property::SETTER: {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005306 // Duplicate the object as an argument to the runtime call.
5307 frame_->Dup();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005308 Load(property->key());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005309 frame_->Push(Smi::FromInt(1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005310 Load(property->value());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005311 Result ignored = frame_->CallRuntime(Runtime::kDefineAccessor, 4);
5312 // Ignore the result.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005313 break;
5314 }
5315 case ObjectLiteral::Property::GETTER: {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005316 // Duplicate the object as an argument to the runtime call.
5317 frame_->Dup();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005318 Load(property->key());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005319 frame_->Push(Smi::FromInt(0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005320 Load(property->value());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005321 Result ignored = frame_->CallRuntime(Runtime::kDefineAccessor, 4);
5322 // Ignore the result.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005323 break;
5324 }
5325 default: UNREACHABLE();
5326 }
5327 }
5328}
5329
5330
ager@chromium.org7c537e22008-10-16 08:43:32 +00005331void CodeGenerator::VisitArrayLiteral(ArrayLiteral* node) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005332 ASSERT(!in_safe_int32_mode());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005333 Comment cmnt(masm_, "[ ArrayLiteral");
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005334
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005335 // Load a writable copy of the function of this activation in a
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005336 // register.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005337 frame_->PushFunction();
5338 Result literals = frame_->Pop();
5339 literals.ToRegister();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005340 frame_->Spill(literals.reg());
5341
5342 // Load the literals array of the function.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005343 __ mov(literals.reg(),
5344 FieldOperand(literals.reg(), JSFunction::kLiteralsOffset));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005345
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005346 frame_->Push(&literals);
5347 frame_->Push(Smi::FromInt(node->literal_index()));
5348 frame_->Push(node->constant_elements());
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00005349 int length = node->values()->length();
5350 Result clone;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005351 if (node->depth() > 1) {
5352 clone = frame_->CallRuntime(Runtime::kCreateArrayLiteral, 3);
5353 } else if (length > FastCloneShallowArrayStub::kMaximumLength) {
5354 clone = frame_->CallRuntime(Runtime::kCreateArrayLiteralShallow, 3);
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00005355 } else {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005356 FastCloneShallowArrayStub stub(length);
5357 clone = frame_->CallStub(&stub, 3);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005358 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005359 frame_->Push(&clone);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005360
5361 // Generate code to set the elements in the array that are not
5362 // literals.
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00005363 for (int i = 0; i < length; i++) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005364 Expression* value = node->values()->at(i);
5365
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005366 // If value is a literal the property value is already set in the
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005367 // boilerplate object.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005368 if (value->AsLiteral() != NULL) continue;
5369 // If value is a materialized literal the property value is already set
5370 // in the boilerplate object if it is simple.
5371 if (CompileTimeValue::IsCompileTimeValue(value)) continue;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005372
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005373 // The property must be set by generated code.
5374 Load(value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005375
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005376 // Get the property value off the stack.
5377 Result prop_value = frame_->Pop();
5378 prop_value.ToRegister();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005379
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005380 // Fetch the array literal while leaving a copy on the stack and
5381 // use it to get the elements array.
5382 frame_->Dup();
5383 Result elements = frame_->Pop();
5384 elements.ToRegister();
5385 frame_->Spill(elements.reg());
5386 // Get the elements array.
5387 __ mov(elements.reg(),
5388 FieldOperand(elements.reg(), JSObject::kElementsOffset));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005389
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005390 // Write to the indexed properties array.
kasperl@chromium.orge959c182009-07-27 08:59:04 +00005391 int offset = i * kPointerSize + FixedArray::kHeaderSize;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005392 __ mov(FieldOperand(elements.reg(), offset), prop_value.reg());
5393
5394 // Update the write barrier for the array address.
5395 frame_->Spill(prop_value.reg()); // Overwritten by the write barrier.
5396 Result scratch = allocator_->Allocate();
5397 ASSERT(scratch.is_valid());
5398 __ RecordWrite(elements.reg(), offset, prop_value.reg(), scratch.reg());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005399 }
5400}
5401
5402
ager@chromium.org32912102009-01-16 10:38:43 +00005403void CodeGenerator::VisitCatchExtensionObject(CatchExtensionObject* node) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005404 ASSERT(!in_safe_int32_mode());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005405 ASSERT(!in_spilled_code());
ager@chromium.org32912102009-01-16 10:38:43 +00005406 // Call runtime routine to allocate the catch extension object and
5407 // assign the exception value to the catch variable.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005408 Comment cmnt(masm_, "[ CatchExtensionObject");
ager@chromium.org32912102009-01-16 10:38:43 +00005409 Load(node->key());
5410 Load(node->value());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005411 Result result =
5412 frame_->CallRuntime(Runtime::kCreateCatchExtensionObject, 2);
5413 frame_->Push(&result);
ager@chromium.org32912102009-01-16 10:38:43 +00005414}
5415
5416
ager@chromium.org5c838252010-02-19 08:53:10 +00005417void CodeGenerator::EmitSlotAssignment(Assignment* node) {
5418#ifdef DEBUG
5419 int original_height = frame()->height();
5420#endif
5421 Comment cmnt(masm(), "[ Variable Assignment");
5422 Variable* var = node->target()->AsVariableProxy()->AsVariable();
5423 ASSERT(var != NULL);
5424 Slot* slot = var->slot();
5425 ASSERT(slot != NULL);
5426
5427 // Evaluate the right-hand side.
5428 if (node->is_compound()) {
ager@chromium.orgac091b72010-05-05 07:34:42 +00005429 // For a compound assignment the right-hand side is a binary operation
5430 // between the current property value and the actual right-hand side.
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00005431 LoadFromSlotCheckForArguments(slot, NOT_INSIDE_TYPEOF);
ager@chromium.org5c838252010-02-19 08:53:10 +00005432 Load(node->value());
5433
ager@chromium.orgac091b72010-05-05 07:34:42 +00005434 // Perform the binary operation.
ager@chromium.org5c838252010-02-19 08:53:10 +00005435 bool overwrite_value =
5436 (node->value()->AsBinaryOperation() != NULL &&
5437 node->value()->AsBinaryOperation()->ResultOverwriteAllowed());
lrn@chromium.org25156de2010-04-06 13:10:27 +00005438 // Construct the implicit binary operation.
5439 BinaryOperation expr(node, node->binary_op(), node->target(),
5440 node->value());
5441 GenericBinaryOperation(&expr,
5442 overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE);
ager@chromium.org5c838252010-02-19 08:53:10 +00005443 } else {
ager@chromium.orgac091b72010-05-05 07:34:42 +00005444 // For non-compound assignment just load the right-hand side.
ager@chromium.org5c838252010-02-19 08:53:10 +00005445 Load(node->value());
5446 }
5447
5448 // Perform the assignment.
5449 if (var->mode() != Variable::CONST || node->op() == Token::INIT_CONST) {
5450 CodeForSourcePosition(node->position());
5451 StoreToSlot(slot,
5452 node->op() == Token::INIT_CONST ? CONST_INIT : NOT_CONST_INIT);
5453 }
5454 ASSERT(frame()->height() == original_height + 1);
5455}
5456
5457
5458void CodeGenerator::EmitNamedPropertyAssignment(Assignment* node) {
5459#ifdef DEBUG
5460 int original_height = frame()->height();
5461#endif
5462 Comment cmnt(masm(), "[ Named Property Assignment");
5463 Variable* var = node->target()->AsVariableProxy()->AsVariable();
5464 Property* prop = node->target()->AsProperty();
5465 ASSERT(var == NULL || (prop == NULL && var->is_global()));
5466
ager@chromium.orgac091b72010-05-05 07:34:42 +00005467 // Initialize name and evaluate the receiver sub-expression if necessary. If
5468 // the receiver is trivial it is not placed on the stack at this point, but
5469 // loaded whenever actually needed.
ager@chromium.org5c838252010-02-19 08:53:10 +00005470 Handle<String> name;
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00005471 bool is_trivial_receiver = false;
ager@chromium.org5c838252010-02-19 08:53:10 +00005472 if (var != NULL) {
5473 name = var->name();
ager@chromium.org5c838252010-02-19 08:53:10 +00005474 } else {
5475 Literal* lit = prop->key()->AsLiteral();
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00005476 ASSERT_NOT_NULL(lit);
ager@chromium.org5c838252010-02-19 08:53:10 +00005477 name = Handle<String>::cast(lit->handle());
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00005478 // Do not materialize the receiver on the frame if it is trivial.
5479 is_trivial_receiver = prop->obj()->IsTrivial();
5480 if (!is_trivial_receiver) Load(prop->obj());
ager@chromium.org5c838252010-02-19 08:53:10 +00005481 }
5482
ager@chromium.orgac091b72010-05-05 07:34:42 +00005483 // Change to slow case in the beginning of an initialization block to
5484 // avoid the quadratic behavior of repeatedly adding fast properties.
ager@chromium.org5c838252010-02-19 08:53:10 +00005485 if (node->starts_initialization_block()) {
ager@chromium.orgac091b72010-05-05 07:34:42 +00005486 // Initialization block consists of assignments of the form expr.x = ..., so
5487 // this will never be an assignment to a variable, so there must be a
5488 // receiver object.
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00005489 ASSERT_EQ(NULL, var);
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00005490 if (is_trivial_receiver) {
5491 frame()->Push(prop->obj());
5492 } else {
5493 frame()->Dup();
5494 }
ager@chromium.org5c838252010-02-19 08:53:10 +00005495 Result ignored = frame()->CallRuntime(Runtime::kToSlowProperties, 1);
5496 }
5497
ager@chromium.orgac091b72010-05-05 07:34:42 +00005498 // Change to fast case at the end of an initialization block. To prepare for
5499 // that add an extra copy of the receiver to the frame, so that it can be
5500 // converted back to fast case after the assignment.
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00005501 if (node->ends_initialization_block() && !is_trivial_receiver) {
ager@chromium.org5c838252010-02-19 08:53:10 +00005502 frame()->Dup();
5503 }
5504
ager@chromium.orgac091b72010-05-05 07:34:42 +00005505 // Stack layout:
5506 // [tos] : receiver (only materialized if non-trivial)
5507 // [tos+1] : receiver if at the end of an initialization block
5508
ager@chromium.org5c838252010-02-19 08:53:10 +00005509 // Evaluate the right-hand side.
5510 if (node->is_compound()) {
ager@chromium.orgac091b72010-05-05 07:34:42 +00005511 // For a compound assignment the right-hand side is a binary operation
5512 // between the current property value and the actual right-hand side.
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00005513 if (is_trivial_receiver) {
5514 frame()->Push(prop->obj());
5515 } else if (var != NULL) {
5516 // The LoadIC stub expects the object in eax.
5517 // Freeing eax causes the code generator to load the global into it.
5518 frame_->Spill(eax);
5519 LoadGlobal();
5520 } else {
5521 frame()->Dup();
5522 }
ager@chromium.org5c838252010-02-19 08:53:10 +00005523 Result value = EmitNamedLoad(name, var != NULL);
5524 frame()->Push(&value);
5525 Load(node->value());
5526
5527 bool overwrite_value =
5528 (node->value()->AsBinaryOperation() != NULL &&
5529 node->value()->AsBinaryOperation()->ResultOverwriteAllowed());
lrn@chromium.org25156de2010-04-06 13:10:27 +00005530 // Construct the implicit binary operation.
5531 BinaryOperation expr(node, node->binary_op(), node->target(),
5532 node->value());
5533 GenericBinaryOperation(&expr,
5534 overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE);
ager@chromium.org5c838252010-02-19 08:53:10 +00005535 } else {
ager@chromium.orgac091b72010-05-05 07:34:42 +00005536 // For non-compound assignment just load the right-hand side.
ager@chromium.org5c838252010-02-19 08:53:10 +00005537 Load(node->value());
5538 }
5539
ager@chromium.orgac091b72010-05-05 07:34:42 +00005540 // Stack layout:
5541 // [tos] : value
5542 // [tos+1] : receiver (only materialized if non-trivial)
5543 // [tos+2] : receiver if at the end of an initialization block
5544
ager@chromium.org5c838252010-02-19 08:53:10 +00005545 // Perform the assignment. It is safe to ignore constants here.
5546 ASSERT(var == NULL || var->mode() != Variable::CONST);
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00005547 ASSERT_NE(Token::INIT_CONST, node->op());
5548 if (is_trivial_receiver) {
5549 Result value = frame()->Pop();
5550 frame()->Push(prop->obj());
5551 frame()->Push(&value);
5552 }
ager@chromium.org5c838252010-02-19 08:53:10 +00005553 CodeForSourcePosition(node->position());
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00005554 bool is_contextual = (var != NULL);
5555 Result answer = EmitNamedStore(name, is_contextual);
ager@chromium.org5c838252010-02-19 08:53:10 +00005556 frame()->Push(&answer);
5557
ager@chromium.orgac091b72010-05-05 07:34:42 +00005558 // Stack layout:
5559 // [tos] : result
5560 // [tos+1] : receiver if at the end of an initialization block
5561
ager@chromium.org5c838252010-02-19 08:53:10 +00005562 if (node->ends_initialization_block()) {
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00005563 ASSERT_EQ(NULL, var);
5564 // The argument to the runtime call is the receiver.
5565 if (is_trivial_receiver) {
5566 frame()->Push(prop->obj());
5567 } else {
5568 // A copy of the receiver is below the value of the assignment. Swap
5569 // the receiver and the value of the assignment expression.
5570 Result result = frame()->Pop();
5571 Result receiver = frame()->Pop();
5572 frame()->Push(&result);
5573 frame()->Push(&receiver);
5574 }
ager@chromium.org5c838252010-02-19 08:53:10 +00005575 Result ignored = frame_->CallRuntime(Runtime::kToFastProperties, 1);
5576 }
5577
ager@chromium.orgac091b72010-05-05 07:34:42 +00005578 // Stack layout:
5579 // [tos] : result
5580
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00005581 ASSERT_EQ(frame()->height(), original_height + 1);
ager@chromium.org5c838252010-02-19 08:53:10 +00005582}
5583
5584
5585void CodeGenerator::EmitKeyedPropertyAssignment(Assignment* node) {
5586#ifdef DEBUG
5587 int original_height = frame()->height();
5588#endif
ager@chromium.orgac091b72010-05-05 07:34:42 +00005589 Comment cmnt(masm_, "[ Keyed Property Assignment");
ager@chromium.org5c838252010-02-19 08:53:10 +00005590 Property* prop = node->target()->AsProperty();
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00005591 ASSERT_NOT_NULL(prop);
ager@chromium.org5c838252010-02-19 08:53:10 +00005592
5593 // Evaluate the receiver subexpression.
5594 Load(prop->obj());
5595
ager@chromium.orgac091b72010-05-05 07:34:42 +00005596 // Change to slow case in the beginning of an initialization block to
5597 // avoid the quadratic behavior of repeatedly adding fast properties.
ager@chromium.org5c838252010-02-19 08:53:10 +00005598 if (node->starts_initialization_block()) {
ager@chromium.org5c838252010-02-19 08:53:10 +00005599 frame_->Dup();
5600 Result ignored = frame_->CallRuntime(Runtime::kToSlowProperties, 1);
5601 }
5602
ager@chromium.orgac091b72010-05-05 07:34:42 +00005603 // Change to fast case at the end of an initialization block. To prepare for
5604 // that add an extra copy of the receiver to the frame, so that it can be
5605 // converted back to fast case after the assignment.
ager@chromium.org5c838252010-02-19 08:53:10 +00005606 if (node->ends_initialization_block()) {
ager@chromium.org5c838252010-02-19 08:53:10 +00005607 frame_->Dup();
5608 }
5609
5610 // Evaluate the key subexpression.
5611 Load(prop->key());
5612
ager@chromium.orgac091b72010-05-05 07:34:42 +00005613 // Stack layout:
5614 // [tos] : key
5615 // [tos+1] : receiver
5616 // [tos+2] : receiver if at the end of an initialization block
5617
ager@chromium.org5c838252010-02-19 08:53:10 +00005618 // Evaluate the right-hand side.
5619 if (node->is_compound()) {
ager@chromium.orgac091b72010-05-05 07:34:42 +00005620 // For a compound assignment the right-hand side is a binary operation
5621 // between the current property value and the actual right-hand side.
5622 // Duplicate receiver and key for loading the current property value.
ager@chromium.org5c838252010-02-19 08:53:10 +00005623 frame()->PushElementAt(1);
5624 frame()->PushElementAt(1);
5625 Result value = EmitKeyedLoad();
5626 frame()->Push(&value);
5627 Load(node->value());
5628
ager@chromium.orgac091b72010-05-05 07:34:42 +00005629 // Perform the binary operation.
ager@chromium.org5c838252010-02-19 08:53:10 +00005630 bool overwrite_value =
5631 (node->value()->AsBinaryOperation() != NULL &&
5632 node->value()->AsBinaryOperation()->ResultOverwriteAllowed());
lrn@chromium.org25156de2010-04-06 13:10:27 +00005633 BinaryOperation expr(node, node->binary_op(), node->target(),
5634 node->value());
5635 GenericBinaryOperation(&expr,
5636 overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE);
ager@chromium.org5c838252010-02-19 08:53:10 +00005637 } else {
ager@chromium.orgac091b72010-05-05 07:34:42 +00005638 // For non-compound assignment just load the right-hand side.
ager@chromium.org5c838252010-02-19 08:53:10 +00005639 Load(node->value());
5640 }
5641
ager@chromium.orgac091b72010-05-05 07:34:42 +00005642 // Stack layout:
5643 // [tos] : value
5644 // [tos+1] : key
5645 // [tos+2] : receiver
5646 // [tos+3] : receiver if at the end of an initialization block
5647
ager@chromium.org5c838252010-02-19 08:53:10 +00005648 // Perform the assignment. It is safe to ignore constants here.
5649 ASSERT(node->op() != Token::INIT_CONST);
5650 CodeForSourcePosition(node->position());
5651 Result answer = EmitKeyedStore(prop->key()->type());
5652 frame()->Push(&answer);
5653
ager@chromium.orgac091b72010-05-05 07:34:42 +00005654 // Stack layout:
5655 // [tos] : result
5656 // [tos+1] : receiver if at the end of an initialization block
5657
5658 // Change to fast case at the end of an initialization block.
ager@chromium.org5c838252010-02-19 08:53:10 +00005659 if (node->ends_initialization_block()) {
5660 // The argument to the runtime call is the extra copy of the receiver,
5661 // which is below the value of the assignment. Swap the receiver and
5662 // the value of the assignment expression.
5663 Result result = frame()->Pop();
5664 Result receiver = frame()->Pop();
5665 frame()->Push(&result);
5666 frame()->Push(&receiver);
5667 Result ignored = frame_->CallRuntime(Runtime::kToFastProperties, 1);
5668 }
5669
ager@chromium.orgac091b72010-05-05 07:34:42 +00005670 // Stack layout:
5671 // [tos] : result
5672
ager@chromium.org5c838252010-02-19 08:53:10 +00005673 ASSERT(frame()->height() == original_height + 1);
5674}
5675
5676
ager@chromium.org7c537e22008-10-16 08:43:32 +00005677void CodeGenerator::VisitAssignment(Assignment* node) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005678 ASSERT(!in_safe_int32_mode());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005679#ifdef DEBUG
ager@chromium.org5c838252010-02-19 08:53:10 +00005680 int original_height = frame()->height();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005681#endif
ager@chromium.org5c838252010-02-19 08:53:10 +00005682 Variable* var = node->target()->AsVariableProxy()->AsVariable();
5683 Property* prop = node->target()->AsProperty();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005684
ager@chromium.org5c838252010-02-19 08:53:10 +00005685 if (var != NULL && !var->is_global()) {
5686 EmitSlotAssignment(node);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005687
ager@chromium.org5c838252010-02-19 08:53:10 +00005688 } else if ((prop != NULL && prop->key()->IsPropertyName()) ||
5689 (var != NULL && var->is_global())) {
5690 // Properties whose keys are property names and global variables are
5691 // treated as named property references. We do not need to consider
5692 // global 'this' because it is not a valid left-hand side.
5693 EmitNamedPropertyAssignment(node);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005694
ager@chromium.org5c838252010-02-19 08:53:10 +00005695 } else if (prop != NULL) {
5696 // Other properties (including rewritten parameters for a function that
5697 // uses arguments) are keyed property assignments.
5698 EmitKeyedPropertyAssignment(node);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005699
ager@chromium.org5c838252010-02-19 08:53:10 +00005700 } else {
5701 // Invalid left-hand side.
5702 Load(node->target());
5703 Result result = frame()->CallRuntime(Runtime::kThrowReferenceError, 1);
5704 // The runtime call doesn't actually return but the code generator will
5705 // still generate code and expects a certain frame height.
5706 frame()->Push(&result);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005707 }
ager@chromium.org5c838252010-02-19 08:53:10 +00005708
5709 ASSERT(frame()->height() == original_height + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005710}
5711
5712
ager@chromium.org7c537e22008-10-16 08:43:32 +00005713void CodeGenerator::VisitThrow(Throw* node) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005714 ASSERT(!in_safe_int32_mode());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005715 Comment cmnt(masm_, "[ Throw");
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005716 Load(node->exception());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005717 Result result = frame_->CallRuntime(Runtime::kThrow, 1);
5718 frame_->Push(&result);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005719}
5720
5721
ager@chromium.org7c537e22008-10-16 08:43:32 +00005722void CodeGenerator::VisitProperty(Property* node) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005723 ASSERT(!in_safe_int32_mode());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005724 Comment cmnt(masm_, "[ Property");
ager@chromium.org7c537e22008-10-16 08:43:32 +00005725 Reference property(this, node);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005726 property.GetValue();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005727}
5728
5729
ager@chromium.org7c537e22008-10-16 08:43:32 +00005730void CodeGenerator::VisitCall(Call* node) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005731 ASSERT(!in_safe_int32_mode());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005732 Comment cmnt(masm_, "[ Call");
5733
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00005734 Expression* function = node->expression();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005735 ZoneList<Expression*>* args = node->arguments();
5736
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005737 // Check if the function is a variable or a property.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005738 Variable* var = function->AsVariableProxy()->AsVariable();
5739 Property* property = function->AsProperty();
5740
5741 // ------------------------------------------------------------------------
5742 // Fast-case: Use inline caching.
5743 // ---
5744 // According to ECMA-262, section 11.2.3, page 44, the function to call
5745 // must be resolved after the arguments have been evaluated. The IC code
5746 // automatically handles this by loading the arguments before the function
5747 // is resolved in cache misses (this also holds for megamorphic calls).
5748 // ------------------------------------------------------------------------
5749
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00005750 if (var != NULL && var->is_possibly_eval()) {
5751 // ----------------------------------
5752 // JavaScript example: 'eval(arg)' // eval is not known to be shadowed
5753 // ----------------------------------
5754
5755 // In a call to eval, we first call %ResolvePossiblyDirectEval to
5756 // resolve the function we need to call and the receiver of the
5757 // call. Then we call the resolved function using the given
5758 // arguments.
5759
5760 // Prepare the stack for the call to the resolved function.
5761 Load(function);
5762
5763 // Allocate a frame slot for the receiver.
5764 frame_->Push(Factory::undefined_value());
5765 int arg_count = args->length();
5766 for (int i = 0; i < arg_count; i++) {
5767 Load(args->at(i));
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005768 frame_->SpillTop();
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00005769 }
5770
5771 // Prepare the stack for the call to ResolvePossiblyDirectEval.
5772 frame_->PushElementAt(arg_count + 1);
5773 if (arg_count > 0) {
5774 frame_->PushElementAt(arg_count);
5775 } else {
5776 frame_->Push(Factory::undefined_value());
5777 }
5778
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005779 // Push the receiver.
5780 frame_->PushParameterAt(-1);
5781
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00005782 // Resolve the call.
5783 Result result =
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005784 frame_->CallRuntime(Runtime::kResolvePossiblyDirectEval, 3);
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00005785
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005786 // The runtime call returns a pair of values in eax (function) and
5787 // edx (receiver). Touch up the stack with the right values.
5788 Result receiver = allocator_->Allocate(edx);
5789 frame_->SetElementAt(arg_count + 1, &result);
5790 frame_->SetElementAt(arg_count, &receiver);
5791 receiver.Unuse();
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00005792
5793 // Call the function.
5794 CodeForSourcePosition(node->position());
5795 InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005796 CallFunctionStub call_function(arg_count, in_loop, RECEIVER_MIGHT_BE_VALUE);
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00005797 result = frame_->CallStub(&call_function, arg_count + 1);
5798
5799 // Restore the context and overwrite the function on the stack with
5800 // the result.
5801 frame_->RestoreContextRegister();
5802 frame_->SetElementAt(0, &result);
5803
5804 } else if (var != NULL && !var->is_this() && var->is_global()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005805 // ----------------------------------
5806 // JavaScript example: 'foo(1, 2, 3)' // foo is global
5807 // ----------------------------------
5808
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005809 // Pass the global object as the receiver and let the IC stub
5810 // patch the stack to use the global proxy as 'this' in the
5811 // invoked function.
5812 LoadGlobal();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005813
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005814 // Load the arguments.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005815 int arg_count = args->length();
5816 for (int i = 0; i < arg_count; i++) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005817 Load(args->at(i));
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005818 frame_->SpillTop();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005819 }
5820
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00005821 // Push the name of the function onto the frame.
5822 frame_->Push(var->name());
5823
ager@chromium.org41826e72009-03-30 13:30:57 +00005824 // Call the IC initialization code.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00005825 CodeForSourcePosition(node->position());
ager@chromium.org41826e72009-03-30 13:30:57 +00005826 Result result = frame_->CallCallIC(RelocInfo::CODE_TARGET_CONTEXT,
5827 arg_count,
5828 loop_nesting());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005829 frame_->RestoreContextRegister();
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00005830 frame_->Push(&result);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005831
5832 } else if (var != NULL && var->slot() != NULL &&
5833 var->slot()->type() == Slot::LOOKUP) {
5834 // ----------------------------------
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00005835 // JavaScript examples:
5836 //
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005837 // with (obj) foo(1, 2, 3) // foo may be in obj.
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00005838 //
5839 // function f() {};
5840 // function g() {
5841 // eval(...);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005842 // f(); // f could be in extension object.
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00005843 // }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005844 // ----------------------------------
5845
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005846 JumpTarget slow, done;
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00005847 Result function;
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00005848
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005849 // Generate fast case for loading functions from slots that
5850 // correspond to local/global variables or arguments unless they
5851 // are shadowed by eval-introduced bindings.
5852 EmitDynamicLoadFromSlotFastCase(var->slot(),
5853 NOT_INSIDE_TYPEOF,
5854 &function,
5855 &slow,
5856 &done);
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00005857
5858 slow.Bind();
5859 // Enter the runtime system to load the function from the context.
5860 // Sync the frame so we can push the arguments directly into
5861 // place.
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00005862 frame_->SyncRange(0, frame_->element_count() - 1);
5863 frame_->EmitPush(esi);
5864 frame_->EmitPush(Immediate(var->name()));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005865 frame_->CallRuntime(Runtime::kLoadContextSlot, 2);
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00005866 // The runtime call returns a pair of values in eax and edx. The
5867 // looked-up function is in eax and the receiver is in edx. These
5868 // register references are not ref counted here. We spill them
5869 // eagerly since they are arguments to an inevitable call (and are
5870 // not sharable by the arguments).
5871 ASSERT(!allocator()->is_used(eax));
5872 frame_->EmitPush(eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005873
5874 // Load the receiver.
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00005875 ASSERT(!allocator()->is_used(edx));
5876 frame_->EmitPush(edx);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005877
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005878 // If fast case code has been generated, emit code to push the
5879 // function and receiver and have the slow path jump around this
5880 // code.
5881 if (done.is_linked()) {
5882 JumpTarget call;
5883 call.Jump();
5884 done.Bind(&function);
5885 frame_->Push(&function);
5886 LoadGlobalReceiver();
5887 call.Bind();
5888 }
5889
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005890 // Call the function.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005891 CallWithArguments(args, NO_CALL_FUNCTION_FLAGS, node->position());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005892
5893 } else if (property != NULL) {
5894 // Check if the key is a literal string.
5895 Literal* literal = property->key()->AsLiteral();
5896
5897 if (literal != NULL && literal->handle()->IsSymbol()) {
5898 // ------------------------------------------------------------------
5899 // JavaScript example: 'object.foo(1, 2, 3)' or 'map["key"](1, 2, 3)'
5900 // ------------------------------------------------------------------
5901
ager@chromium.org3e875802009-06-29 08:26:34 +00005902 Handle<String> name = Handle<String>::cast(literal->handle());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005903
ager@chromium.org3e875802009-06-29 08:26:34 +00005904 if (ArgumentsMode() == LAZY_ARGUMENTS_ALLOCATION &&
5905 name->IsEqualTo(CStrVector("apply")) &&
5906 args->length() == 2 &&
5907 args->at(1)->AsVariableProxy() != NULL &&
5908 args->at(1)->AsVariableProxy()->IsArguments()) {
5909 // Use the optimized Function.prototype.apply that avoids
5910 // allocating lazily allocated arguments objects.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005911 CallApplyLazy(property->obj(),
ager@chromium.org3e875802009-06-29 08:26:34 +00005912 args->at(0),
5913 args->at(1)->AsVariableProxy(),
5914 node->position());
5915
5916 } else {
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00005917 // Push the receiver onto the frame.
ager@chromium.org3e875802009-06-29 08:26:34 +00005918 Load(property->obj());
5919
5920 // Load the arguments.
5921 int arg_count = args->length();
5922 for (int i = 0; i < arg_count; i++) {
5923 Load(args->at(i));
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005924 frame_->SpillTop();
ager@chromium.org3e875802009-06-29 08:26:34 +00005925 }
5926
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00005927 // Push the name of the function onto the frame.
5928 frame_->Push(name);
5929
ager@chromium.org3e875802009-06-29 08:26:34 +00005930 // Call the IC initialization code.
5931 CodeForSourcePosition(node->position());
5932 Result result =
5933 frame_->CallCallIC(RelocInfo::CODE_TARGET, arg_count,
5934 loop_nesting());
5935 frame_->RestoreContextRegister();
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00005936 frame_->Push(&result);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005937 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005938
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005939 } else {
5940 // -------------------------------------------
5941 // JavaScript example: 'array[index](1, 2, 3)'
5942 // -------------------------------------------
5943
5944 // Load the function to call from the property through a reference.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005945
5946 // Pass receiver to called function.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005947 if (property->is_synthetic()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005948 Reference ref(this, property);
5949 ref.GetValue();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005950 // Use global object as receiver.
5951 LoadGlobalReceiver();
5952 } else {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005953 Load(property->obj());
ager@chromium.org5c838252010-02-19 08:53:10 +00005954 frame()->Dup();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005955 Load(property->key());
ager@chromium.org5c838252010-02-19 08:53:10 +00005956 Result function = EmitKeyedLoad();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005957 Result receiver = frame_->Pop();
5958 frame_->Push(&function);
5959 frame_->Push(&receiver);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005960 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005961
5962 // Call the function.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005963 CallWithArguments(args, RECEIVER_MIGHT_BE_VALUE, node->position());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005964 }
5965
5966 } else {
5967 // ----------------------------------
5968 // JavaScript example: 'foo(1, 2, 3)' // foo is not global
5969 // ----------------------------------
5970
5971 // Load the function.
5972 Load(function);
5973
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005974 // Pass the global proxy as the receiver.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005975 LoadGlobalReceiver();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005976
5977 // Call the function.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005978 CallWithArguments(args, NO_CALL_FUNCTION_FLAGS, node->position());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005979 }
5980}
5981
5982
ager@chromium.org7c537e22008-10-16 08:43:32 +00005983void CodeGenerator::VisitCallNew(CallNew* node) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005984 ASSERT(!in_safe_int32_mode());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005985 Comment cmnt(masm_, "[ CallNew");
5986
5987 // According to ECMA-262, section 11.2.2, page 44, the function
5988 // expression in new calls must be evaluated before the
5989 // arguments. This is different from ordinary calls, where the
5990 // actual function to call is resolved after the arguments have been
5991 // evaluated.
5992
5993 // Compute function to call and use the global object as the
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005994 // receiver. There is no need to use the global proxy here because
5995 // it will always be replaced with a newly allocated object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005996 Load(node->expression());
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005997 LoadGlobal();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005998
5999 // Push the arguments ("left-to-right") on the stack.
6000 ZoneList<Expression*>* args = node->arguments();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006001 int arg_count = args->length();
6002 for (int i = 0; i < arg_count; i++) {
6003 Load(args->at(i));
6004 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006005
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006006 // Call the construct call builtin that handles allocation and
6007 // constructor invocation.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006008 CodeForSourcePosition(node->position());
ager@chromium.org41826e72009-03-30 13:30:57 +00006009 Result result = frame_->CallConstructor(arg_count);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006010 // Replace the function on the stack with the result.
6011 frame_->SetElementAt(0, &result);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006012}
6013
6014
ager@chromium.org7c537e22008-10-16 08:43:32 +00006015void CodeGenerator::GenerateIsSmi(ZoneList<Expression*>* args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006016 ASSERT(args->length() == 1);
6017 Load(args->at(0));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006018 Result value = frame_->Pop();
6019 value.ToRegister();
6020 ASSERT(value.is_valid());
6021 __ test(value.reg(), Immediate(kSmiTagMask));
6022 value.Unuse();
6023 destination()->Split(zero);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006024}
6025
6026
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006027void CodeGenerator::GenerateLog(ZoneList<Expression*>* args) {
6028 // Conditionally generate a log call.
6029 // Args:
6030 // 0 (literal string): The type of logging (corresponds to the flags).
6031 // This is used to determine whether or not to generate the log call.
6032 // 1 (string): Format string. Access the string at argument index 2
6033 // with '%2s' (see Logger::LogRuntime for all the formats).
6034 // 2 (array): Arguments to the format string.
6035 ASSERT_EQ(args->length(), 3);
christian.plesner.hansen@gmail.comaca49682009-01-07 14:29:04 +00006036#ifdef ENABLE_LOGGING_AND_PROFILING
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006037 if (ShouldGenerateLog(args->at(0))) {
6038 Load(args->at(1));
6039 Load(args->at(2));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006040 frame_->CallRuntime(Runtime::kLog, 2);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006041 }
christian.plesner.hansen@gmail.comaca49682009-01-07 14:29:04 +00006042#endif
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006043 // Finally, we're expected to leave a value on the top of the stack.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006044 frame_->Push(Factory::undefined_value());
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006045}
6046
6047
ager@chromium.org7c537e22008-10-16 08:43:32 +00006048void CodeGenerator::GenerateIsNonNegativeSmi(ZoneList<Expression*>* args) {
ager@chromium.orgc27e4e72008-09-04 13:52:27 +00006049 ASSERT(args->length() == 1);
6050 Load(args->at(0));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006051 Result value = frame_->Pop();
6052 value.ToRegister();
6053 ASSERT(value.is_valid());
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00006054 __ test(value.reg(), Immediate(kSmiTagMask | kSmiSignMask));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006055 value.Unuse();
6056 destination()->Split(zero);
ager@chromium.orgc27e4e72008-09-04 13:52:27 +00006057}
6058
6059
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006060class DeferredStringCharCodeAt : public DeferredCode {
6061 public:
6062 DeferredStringCharCodeAt(Register object,
6063 Register index,
6064 Register scratch,
6065 Register result)
6066 : result_(result),
6067 char_code_at_generator_(object,
6068 index,
6069 scratch,
6070 result,
6071 &need_conversion_,
6072 &need_conversion_,
6073 &index_out_of_range_,
6074 STRING_INDEX_IS_NUMBER) {}
6075
6076 StringCharCodeAtGenerator* fast_case_generator() {
6077 return &char_code_at_generator_;
6078 }
6079
6080 virtual void Generate() {
6081 VirtualFrameRuntimeCallHelper call_helper(frame_state());
6082 char_code_at_generator_.GenerateSlow(masm(), call_helper);
6083
6084 __ bind(&need_conversion_);
6085 // Move the undefined value into the result register, which will
6086 // trigger conversion.
6087 __ Set(result_, Immediate(Factory::undefined_value()));
6088 __ jmp(exit_label());
6089
6090 __ bind(&index_out_of_range_);
6091 // When the index is out of range, the spec requires us to return
6092 // NaN.
6093 __ Set(result_, Immediate(Factory::nan_value()));
6094 __ jmp(exit_label());
6095 }
6096
6097 private:
6098 Register result_;
6099
6100 Label need_conversion_;
6101 Label index_out_of_range_;
6102
6103 StringCharCodeAtGenerator char_code_at_generator_;
6104};
6105
6106
6107// This generates code that performs a String.prototype.charCodeAt() call
6108// or returns a smi in order to trigger conversion.
6109void CodeGenerator::GenerateStringCharCodeAt(ZoneList<Expression*>* args) {
6110 Comment(masm_, "[ GenerateStringCharCodeAt");
kasper.lund7276f142008-07-30 08:49:36 +00006111 ASSERT(args->length() == 2);
6112
kasper.lund7276f142008-07-30 08:49:36 +00006113 Load(args->at(0));
ager@chromium.org8bb60582008-12-11 12:02:20 +00006114 Load(args->at(1));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006115 Result index = frame_->Pop();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006116 Result object = frame_->Pop();
6117 object.ToRegister();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006118 index.ToRegister();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006119 // We might mutate the object register.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006120 frame_->Spill(object.reg());
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006121
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00006122 // We need two extra registers.
6123 Result result = allocator()->Allocate();
6124 ASSERT(result.is_valid());
6125 Result scratch = allocator()->Allocate();
6126 ASSERT(scratch.is_valid());
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006127
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006128 DeferredStringCharCodeAt* deferred =
6129 new DeferredStringCharCodeAt(object.reg(),
6130 index.reg(),
6131 scratch.reg(),
6132 result.reg());
6133 deferred->fast_case_generator()->GenerateFast(masm_);
6134 deferred->BindExit();
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00006135 frame_->Push(&result);
kasper.lund7276f142008-07-30 08:49:36 +00006136}
6137
6138
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006139class DeferredStringCharFromCode : public DeferredCode {
6140 public:
6141 DeferredStringCharFromCode(Register code,
6142 Register result)
6143 : char_from_code_generator_(code, result) {}
6144
6145 StringCharFromCodeGenerator* fast_case_generator() {
6146 return &char_from_code_generator_;
6147 }
6148
6149 virtual void Generate() {
6150 VirtualFrameRuntimeCallHelper call_helper(frame_state());
6151 char_from_code_generator_.GenerateSlow(masm(), call_helper);
6152 }
6153
6154 private:
6155 StringCharFromCodeGenerator char_from_code_generator_;
6156};
6157
6158
6159// Generates code for creating a one-char string from a char code.
6160void CodeGenerator::GenerateStringCharFromCode(ZoneList<Expression*>* args) {
6161 Comment(masm_, "[ GenerateStringCharFromCode");
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006162 ASSERT(args->length() == 1);
6163
6164 Load(args->at(0));
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00006165
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006166 Result code = frame_->Pop();
6167 code.ToRegister();
6168 ASSERT(code.is_valid());
6169
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00006170 Result result = allocator()->Allocate();
6171 ASSERT(result.is_valid());
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006172
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006173 DeferredStringCharFromCode* deferred = new DeferredStringCharFromCode(
6174 code.reg(), result.reg());
6175 deferred->fast_case_generator()->GenerateFast(masm_);
6176 deferred->BindExit();
6177 frame_->Push(&result);
6178}
6179
6180
6181class DeferredStringCharAt : public DeferredCode {
6182 public:
6183 DeferredStringCharAt(Register object,
6184 Register index,
6185 Register scratch1,
6186 Register scratch2,
6187 Register result)
6188 : result_(result),
6189 char_at_generator_(object,
6190 index,
6191 scratch1,
6192 scratch2,
6193 result,
6194 &need_conversion_,
6195 &need_conversion_,
6196 &index_out_of_range_,
6197 STRING_INDEX_IS_NUMBER) {}
6198
6199 StringCharAtGenerator* fast_case_generator() {
6200 return &char_at_generator_;
6201 }
6202
6203 virtual void Generate() {
6204 VirtualFrameRuntimeCallHelper call_helper(frame_state());
6205 char_at_generator_.GenerateSlow(masm(), call_helper);
6206
6207 __ bind(&need_conversion_);
6208 // Move smi zero into the result register, which will trigger
6209 // conversion.
6210 __ Set(result_, Immediate(Smi::FromInt(0)));
6211 __ jmp(exit_label());
6212
6213 __ bind(&index_out_of_range_);
6214 // When the index is out of range, the spec requires us to return
6215 // the empty string.
6216 __ Set(result_, Immediate(Factory::empty_string()));
6217 __ jmp(exit_label());
6218 }
6219
6220 private:
6221 Register result_;
6222
6223 Label need_conversion_;
6224 Label index_out_of_range_;
6225
6226 StringCharAtGenerator char_at_generator_;
6227};
6228
6229
6230// This generates code that performs a String.prototype.charAt() call
6231// or returns a smi in order to trigger conversion.
6232void CodeGenerator::GenerateStringCharAt(ZoneList<Expression*>* args) {
6233 Comment(masm_, "[ GenerateStringCharAt");
6234 ASSERT(args->length() == 2);
6235
6236 Load(args->at(0));
6237 Load(args->at(1));
6238 Result index = frame_->Pop();
6239 Result object = frame_->Pop();
6240 object.ToRegister();
6241 index.ToRegister();
6242 // We might mutate the object register.
6243 frame_->Spill(object.reg());
6244
6245 // We need three extra registers.
6246 Result result = allocator()->Allocate();
6247 ASSERT(result.is_valid());
6248 Result scratch1 = allocator()->Allocate();
6249 ASSERT(scratch1.is_valid());
6250 Result scratch2 = allocator()->Allocate();
6251 ASSERT(scratch2.is_valid());
6252
6253 DeferredStringCharAt* deferred =
6254 new DeferredStringCharAt(object.reg(),
6255 index.reg(),
6256 scratch1.reg(),
6257 scratch2.reg(),
6258 result.reg());
6259 deferred->fast_case_generator()->GenerateFast(masm_);
6260 deferred->BindExit();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006261 frame_->Push(&result);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006262}
6263
6264
ager@chromium.org7c537e22008-10-16 08:43:32 +00006265void CodeGenerator::GenerateIsArray(ZoneList<Expression*>* args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006266 ASSERT(args->length() == 1);
6267 Load(args->at(0));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006268 Result value = frame_->Pop();
6269 value.ToRegister();
6270 ASSERT(value.is_valid());
6271 __ test(value.reg(), Immediate(kSmiTagMask));
6272 destination()->false_target()->Branch(equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006273 // It is a heap object - get map.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006274 Result temp = allocator()->Allocate();
6275 ASSERT(temp.is_valid());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006276 // Check if the object is a JS array or not.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006277 __ CmpObjectType(value.reg(), JS_ARRAY_TYPE, temp.reg());
6278 value.Unuse();
6279 temp.Unuse();
6280 destination()->Split(equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006281}
6282
6283
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00006284void CodeGenerator::GenerateIsRegExp(ZoneList<Expression*>* args) {
6285 ASSERT(args->length() == 1);
6286 Load(args->at(0));
6287 Result value = frame_->Pop();
6288 value.ToRegister();
6289 ASSERT(value.is_valid());
6290 __ test(value.reg(), Immediate(kSmiTagMask));
6291 destination()->false_target()->Branch(equal);
6292 // It is a heap object - get map.
6293 Result temp = allocator()->Allocate();
6294 ASSERT(temp.is_valid());
6295 // Check if the object is a regexp.
6296 __ CmpObjectType(value.reg(), JS_REGEXP_TYPE, temp.reg());
6297 value.Unuse();
6298 temp.Unuse();
6299 destination()->Split(equal);
6300}
6301
6302
ager@chromium.org6141cbe2009-11-20 12:14:52 +00006303void CodeGenerator::GenerateIsObject(ZoneList<Expression*>* args) {
6304 // This generates a fast version of:
6305 // (typeof(arg) === 'object' || %_ClassOf(arg) == 'RegExp')
6306 ASSERT(args->length() == 1);
6307 Load(args->at(0));
6308 Result obj = frame_->Pop();
6309 obj.ToRegister();
6310
6311 __ test(obj.reg(), Immediate(kSmiTagMask));
6312 destination()->false_target()->Branch(zero);
6313 __ cmp(obj.reg(), Factory::null_value());
6314 destination()->true_target()->Branch(equal);
6315
6316 Result map = allocator()->Allocate();
6317 ASSERT(map.is_valid());
6318 __ mov(map.reg(), FieldOperand(obj.reg(), HeapObject::kMapOffset));
6319 // Undetectable objects behave like undefined when tested with typeof.
6320 __ movzx_b(map.reg(), FieldOperand(map.reg(), Map::kBitFieldOffset));
6321 __ test(map.reg(), Immediate(1 << Map::kIsUndetectable));
6322 destination()->false_target()->Branch(not_zero);
6323 __ mov(map.reg(), FieldOperand(obj.reg(), HeapObject::kMapOffset));
6324 __ movzx_b(map.reg(), FieldOperand(map.reg(), Map::kInstanceTypeOffset));
6325 __ cmp(map.reg(), FIRST_JS_OBJECT_TYPE);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006326 destination()->false_target()->Branch(below);
ager@chromium.org6141cbe2009-11-20 12:14:52 +00006327 __ cmp(map.reg(), LAST_JS_OBJECT_TYPE);
6328 obj.Unuse();
6329 map.Unuse();
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006330 destination()->Split(below_equal);
ager@chromium.org6141cbe2009-11-20 12:14:52 +00006331}
6332
6333
6334void CodeGenerator::GenerateIsFunction(ZoneList<Expression*>* args) {
6335 // This generates a fast version of:
6336 // (%_ClassOf(arg) === 'Function')
6337 ASSERT(args->length() == 1);
6338 Load(args->at(0));
6339 Result obj = frame_->Pop();
6340 obj.ToRegister();
6341 __ test(obj.reg(), Immediate(kSmiTagMask));
6342 destination()->false_target()->Branch(zero);
6343 Result temp = allocator()->Allocate();
6344 ASSERT(temp.is_valid());
6345 __ CmpObjectType(obj.reg(), JS_FUNCTION_TYPE, temp.reg());
6346 obj.Unuse();
6347 temp.Unuse();
6348 destination()->Split(equal);
6349}
6350
6351
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006352void CodeGenerator::GenerateIsUndetectableObject(ZoneList<Expression*>* args) {
6353 ASSERT(args->length() == 1);
6354 Load(args->at(0));
6355 Result obj = frame_->Pop();
6356 obj.ToRegister();
6357 __ test(obj.reg(), Immediate(kSmiTagMask));
6358 destination()->false_target()->Branch(zero);
6359 Result temp = allocator()->Allocate();
6360 ASSERT(temp.is_valid());
6361 __ mov(temp.reg(),
6362 FieldOperand(obj.reg(), HeapObject::kMapOffset));
6363 __ movzx_b(temp.reg(),
6364 FieldOperand(temp.reg(), Map::kBitFieldOffset));
6365 __ test(temp.reg(), Immediate(1 << Map::kIsUndetectable));
6366 obj.Unuse();
6367 temp.Unuse();
6368 destination()->Split(not_zero);
6369}
6370
6371
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006372void CodeGenerator::GenerateIsConstructCall(ZoneList<Expression*>* args) {
6373 ASSERT(args->length() == 0);
6374
6375 // Get the frame pointer for the calling frame.
6376 Result fp = allocator()->Allocate();
6377 __ mov(fp.reg(), Operand(ebp, StandardFrameConstants::kCallerFPOffset));
6378
6379 // Skip the arguments adaptor frame if it exists.
6380 Label check_frame_marker;
6381 __ cmp(Operand(fp.reg(), StandardFrameConstants::kContextOffset),
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006382 Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006383 __ j(not_equal, &check_frame_marker);
6384 __ mov(fp.reg(), Operand(fp.reg(), StandardFrameConstants::kCallerFPOffset));
6385
6386 // Check the marker in the calling frame.
6387 __ bind(&check_frame_marker);
6388 __ cmp(Operand(fp.reg(), StandardFrameConstants::kMarkerOffset),
6389 Immediate(Smi::FromInt(StackFrame::CONSTRUCT)));
6390 fp.Unuse();
6391 destination()->Split(equal);
6392}
6393
6394
ager@chromium.org7c537e22008-10-16 08:43:32 +00006395void CodeGenerator::GenerateArgumentsLength(ZoneList<Expression*>* args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006396 ASSERT(args->length() == 0);
lrn@chromium.org25156de2010-04-06 13:10:27 +00006397
6398 Result fp = allocator_->Allocate();
6399 Result result = allocator_->Allocate();
6400 ASSERT(fp.is_valid() && result.is_valid());
6401
6402 Label exit;
6403
6404 // Get the number of formal parameters.
6405 __ Set(result.reg(), Immediate(Smi::FromInt(scope()->num_parameters())));
6406
6407 // Check if the calling frame is an arguments adaptor frame.
6408 __ mov(fp.reg(), Operand(ebp, StandardFrameConstants::kCallerFPOffset));
6409 __ cmp(Operand(fp.reg(), StandardFrameConstants::kContextOffset),
6410 Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
6411 __ j(not_equal, &exit);
6412
6413 // Arguments adaptor case: Read the arguments length from the
6414 // adaptor frame.
6415 __ mov(result.reg(),
6416 Operand(fp.reg(), ArgumentsAdaptorFrameConstants::kLengthOffset));
6417
6418 __ bind(&exit);
6419 result.set_type_info(TypeInfo::Smi());
6420 if (FLAG_debug_code) __ AbortIfNotSmi(result.reg());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006421 frame_->Push(&result);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006422}
6423
6424
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006425void CodeGenerator::GenerateClassOf(ZoneList<Expression*>* args) {
6426 ASSERT(args->length() == 1);
6427 JumpTarget leave, null, function, non_function_constructor;
6428 Load(args->at(0)); // Load the object.
6429 Result obj = frame_->Pop();
6430 obj.ToRegister();
6431 frame_->Spill(obj.reg());
6432
6433 // If the object is a smi, we return null.
6434 __ test(obj.reg(), Immediate(kSmiTagMask));
6435 null.Branch(zero);
6436
6437 // Check that the object is a JS object but take special care of JS
6438 // functions to make sure they have 'Function' as their class.
6439 { Result tmp = allocator()->Allocate();
6440 __ mov(obj.reg(), FieldOperand(obj.reg(), HeapObject::kMapOffset));
6441 __ movzx_b(tmp.reg(), FieldOperand(obj.reg(), Map::kInstanceTypeOffset));
6442 __ cmp(tmp.reg(), FIRST_JS_OBJECT_TYPE);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006443 null.Branch(below);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006444
6445 // As long as JS_FUNCTION_TYPE is the last instance type and it is
6446 // right after LAST_JS_OBJECT_TYPE, we can avoid checking for
6447 // LAST_JS_OBJECT_TYPE.
6448 ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
6449 ASSERT(JS_FUNCTION_TYPE == LAST_JS_OBJECT_TYPE + 1);
6450 __ cmp(tmp.reg(), JS_FUNCTION_TYPE);
6451 function.Branch(equal);
6452 }
6453
6454 // Check if the constructor in the map is a function.
6455 { Result tmp = allocator()->Allocate();
6456 __ mov(obj.reg(), FieldOperand(obj.reg(), Map::kConstructorOffset));
6457 __ CmpObjectType(obj.reg(), JS_FUNCTION_TYPE, tmp.reg());
6458 non_function_constructor.Branch(not_equal);
6459 }
6460
6461 // The map register now contains the constructor function. Grab the
6462 // instance class name from there.
6463 __ mov(obj.reg(),
6464 FieldOperand(obj.reg(), JSFunction::kSharedFunctionInfoOffset));
6465 __ mov(obj.reg(),
6466 FieldOperand(obj.reg(), SharedFunctionInfo::kInstanceClassNameOffset));
6467 frame_->Push(&obj);
6468 leave.Jump();
6469
6470 // Functions have class 'Function'.
6471 function.Bind();
6472 frame_->Push(Factory::function_class_symbol());
6473 leave.Jump();
6474
6475 // Objects with a non-function constructor have class 'Object'.
6476 non_function_constructor.Bind();
6477 frame_->Push(Factory::Object_symbol());
6478 leave.Jump();
6479
6480 // Non-JS objects have class null.
6481 null.Bind();
6482 frame_->Push(Factory::null_value());
6483
6484 // All done.
6485 leave.Bind();
6486}
6487
6488
ager@chromium.org7c537e22008-10-16 08:43:32 +00006489void CodeGenerator::GenerateValueOf(ZoneList<Expression*>* args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006490 ASSERT(args->length() == 1);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00006491 JumpTarget leave;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006492 Load(args->at(0)); // Load the object.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006493 frame_->Dup();
6494 Result object = frame_->Pop();
6495 object.ToRegister();
6496 ASSERT(object.is_valid());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006497 // if (object->IsSmi()) return object.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006498 __ test(object.reg(), Immediate(kSmiTagMask));
6499 leave.Branch(zero, taken);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006500 // It is a heap object - get map.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006501 Result temp = allocator()->Allocate();
6502 ASSERT(temp.is_valid());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006503 // if (!object->IsJSValue()) return object.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006504 __ CmpObjectType(object.reg(), JS_VALUE_TYPE, temp.reg());
6505 leave.Branch(not_equal, not_taken);
6506 __ mov(temp.reg(), FieldOperand(object.reg(), JSValue::kValueOffset));
6507 object.Unuse();
6508 frame_->SetElementAt(0, &temp);
6509 leave.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006510}
6511
6512
ager@chromium.org7c537e22008-10-16 08:43:32 +00006513void CodeGenerator::GenerateSetValueOf(ZoneList<Expression*>* args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006514 ASSERT(args->length() == 2);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00006515 JumpTarget leave;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006516 Load(args->at(0)); // Load the object.
6517 Load(args->at(1)); // Load the value.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006518 Result value = frame_->Pop();
6519 Result object = frame_->Pop();
6520 value.ToRegister();
6521 object.ToRegister();
6522
6523 // if (object->IsSmi()) return value.
6524 __ test(object.reg(), Immediate(kSmiTagMask));
6525 leave.Branch(zero, &value, taken);
6526
6527 // It is a heap object - get its map.
6528 Result scratch = allocator_->Allocate();
6529 ASSERT(scratch.is_valid());
6530 // if (!object->IsJSValue()) return value.
6531 __ CmpObjectType(object.reg(), JS_VALUE_TYPE, scratch.reg());
6532 leave.Branch(not_equal, &value, not_taken);
6533
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006534 // Store the value.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006535 __ mov(FieldOperand(object.reg(), JSValue::kValueOffset), value.reg());
6536 // Update the write barrier. Save the value as it will be
6537 // overwritten by the write barrier code and is needed afterward.
6538 Result duplicate_value = allocator_->Allocate();
6539 ASSERT(duplicate_value.is_valid());
6540 __ mov(duplicate_value.reg(), value.reg());
6541 // The object register is also overwritten by the write barrier and
6542 // possibly aliased in the frame.
6543 frame_->Spill(object.reg());
6544 __ RecordWrite(object.reg(), JSValue::kValueOffset, duplicate_value.reg(),
6545 scratch.reg());
6546 object.Unuse();
6547 scratch.Unuse();
6548 duplicate_value.Unuse();
6549
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006550 // Leave.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006551 leave.Bind(&value);
6552 frame_->Push(&value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006553}
6554
6555
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006556void CodeGenerator::GenerateArguments(ZoneList<Expression*>* args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006557 ASSERT(args->length() == 1);
6558
ager@chromium.org41826e72009-03-30 13:30:57 +00006559 // ArgumentsAccessStub expects the key in edx and the formal
6560 // parameter count in eax.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006561 Load(args->at(0));
ager@chromium.org41826e72009-03-30 13:30:57 +00006562 Result key = frame_->Pop();
6563 // Explicitly create a constant result.
ager@chromium.org5c838252010-02-19 08:53:10 +00006564 Result count(Handle<Smi>(Smi::FromInt(scope()->num_parameters())));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006565 // Call the shared stub to get to arguments[key].
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006566 ArgumentsAccessStub stub(ArgumentsAccessStub::READ_ELEMENT);
ager@chromium.org41826e72009-03-30 13:30:57 +00006567 Result result = frame_->CallStub(&stub, &key, &count);
6568 frame_->Push(&result);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006569}
6570
6571
ager@chromium.org7c537e22008-10-16 08:43:32 +00006572void CodeGenerator::GenerateObjectEquals(ZoneList<Expression*>* args) {
ager@chromium.org9258b6b2008-09-11 09:11:10 +00006573 ASSERT(args->length() == 2);
6574
6575 // Load the two objects into registers and perform the comparison.
6576 Load(args->at(0));
6577 Load(args->at(1));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006578 Result right = frame_->Pop();
6579 Result left = frame_->Pop();
6580 right.ToRegister();
6581 left.ToRegister();
6582 __ cmp(right.reg(), Operand(left.reg()));
6583 right.Unuse();
6584 left.Unuse();
6585 destination()->Split(equal);
ager@chromium.org9258b6b2008-09-11 09:11:10 +00006586}
6587
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006588
ager@chromium.org9085a012009-05-11 19:22:57 +00006589void CodeGenerator::GenerateGetFramePointer(ZoneList<Expression*>* args) {
6590 ASSERT(args->length() == 0);
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +00006591 ASSERT(kSmiTag == 0); // EBP value is aligned, so it should look like Smi.
ager@chromium.org9085a012009-05-11 19:22:57 +00006592 Result ebp_as_smi = allocator_->Allocate();
6593 ASSERT(ebp_as_smi.is_valid());
6594 __ mov(ebp_as_smi.reg(), Operand(ebp));
ager@chromium.org9085a012009-05-11 19:22:57 +00006595 frame_->Push(&ebp_as_smi);
6596}
6597
6598
ager@chromium.org357bf652010-04-12 11:30:10 +00006599void CodeGenerator::GenerateRandomHeapNumber(
6600 ZoneList<Expression*>* args) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006601 ASSERT(args->length() == 0);
6602 frame_->SpillAll();
6603
ager@chromium.org357bf652010-04-12 11:30:10 +00006604 Label slow_allocate_heapnumber;
6605 Label heapnumber_allocated;
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006606
ager@chromium.org357bf652010-04-12 11:30:10 +00006607 __ AllocateHeapNumber(edi, ebx, ecx, &slow_allocate_heapnumber);
6608 __ jmp(&heapnumber_allocated);
6609
6610 __ bind(&slow_allocate_heapnumber);
6611 // To allocate a heap number, and ensure that it is not a smi, we
6612 // call the runtime function FUnaryMinus on 0, returning the double
6613 // -0.0. A new, distinct heap number is returned each time.
6614 __ push(Immediate(Smi::FromInt(0)));
6615 __ CallRuntime(Runtime::kNumberUnaryMinus, 1);
6616 __ mov(edi, eax);
6617
6618 __ bind(&heapnumber_allocated);
6619
6620 __ PrepareCallCFunction(0, ebx);
6621 __ CallCFunction(ExternalReference::random_uint32_function(), 0);
6622
6623 // Convert 32 random bits in eax to 0.(32 random bits) in a double
6624 // by computing:
6625 // ( 1.(20 0s)(32 random bits) x 2^20 ) - (1.0 x 2^20)).
6626 // This is implemented on both SSE2 and FPU.
6627 if (CpuFeatures::IsSupported(SSE2)) {
6628 CpuFeatures::Scope fscope(SSE2);
6629 __ mov(ebx, Immediate(0x49800000)); // 1.0 x 2^20 as single.
6630 __ movd(xmm1, Operand(ebx));
6631 __ movd(xmm0, Operand(eax));
6632 __ cvtss2sd(xmm1, xmm1);
6633 __ pxor(xmm0, xmm1);
6634 __ subsd(xmm0, xmm1);
6635 __ movdbl(FieldOperand(edi, HeapNumber::kValueOffset), xmm0);
6636 } else {
6637 // 0x4130000000000000 is 1.0 x 2^20 as a double.
6638 __ mov(FieldOperand(edi, HeapNumber::kExponentOffset),
6639 Immediate(0x41300000));
6640 __ mov(FieldOperand(edi, HeapNumber::kMantissaOffset), eax);
6641 __ fld_d(FieldOperand(edi, HeapNumber::kValueOffset));
6642 __ mov(FieldOperand(edi, HeapNumber::kMantissaOffset), Immediate(0));
6643 __ fld_d(FieldOperand(edi, HeapNumber::kValueOffset));
6644 __ fsubp(1);
6645 __ fstp_d(FieldOperand(edi, HeapNumber::kValueOffset));
6646 }
6647 __ mov(eax, edi);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006648
6649 Result result = allocator_->Allocate(eax);
6650 frame_->Push(&result);
6651}
6652
6653
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00006654void CodeGenerator::GenerateStringAdd(ZoneList<Expression*>* args) {
6655 ASSERT_EQ(2, args->length());
6656
6657 Load(args->at(0));
6658 Load(args->at(1));
6659
6660 StringAddStub stub(NO_STRING_ADD_FLAGS);
6661 Result answer = frame_->CallStub(&stub, 2);
6662 frame_->Push(&answer);
6663}
6664
6665
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006666void CodeGenerator::GenerateSubString(ZoneList<Expression*>* args) {
6667 ASSERT_EQ(3, args->length());
6668
6669 Load(args->at(0));
6670 Load(args->at(1));
6671 Load(args->at(2));
6672
6673 SubStringStub stub;
6674 Result answer = frame_->CallStub(&stub, 3);
6675 frame_->Push(&answer);
6676}
6677
6678
6679void CodeGenerator::GenerateStringCompare(ZoneList<Expression*>* args) {
6680 ASSERT_EQ(2, args->length());
6681
6682 Load(args->at(0));
6683 Load(args->at(1));
6684
6685 StringCompareStub stub;
6686 Result answer = frame_->CallStub(&stub, 2);
6687 frame_->Push(&answer);
6688}
6689
6690
6691void CodeGenerator::GenerateRegExpExec(ZoneList<Expression*>* args) {
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00006692 ASSERT_EQ(4, args->length());
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006693
6694 // Load the arguments on the stack and call the stub.
6695 Load(args->at(0));
6696 Load(args->at(1));
6697 Load(args->at(2));
6698 Load(args->at(3));
6699 RegExpExecStub stub;
6700 Result result = frame_->CallStub(&stub, 4);
6701 frame_->Push(&result);
6702}
6703
6704
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00006705void CodeGenerator::GenerateRegExpConstructResult(ZoneList<Expression*>* args) {
6706 // No stub. This code only occurs a few times in regexp.js.
6707 const int kMaxInlineLength = 100;
6708 ASSERT_EQ(3, args->length());
6709 Load(args->at(0)); // Size of array, smi.
6710 Load(args->at(1)); // "index" property value.
6711 Load(args->at(2)); // "input" property value.
6712 {
6713 VirtualFrame::SpilledScope spilled_scope;
6714
6715 Label slowcase;
6716 Label done;
6717 __ mov(ebx, Operand(esp, kPointerSize * 2));
6718 __ test(ebx, Immediate(kSmiTagMask));
6719 __ j(not_zero, &slowcase);
6720 __ cmp(Operand(ebx), Immediate(Smi::FromInt(kMaxInlineLength)));
6721 __ j(above, &slowcase);
6722 // Smi-tagging is equivalent to multiplying by 2.
6723 STATIC_ASSERT(kSmiTag == 0);
6724 STATIC_ASSERT(kSmiTagSize == 1);
6725 // Allocate RegExpResult followed by FixedArray with size in ebx.
6726 // JSArray: [Map][empty properties][Elements][Length-smi][index][input]
6727 // Elements: [Map][Length][..elements..]
6728 __ AllocateInNewSpace(JSRegExpResult::kSize + FixedArray::kHeaderSize,
6729 times_half_pointer_size,
6730 ebx, // In: Number of elements (times 2, being a smi)
6731 eax, // Out: Start of allocation (tagged).
6732 ecx, // Out: End of allocation.
6733 edx, // Scratch register
6734 &slowcase,
6735 TAG_OBJECT);
6736 // eax: Start of allocated area, object-tagged.
6737
6738 // Set JSArray map to global.regexp_result_map().
6739 // Set empty properties FixedArray.
6740 // Set elements to point to FixedArray allocated right after the JSArray.
6741 // Interleave operations for better latency.
6742 __ mov(edx, ContextOperand(esi, Context::GLOBAL_INDEX));
6743 __ mov(ecx, Immediate(Factory::empty_fixed_array()));
6744 __ lea(ebx, Operand(eax, JSRegExpResult::kSize));
6745 __ mov(edx, FieldOperand(edx, GlobalObject::kGlobalContextOffset));
6746 __ mov(FieldOperand(eax, JSObject::kElementsOffset), ebx);
6747 __ mov(FieldOperand(eax, JSObject::kPropertiesOffset), ecx);
6748 __ mov(edx, ContextOperand(edx, Context::REGEXP_RESULT_MAP_INDEX));
6749 __ mov(FieldOperand(eax, HeapObject::kMapOffset), edx);
6750
6751 // Set input, index and length fields from arguments.
6752 __ pop(FieldOperand(eax, JSRegExpResult::kInputOffset));
6753 __ pop(FieldOperand(eax, JSRegExpResult::kIndexOffset));
6754 __ pop(ecx);
6755 __ mov(FieldOperand(eax, JSArray::kLengthOffset), ecx);
6756
6757 // Fill out the elements FixedArray.
6758 // eax: JSArray.
6759 // ebx: FixedArray.
6760 // ecx: Number of elements in array, as smi.
6761
6762 // Set map.
6763 __ mov(FieldOperand(ebx, HeapObject::kMapOffset),
6764 Immediate(Factory::fixed_array_map()));
6765 // Set length.
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00006766 __ mov(FieldOperand(ebx, FixedArray::kLengthOffset), ecx);
6767 // Fill contents of fixed-array with the-hole.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006768 __ SmiUntag(ecx);
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00006769 __ mov(edx, Immediate(Factory::the_hole_value()));
6770 __ lea(ebx, FieldOperand(ebx, FixedArray::kHeaderSize));
6771 // Fill fixed array elements with hole.
6772 // eax: JSArray.
6773 // ecx: Number of elements to fill.
6774 // ebx: Start of elements in FixedArray.
6775 // edx: the hole.
6776 Label loop;
6777 __ test(ecx, Operand(ecx));
6778 __ bind(&loop);
6779 __ j(less_equal, &done); // Jump if ecx is negative or zero.
6780 __ sub(Operand(ecx), Immediate(1));
6781 __ mov(Operand(ebx, ecx, times_pointer_size, 0), edx);
6782 __ jmp(&loop);
6783
6784 __ bind(&slowcase);
6785 __ CallRuntime(Runtime::kRegExpConstructResult, 3);
6786
6787 __ bind(&done);
6788 }
6789 frame_->Forget(3);
6790 frame_->Push(eax);
6791}
6792
6793
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00006794class DeferredSearchCache: public DeferredCode {
6795 public:
6796 DeferredSearchCache(Register dst, Register cache, Register key)
6797 : dst_(dst), cache_(cache), key_(key) {
6798 set_comment("[ DeferredSearchCache");
6799 }
6800
6801 virtual void Generate();
6802
6803 private:
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00006804 Register dst_; // on invocation Smi index of finger, on exit
6805 // holds value being looked up.
6806 Register cache_; // instance of JSFunctionResultCache.
6807 Register key_; // key being looked up.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00006808};
6809
6810
6811void DeferredSearchCache::Generate() {
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00006812 Label first_loop, search_further, second_loop, cache_miss;
6813
6814 // Smi-tagging is equivalent to multiplying by 2.
6815 STATIC_ASSERT(kSmiTag == 0);
6816 STATIC_ASSERT(kSmiTagSize == 1);
6817
6818 Smi* kEntrySizeSmi = Smi::FromInt(JSFunctionResultCache::kEntrySize);
6819 Smi* kEntriesIndexSmi = Smi::FromInt(JSFunctionResultCache::kEntriesIndex);
6820
6821 // Check the cache from finger to start of the cache.
6822 __ bind(&first_loop);
6823 __ sub(Operand(dst_), Immediate(kEntrySizeSmi));
6824 __ cmp(Operand(dst_), Immediate(kEntriesIndexSmi));
6825 __ j(less, &search_further);
6826
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00006827 __ cmp(key_, CodeGenerator::FixedArrayElementOperand(cache_, dst_));
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00006828 __ j(not_equal, &first_loop);
6829
6830 __ mov(FieldOperand(cache_, JSFunctionResultCache::kFingerOffset), dst_);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00006831 __ mov(dst_, CodeGenerator::FixedArrayElementOperand(cache_, dst_, 1));
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00006832 __ jmp(exit_label());
6833
6834 __ bind(&search_further);
6835
6836 // Check the cache from end of cache up to finger.
6837 __ mov(dst_, FieldOperand(cache_, JSFunctionResultCache::kCacheSizeOffset));
6838
6839 __ bind(&second_loop);
6840 __ sub(Operand(dst_), Immediate(kEntrySizeSmi));
6841 // Consider prefetching into some reg.
6842 __ cmp(dst_, FieldOperand(cache_, JSFunctionResultCache::kFingerOffset));
6843 __ j(less_equal, &cache_miss);
6844
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00006845 __ cmp(key_, CodeGenerator::FixedArrayElementOperand(cache_, dst_));
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00006846 __ j(not_equal, &second_loop);
6847
6848 __ mov(FieldOperand(cache_, JSFunctionResultCache::kFingerOffset), dst_);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00006849 __ mov(dst_, CodeGenerator::FixedArrayElementOperand(cache_, dst_, 1));
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00006850 __ jmp(exit_label());
6851
6852 __ bind(&cache_miss);
6853 __ push(cache_); // store a reference to cache
6854 __ push(key_); // store a key
6855 Handle<Object> receiver(Top::global_context()->global());
6856 __ push(Immediate(receiver));
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00006857 __ push(key_);
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00006858 // On ia32 function must be in edi.
6859 __ mov(edi, FieldOperand(cache_, JSFunctionResultCache::kFactoryOffset));
6860 ParameterCount expected(1);
6861 __ InvokeFunction(edi, expected, CALL_FUNCTION);
6862
6863 // Find a place to put new cached value into.
6864 Label add_new_entry, update_cache;
6865 __ mov(ecx, Operand(esp, kPointerSize)); // restore the cache
6866 // Possible optimization: cache size is constant for the given cache
6867 // so technically we could use a constant here. However, if we have
6868 // cache miss this optimization would hardly matter much.
6869
6870 // Check if we could add new entry to cache.
6871 __ mov(ebx, FieldOperand(ecx, FixedArray::kLengthOffset));
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00006872 __ cmp(ebx, FieldOperand(ecx, JSFunctionResultCache::kCacheSizeOffset));
6873 __ j(greater, &add_new_entry);
6874
6875 // Check if we could evict entry after finger.
6876 __ mov(edx, FieldOperand(ecx, JSFunctionResultCache::kFingerOffset));
6877 __ add(Operand(edx), Immediate(kEntrySizeSmi));
6878 __ cmp(ebx, Operand(edx));
6879 __ j(greater, &update_cache);
6880
6881 // Need to wrap over the cache.
6882 __ mov(edx, Immediate(kEntriesIndexSmi));
6883 __ jmp(&update_cache);
6884
6885 __ bind(&add_new_entry);
6886 __ mov(edx, FieldOperand(ecx, JSFunctionResultCache::kCacheSizeOffset));
6887 __ lea(ebx, Operand(edx, JSFunctionResultCache::kEntrySize << 1));
6888 __ mov(FieldOperand(ecx, JSFunctionResultCache::kCacheSizeOffset), ebx);
6889
6890 // Update the cache itself.
6891 // edx holds the index.
6892 __ bind(&update_cache);
6893 __ pop(ebx); // restore the key
6894 __ mov(FieldOperand(ecx, JSFunctionResultCache::kFingerOffset), edx);
6895 // Store key.
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00006896 __ mov(CodeGenerator::FixedArrayElementOperand(ecx, edx), ebx);
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00006897 __ RecordWrite(ecx, 0, ebx, edx);
6898
6899 // Store value.
6900 __ pop(ecx); // restore the cache.
6901 __ mov(edx, FieldOperand(ecx, JSFunctionResultCache::kFingerOffset));
6902 __ add(Operand(edx), Immediate(Smi::FromInt(1)));
6903 __ mov(ebx, eax);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00006904 __ mov(CodeGenerator::FixedArrayElementOperand(ecx, edx), ebx);
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00006905 __ RecordWrite(ecx, 0, ebx, edx);
6906
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00006907 if (!dst_.is(eax)) {
6908 __ mov(dst_, eax);
6909 }
6910}
6911
6912
6913void CodeGenerator::GenerateGetFromCache(ZoneList<Expression*>* args) {
6914 ASSERT_EQ(2, args->length());
6915
6916 ASSERT_NE(NULL, args->at(0)->AsLiteral());
6917 int cache_id = Smi::cast(*(args->at(0)->AsLiteral()->handle()))->value();
6918
6919 Handle<FixedArray> jsfunction_result_caches(
6920 Top::global_context()->jsfunction_result_caches());
6921 if (jsfunction_result_caches->length() <= cache_id) {
6922 __ Abort("Attempt to use undefined cache.");
6923 frame_->Push(Factory::undefined_value());
6924 return;
6925 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00006926
6927 Load(args->at(1));
6928 Result key = frame_->Pop();
6929 key.ToRegister();
6930
6931 Result cache = allocator()->Allocate();
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006932 ASSERT(cache.is_valid());
6933 __ mov(cache.reg(), ContextOperand(esi, Context::GLOBAL_INDEX));
6934 __ mov(cache.reg(),
6935 FieldOperand(cache.reg(), GlobalObject::kGlobalContextOffset));
6936 __ mov(cache.reg(),
6937 ContextOperand(cache.reg(), Context::JSFUNCTION_RESULT_CACHES_INDEX));
6938 __ mov(cache.reg(),
6939 FieldOperand(cache.reg(), FixedArray::OffsetOfElementAt(cache_id)));
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00006940
6941 Result tmp = allocator()->Allocate();
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006942 ASSERT(tmp.is_valid());
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00006943
6944 DeferredSearchCache* deferred = new DeferredSearchCache(tmp.reg(),
6945 cache.reg(),
6946 key.reg());
6947
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00006948 // tmp.reg() now holds finger offset as a smi.
6949 ASSERT(kSmiTag == 0 && kSmiTagSize == 1);
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00006950 __ mov(tmp.reg(), FieldOperand(cache.reg(),
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00006951 JSFunctionResultCache::kFingerOffset));
6952 __ cmp(key.reg(), FixedArrayElementOperand(cache.reg(), tmp.reg()));
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00006953 deferred->Branch(not_equal);
6954
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00006955 __ mov(tmp.reg(), FixedArrayElementOperand(cache.reg(), tmp.reg(), 1));
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00006956
6957 deferred->BindExit();
6958 frame_->Push(&tmp);
6959}
6960
6961
ager@chromium.org5c838252010-02-19 08:53:10 +00006962void CodeGenerator::GenerateNumberToString(ZoneList<Expression*>* args) {
6963 ASSERT_EQ(args->length(), 1);
6964
6965 // Load the argument on the stack and call the stub.
6966 Load(args->at(0));
6967 NumberToStringStub stub;
6968 Result result = frame_->CallStub(&stub, 1);
6969 frame_->Push(&result);
6970}
6971
6972
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00006973class DeferredSwapElements: public DeferredCode {
6974 public:
6975 DeferredSwapElements(Register object, Register index1, Register index2)
6976 : object_(object), index1_(index1), index2_(index2) {
6977 set_comment("[ DeferredSwapElements");
6978 }
6979
6980 virtual void Generate();
6981
6982 private:
6983 Register object_, index1_, index2_;
6984};
6985
6986
6987void DeferredSwapElements::Generate() {
6988 __ push(object_);
6989 __ push(index1_);
6990 __ push(index2_);
6991 __ CallRuntime(Runtime::kSwapElements, 3);
6992}
6993
6994
6995void CodeGenerator::GenerateSwapElements(ZoneList<Expression*>* args) {
6996 // Note: this code assumes that indices are passed are within
6997 // elements' bounds and refer to valid (not holes) values.
6998 Comment cmnt(masm_, "[ GenerateSwapElements");
6999
7000 ASSERT_EQ(3, args->length());
7001
7002 Load(args->at(0));
7003 Load(args->at(1));
7004 Load(args->at(2));
7005
7006 Result index2 = frame_->Pop();
7007 index2.ToRegister();
7008
7009 Result index1 = frame_->Pop();
7010 index1.ToRegister();
7011
7012 Result object = frame_->Pop();
7013 object.ToRegister();
7014
7015 Result tmp1 = allocator()->Allocate();
7016 tmp1.ToRegister();
7017 Result tmp2 = allocator()->Allocate();
7018 tmp2.ToRegister();
7019
7020 frame_->Spill(object.reg());
7021 frame_->Spill(index1.reg());
7022 frame_->Spill(index2.reg());
7023
7024 DeferredSwapElements* deferred = new DeferredSwapElements(object.reg(),
7025 index1.reg(),
7026 index2.reg());
7027
7028 // Fetch the map and check if array is in fast case.
7029 // Check that object doesn't require security checks and
7030 // has no indexed interceptor.
7031 __ CmpObjectType(object.reg(), FIRST_JS_OBJECT_TYPE, tmp1.reg());
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00007032 deferred->Branch(below);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00007033 __ movzx_b(tmp1.reg(), FieldOperand(tmp1.reg(), Map::kBitFieldOffset));
7034 __ test(tmp1.reg(), Immediate(KeyedLoadIC::kSlowCaseBitFieldMask));
7035 deferred->Branch(not_zero);
7036
7037 // Check the object's elements are in fast case.
7038 __ mov(tmp1.reg(), FieldOperand(object.reg(), JSObject::kElementsOffset));
7039 __ cmp(FieldOperand(tmp1.reg(), HeapObject::kMapOffset),
7040 Immediate(Factory::fixed_array_map()));
7041 deferred->Branch(not_equal);
7042
7043 // Smi-tagging is equivalent to multiplying by 2.
7044 STATIC_ASSERT(kSmiTag == 0);
7045 STATIC_ASSERT(kSmiTagSize == 1);
7046
7047 // Check that both indices are smis.
7048 __ mov(tmp2.reg(), index1.reg());
7049 __ or_(tmp2.reg(), Operand(index2.reg()));
7050 __ test(tmp2.reg(), Immediate(kSmiTagMask));
7051 deferred->Branch(not_zero);
7052
7053 // Bring addresses into index1 and index2.
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00007054 __ lea(index1.reg(), FixedArrayElementOperand(tmp1.reg(), index1.reg()));
7055 __ lea(index2.reg(), FixedArrayElementOperand(tmp1.reg(), index2.reg()));
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00007056
7057 // Swap elements.
7058 __ mov(object.reg(), Operand(index1.reg(), 0));
7059 __ mov(tmp2.reg(), Operand(index2.reg(), 0));
7060 __ mov(Operand(index2.reg(), 0), object.reg());
7061 __ mov(Operand(index1.reg(), 0), tmp2.reg());
7062
7063 Label done;
7064 __ InNewSpace(tmp1.reg(), tmp2.reg(), equal, &done);
7065 // Possible optimization: do a check that both values are Smis
7066 // (or them and test against Smi mask.)
7067
7068 __ mov(tmp2.reg(), tmp1.reg());
ricow@chromium.org30ce4112010-05-31 10:38:25 +00007069 __ RecordWriteHelper(tmp2.reg(), index1.reg(), object.reg());
7070 __ RecordWriteHelper(tmp1.reg(), index2.reg(), object.reg());
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00007071 __ bind(&done);
7072
7073 deferred->BindExit();
7074 frame_->Push(Factory::undefined_value());
7075}
7076
7077
ager@chromium.org357bf652010-04-12 11:30:10 +00007078void CodeGenerator::GenerateCallFunction(ZoneList<Expression*>* args) {
7079 Comment cmnt(masm_, "[ GenerateCallFunction");
7080
7081 ASSERT(args->length() >= 2);
7082
7083 int n_args = args->length() - 2; // for receiver and function.
7084 Load(args->at(0)); // receiver
7085 for (int i = 0; i < n_args; i++) {
7086 Load(args->at(i + 1));
7087 }
7088 Load(args->at(n_args + 1)); // function
7089 Result result = frame_->CallJSFunction(n_args);
7090 frame_->Push(&result);
7091}
7092
7093
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00007094// Generates the Math.pow method. Only handles special cases and
7095// branches to the runtime system for everything else. Please note
7096// that this function assumes that the callsite has executed ToNumber
7097// on both arguments.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00007098void CodeGenerator::GenerateMathPow(ZoneList<Expression*>* args) {
7099 ASSERT(args->length() == 2);
7100 Load(args->at(0));
7101 Load(args->at(1));
7102 if (!CpuFeatures::IsSupported(SSE2)) {
7103 Result res = frame_->CallRuntime(Runtime::kMath_pow, 2);
7104 frame_->Push(&res);
7105 } else {
7106 CpuFeatures::Scope use_sse2(SSE2);
7107 Label allocate_return;
7108 // Load the two operands while leaving the values on the frame.
7109 frame()->Dup();
7110 Result exponent = frame()->Pop();
7111 exponent.ToRegister();
7112 frame()->Spill(exponent.reg());
7113 frame()->PushElementAt(1);
7114 Result base = frame()->Pop();
7115 base.ToRegister();
7116 frame()->Spill(base.reg());
7117
7118 Result answer = allocator()->Allocate();
7119 ASSERT(answer.is_valid());
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00007120 ASSERT(!exponent.reg().is(base.reg()));
7121 JumpTarget call_runtime;
7122
7123 // Save 1 in xmm3 - we need this several times later on.
7124 __ mov(answer.reg(), Immediate(1));
7125 __ cvtsi2sd(xmm3, Operand(answer.reg()));
7126
7127 Label exponent_nonsmi;
7128 Label base_nonsmi;
7129 // If the exponent is a heap number go to that specific case.
7130 __ test(exponent.reg(), Immediate(kSmiTagMask));
7131 __ j(not_zero, &exponent_nonsmi);
7132 __ test(base.reg(), Immediate(kSmiTagMask));
7133 __ j(not_zero, &base_nonsmi);
7134
7135 // Optimized version when y is an integer.
7136 Label powi;
7137 __ SmiUntag(base.reg());
7138 __ cvtsi2sd(xmm0, Operand(base.reg()));
7139 __ jmp(&powi);
7140 // exponent is smi and base is a heapnumber.
7141 __ bind(&base_nonsmi);
7142 __ cmp(FieldOperand(base.reg(), HeapObject::kMapOffset),
7143 Factory::heap_number_map());
7144 call_runtime.Branch(not_equal);
7145
7146 __ movdbl(xmm0, FieldOperand(base.reg(), HeapNumber::kValueOffset));
7147
7148 // Optimized version of pow if y is an integer.
7149 __ bind(&powi);
7150 __ SmiUntag(exponent.reg());
7151
7152 // Save exponent in base as we need to check if exponent is negative later.
7153 // We know that base and exponent are in different registers.
7154 __ mov(base.reg(), exponent.reg());
7155
7156 // Get absolute value of exponent.
7157 Label no_neg;
7158 __ cmp(exponent.reg(), 0);
7159 __ j(greater_equal, &no_neg);
7160 __ neg(exponent.reg());
7161 __ bind(&no_neg);
7162
7163 // Load xmm1 with 1.
7164 __ movsd(xmm1, xmm3);
7165 Label while_true;
7166 Label no_multiply;
7167
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00007168 __ bind(&while_true);
7169 __ shr(exponent.reg(), 1);
7170 __ j(not_carry, &no_multiply);
7171 __ mulsd(xmm1, xmm0);
7172 __ bind(&no_multiply);
7173 __ test(exponent.reg(), Operand(exponent.reg()));
7174 __ mulsd(xmm0, xmm0);
7175 __ j(not_zero, &while_true);
7176
7177 // x has the original value of y - if y is negative return 1/result.
7178 __ test(base.reg(), Operand(base.reg()));
7179 __ j(positive, &allocate_return);
7180 // Special case if xmm1 has reached infinity.
7181 __ mov(answer.reg(), Immediate(0x7FB00000));
7182 __ movd(xmm0, Operand(answer.reg()));
7183 __ cvtss2sd(xmm0, xmm0);
7184 __ ucomisd(xmm0, xmm1);
7185 call_runtime.Branch(equal);
7186 __ divsd(xmm3, xmm1);
7187 __ movsd(xmm1, xmm3);
7188 __ jmp(&allocate_return);
7189
7190 // exponent (or both) is a heapnumber - no matter what we should now work
7191 // on doubles.
7192 __ bind(&exponent_nonsmi);
7193 __ cmp(FieldOperand(exponent.reg(), HeapObject::kMapOffset),
7194 Factory::heap_number_map());
7195 call_runtime.Branch(not_equal);
7196 __ movdbl(xmm1, FieldOperand(exponent.reg(), HeapNumber::kValueOffset));
7197 // Test if exponent is nan.
7198 __ ucomisd(xmm1, xmm1);
7199 call_runtime.Branch(parity_even);
7200
7201 Label base_not_smi;
7202 Label handle_special_cases;
7203 __ test(base.reg(), Immediate(kSmiTagMask));
7204 __ j(not_zero, &base_not_smi);
7205 __ SmiUntag(base.reg());
7206 __ cvtsi2sd(xmm0, Operand(base.reg()));
7207 __ jmp(&handle_special_cases);
7208 __ bind(&base_not_smi);
7209 __ cmp(FieldOperand(base.reg(), HeapObject::kMapOffset),
7210 Factory::heap_number_map());
7211 call_runtime.Branch(not_equal);
7212 __ mov(answer.reg(), FieldOperand(base.reg(), HeapNumber::kExponentOffset));
7213 __ and_(answer.reg(), HeapNumber::kExponentMask);
7214 __ cmp(Operand(answer.reg()), Immediate(HeapNumber::kExponentMask));
7215 // base is NaN or +/-Infinity
7216 call_runtime.Branch(greater_equal);
7217 __ movdbl(xmm0, FieldOperand(base.reg(), HeapNumber::kValueOffset));
7218
7219 // base is in xmm0 and exponent is in xmm1.
7220 __ bind(&handle_special_cases);
7221 Label not_minus_half;
7222 // Test for -0.5.
7223 // Load xmm2 with -0.5.
7224 __ mov(answer.reg(), Immediate(0xBF000000));
7225 __ movd(xmm2, Operand(answer.reg()));
7226 __ cvtss2sd(xmm2, xmm2);
7227 // xmm2 now has -0.5.
7228 __ ucomisd(xmm2, xmm1);
7229 __ j(not_equal, &not_minus_half);
7230
7231 // Calculates reciprocal of square root.
7232 // Note that 1/sqrt(x) = sqrt(1/x))
7233 __ divsd(xmm3, xmm0);
7234 __ movsd(xmm1, xmm3);
7235 __ sqrtsd(xmm1, xmm1);
7236 __ jmp(&allocate_return);
7237
7238 // Test for 0.5.
7239 __ bind(&not_minus_half);
7240 // Load xmm2 with 0.5.
7241 // Since xmm3 is 1 and xmm2 is -0.5 this is simply xmm2 + xmm3.
7242 __ addsd(xmm2, xmm3);
7243 // xmm2 now has 0.5.
7244 __ comisd(xmm2, xmm1);
7245 call_runtime.Branch(not_equal);
7246 // Calculates square root.
7247 __ movsd(xmm1, xmm0);
7248 __ sqrtsd(xmm1, xmm1);
7249
7250 JumpTarget done;
7251 Label failure, success;
7252 __ bind(&allocate_return);
7253 // Make a copy of the frame to enable us to handle allocation
7254 // failure after the JumpTarget jump.
7255 VirtualFrame* clone = new VirtualFrame(frame());
7256 __ AllocateHeapNumber(answer.reg(), exponent.reg(),
7257 base.reg(), &failure);
7258 __ movdbl(FieldOperand(answer.reg(), HeapNumber::kValueOffset), xmm1);
7259 // Remove the two original values from the frame - we only need those
7260 // in the case where we branch to runtime.
7261 frame()->Drop(2);
7262 exponent.Unuse();
7263 base.Unuse();
7264 done.Jump(&answer);
7265 // Use the copy of the original frame as our current frame.
7266 RegisterFile empty_regs;
7267 SetFrame(clone, &empty_regs);
7268 // If we experience an allocation failure we branch to runtime.
7269 __ bind(&failure);
7270 call_runtime.Bind();
7271 answer = frame()->CallRuntime(Runtime::kMath_pow_cfunction, 2);
7272
7273 done.Bind(&answer);
7274 frame()->Push(&answer);
7275 }
7276}
7277
7278
7279void CodeGenerator::GenerateMathSin(ZoneList<Expression*>* args) {
7280 ASSERT_EQ(args->length(), 1);
7281 Load(args->at(0));
7282 TranscendentalCacheStub stub(TranscendentalCache::SIN);
7283 Result result = frame_->CallStub(&stub, 1);
7284 frame_->Push(&result);
7285}
7286
7287
7288void CodeGenerator::GenerateMathCos(ZoneList<Expression*>* args) {
7289 ASSERT_EQ(args->length(), 1);
7290 Load(args->at(0));
7291 TranscendentalCacheStub stub(TranscendentalCache::COS);
7292 Result result = frame_->CallStub(&stub, 1);
7293 frame_->Push(&result);
7294}
7295
7296
7297// Generates the Math.sqrt method. Please note - this function assumes that
7298// the callsite has executed ToNumber on the argument.
7299void CodeGenerator::GenerateMathSqrt(ZoneList<Expression*>* args) {
7300 ASSERT_EQ(args->length(), 1);
7301 Load(args->at(0));
7302
7303 if (!CpuFeatures::IsSupported(SSE2)) {
7304 Result result = frame()->CallRuntime(Runtime::kMath_sqrt, 1);
7305 frame()->Push(&result);
7306 } else {
7307 CpuFeatures::Scope use_sse2(SSE2);
7308 // Leave original value on the frame if we need to call runtime.
7309 frame()->Dup();
7310 Result result = frame()->Pop();
7311 result.ToRegister();
7312 frame()->Spill(result.reg());
7313 Label runtime;
7314 Label non_smi;
7315 Label load_done;
7316 JumpTarget end;
7317
7318 __ test(result.reg(), Immediate(kSmiTagMask));
7319 __ j(not_zero, &non_smi);
7320 __ SmiUntag(result.reg());
7321 __ cvtsi2sd(xmm0, Operand(result.reg()));
7322 __ jmp(&load_done);
7323 __ bind(&non_smi);
7324 __ cmp(FieldOperand(result.reg(), HeapObject::kMapOffset),
7325 Factory::heap_number_map());
7326 __ j(not_equal, &runtime);
7327 __ movdbl(xmm0, FieldOperand(result.reg(), HeapNumber::kValueOffset));
7328
7329 __ bind(&load_done);
7330 __ sqrtsd(xmm0, xmm0);
7331 // A copy of the virtual frame to allow us to go to runtime after the
7332 // JumpTarget jump.
7333 Result scratch = allocator()->Allocate();
7334 VirtualFrame* clone = new VirtualFrame(frame());
7335 __ AllocateHeapNumber(result.reg(), scratch.reg(), no_reg, &runtime);
7336
7337 __ movdbl(FieldOperand(result.reg(), HeapNumber::kValueOffset), xmm0);
7338 frame()->Drop(1);
7339 scratch.Unuse();
7340 end.Jump(&result);
7341 // We only branch to runtime if we have an allocation error.
7342 // Use the copy of the original frame as our current frame.
7343 RegisterFile empty_regs;
7344 SetFrame(clone, &empty_regs);
7345 __ bind(&runtime);
7346 result = frame()->CallRuntime(Runtime::kMath_sqrt, 1);
7347
7348 end.Bind(&result);
7349 frame()->Push(&result);
7350 }
7351}
7352
7353
ager@chromium.org7c537e22008-10-16 08:43:32 +00007354void CodeGenerator::VisitCallRuntime(CallRuntime* node) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007355 ASSERT(!in_safe_int32_mode());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00007356 if (CheckForInlineRuntimeCall(node)) {
7357 return;
7358 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007359
7360 ZoneList<Expression*>* args = node->arguments();
7361 Comment cmnt(masm_, "[ CallRuntime");
7362 Runtime::Function* function = node->function();
7363
7364 if (function == NULL) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007365 // Push the builtins object found in the current global object.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00007366 Result temp = allocator()->Allocate();
7367 ASSERT(temp.is_valid());
7368 __ mov(temp.reg(), GlobalObject());
7369 __ mov(temp.reg(), FieldOperand(temp.reg(), GlobalObject::kBuiltinsOffset));
7370 frame_->Push(&temp);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007371 }
7372
7373 // Push the arguments ("left-to-right").
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00007374 int arg_count = args->length();
7375 for (int i = 0; i < arg_count; i++) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007376 Load(args->at(i));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00007377 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007378
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00007379 if (function == NULL) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007380 // Call the JS runtime function.
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00007381 frame_->Push(node->name());
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007382 Result answer = frame_->CallCallIC(RelocInfo::CODE_TARGET,
7383 arg_count,
7384 loop_nesting_);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00007385 frame_->RestoreContextRegister();
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00007386 frame_->Push(&answer);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00007387 } else {
7388 // Call the C runtime function.
7389 Result answer = frame_->CallRuntime(function, arg_count);
7390 frame_->Push(&answer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007391 }
7392}
7393
7394
ager@chromium.org7c537e22008-10-16 08:43:32 +00007395void CodeGenerator::VisitUnaryOperation(UnaryOperation* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007396 Comment cmnt(masm_, "[ UnaryOperation");
7397
7398 Token::Value op = node->op();
7399
7400 if (op == Token::NOT) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00007401 // Swap the true and false targets but keep the same actual label
7402 // as the fall through.
7403 destination()->Invert();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007404 LoadCondition(node->expression(), destination(), true);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00007405 // Swap the labels back.
7406 destination()->Invert();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007407
7408 } else if (op == Token::DELETE) {
7409 Property* property = node->expression()->AsProperty();
7410 if (property != NULL) {
7411 Load(property->obj());
7412 Load(property->key());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00007413 Result answer = frame_->InvokeBuiltin(Builtins::DELETE, CALL_FUNCTION, 2);
7414 frame_->Push(&answer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007415 return;
7416 }
7417
7418 Variable* variable = node->expression()->AsVariableProxy()->AsVariable();
7419 if (variable != NULL) {
7420 Slot* slot = variable->slot();
7421 if (variable->is_global()) {
7422 LoadGlobal();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00007423 frame_->Push(variable->name());
7424 Result answer = frame_->InvokeBuiltin(Builtins::DELETE,
7425 CALL_FUNCTION, 2);
7426 frame_->Push(&answer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007427 return;
7428
7429 } else if (slot != NULL && slot->type() == Slot::LOOKUP) {
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00007430 // Call the runtime to look up the context holding the named
7431 // variable. Sync the virtual frame eagerly so we can push the
7432 // arguments directly into place.
7433 frame_->SyncRange(0, frame_->element_count() - 1);
7434 frame_->EmitPush(esi);
7435 frame_->EmitPush(Immediate(variable->name()));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00007436 Result context = frame_->CallRuntime(Runtime::kLookupContext, 2);
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00007437 ASSERT(context.is_register());
7438 frame_->EmitPush(context.reg());
7439 context.Unuse();
7440 frame_->EmitPush(Immediate(variable->name()));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00007441 Result answer = frame_->InvokeBuiltin(Builtins::DELETE,
7442 CALL_FUNCTION, 2);
7443 frame_->Push(&answer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007444 return;
7445 }
7446
7447 // Default: Result of deleting non-global, not dynamically
7448 // introduced variables is false.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00007449 frame_->Push(Factory::false_value());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007450
7451 } else {
7452 // Default: Result of deleting expressions is true.
7453 Load(node->expression()); // may have side-effects
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00007454 frame_->SetElementAt(0, Factory::true_value());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007455 }
7456
7457 } else if (op == Token::TYPEOF) {
7458 // Special case for loading the typeof expression; see comment on
7459 // LoadTypeofExpression().
7460 LoadTypeofExpression(node->expression());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00007461 Result answer = frame_->CallRuntime(Runtime::kTypeof, 1);
7462 frame_->Push(&answer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007463
ager@chromium.org381abbb2009-02-25 13:23:22 +00007464 } else if (op == Token::VOID) {
7465 Expression* expression = node->expression();
7466 if (expression && expression->AsLiteral() && (
7467 expression->AsLiteral()->IsTrue() ||
7468 expression->AsLiteral()->IsFalse() ||
7469 expression->AsLiteral()->handle()->IsNumber() ||
7470 expression->AsLiteral()->handle()->IsString() ||
7471 expression->AsLiteral()->handle()->IsJSRegExp() ||
7472 expression->AsLiteral()->IsNull())) {
7473 // Omit evaluating the value of the primitive literal.
7474 // It will be discarded anyway, and can have no side effect.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00007475 frame_->Push(Factory::undefined_value());
ager@chromium.org381abbb2009-02-25 13:23:22 +00007476 } else {
7477 Load(node->expression());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00007478 frame_->SetElementAt(0, Factory::undefined_value());
ager@chromium.org381abbb2009-02-25 13:23:22 +00007479 }
7480
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007481 } else {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007482 if (in_safe_int32_mode()) {
7483 Visit(node->expression());
7484 Result value = frame_->Pop();
7485 ASSERT(value.is_untagged_int32());
7486 // Registers containing an int32 value are not multiply used.
7487 ASSERT(!value.is_register() || !frame_->is_used(value.reg()));
7488 value.ToRegister();
7489 switch (op) {
7490 case Token::SUB: {
7491 __ neg(value.reg());
7492 if (node->no_negative_zero()) {
7493 // -MIN_INT is MIN_INT with the overflow flag set.
7494 unsafe_bailout_->Branch(overflow);
7495 } else {
7496 // MIN_INT and 0 both have bad negations. They both have 31 zeros.
7497 __ test(value.reg(), Immediate(0x7FFFFFFF));
7498 unsafe_bailout_->Branch(zero);
7499 }
7500 break;
7501 }
7502 case Token::BIT_NOT: {
7503 __ not_(value.reg());
7504 break;
7505 }
7506 case Token::ADD: {
7507 // Unary plus has no effect on int32 values.
7508 break;
7509 }
7510 default:
7511 UNREACHABLE();
7512 break;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007513 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007514 frame_->Push(&value);
7515 } else {
7516 Load(node->expression());
7517 bool overwrite =
7518 (node->expression()->AsBinaryOperation() != NULL &&
7519 node->expression()->AsBinaryOperation()->ResultOverwriteAllowed());
7520 switch (op) {
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00007521 case Token::NOT:
7522 case Token::DELETE:
7523 case Token::TYPEOF:
7524 UNREACHABLE(); // handled above
7525 break;
7526
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007527 case Token::SUB: {
7528 GenericUnaryOpStub stub(Token::SUB, overwrite);
7529 Result operand = frame_->Pop();
7530 Result answer = frame_->CallStub(&stub, &operand);
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00007531 answer.set_type_info(TypeInfo::Number());
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007532 frame_->Push(&answer);
7533 break;
7534 }
7535 case Token::BIT_NOT: {
7536 // Smi check.
7537 JumpTarget smi_label;
7538 JumpTarget continue_label;
7539 Result operand = frame_->Pop();
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00007540 TypeInfo operand_info = operand.type_info();
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007541 operand.ToRegister();
7542 if (operand_info.IsSmi()) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00007543 if (FLAG_debug_code) __ AbortIfNotSmi(operand.reg());
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007544 frame_->Spill(operand.reg());
7545 // Set smi tag bit. It will be reset by the not operation.
7546 __ lea(operand.reg(), Operand(operand.reg(), kSmiTagMask));
7547 __ not_(operand.reg());
7548 Result answer = operand;
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00007549 answer.set_type_info(TypeInfo::Smi());
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007550 frame_->Push(&answer);
7551 } else {
7552 __ test(operand.reg(), Immediate(kSmiTagMask));
7553 smi_label.Branch(zero, &operand, taken);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007554
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007555 GenericUnaryOpStub stub(Token::BIT_NOT, overwrite);
7556 Result answer = frame_->CallStub(&stub, &operand);
7557 continue_label.Jump(&answer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007558
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007559 smi_label.Bind(&answer);
7560 answer.ToRegister();
7561 frame_->Spill(answer.reg());
7562 // Set smi tag bit. It will be reset by the not operation.
7563 __ lea(answer.reg(), Operand(answer.reg(), kSmiTagMask));
7564 __ not_(answer.reg());
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007565
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007566 continue_label.Bind(&answer);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00007567 answer.set_type_info(TypeInfo::Integer32());
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007568 frame_->Push(&answer);
7569 }
7570 break;
7571 }
7572 case Token::ADD: {
7573 // Smi check.
7574 JumpTarget continue_label;
7575 Result operand = frame_->Pop();
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00007576 TypeInfo operand_info = operand.type_info();
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007577 operand.ToRegister();
7578 __ test(operand.reg(), Immediate(kSmiTagMask));
7579 continue_label.Branch(zero, &operand, taken);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007580
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007581 frame_->Push(&operand);
7582 Result answer = frame_->InvokeBuiltin(Builtins::TO_NUMBER,
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00007583 CALL_FUNCTION, 1);
mads.s.ager@gmail.com769cc962008-08-06 10:02:49 +00007584
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007585 continue_label.Bind(&answer);
7586 if (operand_info.IsSmi()) {
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00007587 answer.set_type_info(TypeInfo::Smi());
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007588 } else if (operand_info.IsInteger32()) {
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00007589 answer.set_type_info(TypeInfo::Integer32());
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007590 } else {
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00007591 answer.set_type_info(TypeInfo::Number());
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007592 }
7593 frame_->Push(&answer);
7594 break;
7595 }
7596 default:
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007597 UNREACHABLE();
mads.s.ager@gmail.com769cc962008-08-06 10:02:49 +00007598 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007599 }
7600 }
7601}
7602
7603
ager@chromium.orge2902be2009-06-08 12:21:35 +00007604// The value in dst was optimistically incremented or decremented. The
7605// result overflowed or was not smi tagged. Undo the operation, call
7606// into the runtime to convert the argument to a number, and call the
7607// specialized add or subtract stub. The result is left in dst.
7608class DeferredPrefixCountOperation: public DeferredCode {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007609 public:
lrn@chromium.org25156de2010-04-06 13:10:27 +00007610 DeferredPrefixCountOperation(Register dst,
7611 bool is_increment,
7612 TypeInfo input_type)
7613 : dst_(dst), is_increment_(is_increment), input_type_(input_type) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00007614 set_comment("[ DeferredCountOperation");
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007615 }
7616
7617 virtual void Generate();
7618
7619 private:
ager@chromium.orge2902be2009-06-08 12:21:35 +00007620 Register dst_;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007621 bool is_increment_;
lrn@chromium.org25156de2010-04-06 13:10:27 +00007622 TypeInfo input_type_;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007623};
7624
7625
ager@chromium.orge2902be2009-06-08 12:21:35 +00007626void DeferredPrefixCountOperation::Generate() {
kasperl@chromium.org8ccb0be2009-04-07 07:21:39 +00007627 // Undo the optimistic smi operation.
kasperl@chromium.org8ccb0be2009-04-07 07:21:39 +00007628 if (is_increment_) {
ager@chromium.orge2902be2009-06-08 12:21:35 +00007629 __ sub(Operand(dst_), Immediate(Smi::FromInt(1)));
kasperl@chromium.org8ccb0be2009-04-07 07:21:39 +00007630 } else {
ager@chromium.orge2902be2009-06-08 12:21:35 +00007631 __ add(Operand(dst_), Immediate(Smi::FromInt(1)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007632 }
lrn@chromium.org25156de2010-04-06 13:10:27 +00007633 Register left;
7634 if (input_type_.IsNumber()) {
7635 left = dst_;
kasperl@chromium.org8ccb0be2009-04-07 07:21:39 +00007636 } else {
lrn@chromium.org25156de2010-04-06 13:10:27 +00007637 __ push(dst_);
7638 __ InvokeBuiltin(Builtins::TO_NUMBER, CALL_FUNCTION);
7639 left = eax;
kasperl@chromium.org8ccb0be2009-04-07 07:21:39 +00007640 }
lrn@chromium.org25156de2010-04-06 13:10:27 +00007641
7642 GenericBinaryOpStub stub(is_increment_ ? Token::ADD : Token::SUB,
7643 NO_OVERWRITE,
7644 NO_GENERIC_BINARY_FLAGS,
7645 TypeInfo::Number());
7646 stub.GenerateCall(masm_, left, Smi::FromInt(1));
7647
ager@chromium.orge2902be2009-06-08 12:21:35 +00007648 if (!dst_.is(eax)) __ mov(dst_, eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007649}
7650
7651
ager@chromium.orge2902be2009-06-08 12:21:35 +00007652// The value in dst was optimistically incremented or decremented. The
7653// result overflowed or was not smi tagged. Undo the operation and call
7654// into the runtime to convert the argument to a number. Update the
7655// original value in old. Call the specialized add or subtract stub.
7656// The result is left in dst.
7657class DeferredPostfixCountOperation: public DeferredCode {
7658 public:
lrn@chromium.org25156de2010-04-06 13:10:27 +00007659 DeferredPostfixCountOperation(Register dst,
7660 Register old,
7661 bool is_increment,
7662 TypeInfo input_type)
7663 : dst_(dst),
7664 old_(old),
7665 is_increment_(is_increment),
7666 input_type_(input_type) {
ager@chromium.orge2902be2009-06-08 12:21:35 +00007667 set_comment("[ DeferredCountOperation");
7668 }
7669
7670 virtual void Generate();
7671
7672 private:
7673 Register dst_;
7674 Register old_;
7675 bool is_increment_;
lrn@chromium.org25156de2010-04-06 13:10:27 +00007676 TypeInfo input_type_;
ager@chromium.orge2902be2009-06-08 12:21:35 +00007677};
7678
7679
7680void DeferredPostfixCountOperation::Generate() {
7681 // Undo the optimistic smi operation.
7682 if (is_increment_) {
7683 __ sub(Operand(dst_), Immediate(Smi::FromInt(1)));
7684 } else {
7685 __ add(Operand(dst_), Immediate(Smi::FromInt(1)));
7686 }
lrn@chromium.org25156de2010-04-06 13:10:27 +00007687 Register left;
7688 if (input_type_.IsNumber()) {
7689 __ push(dst_); // Save the input to use as the old value.
7690 left = dst_;
ager@chromium.orge2902be2009-06-08 12:21:35 +00007691 } else {
lrn@chromium.org25156de2010-04-06 13:10:27 +00007692 __ push(dst_);
7693 __ InvokeBuiltin(Builtins::TO_NUMBER, CALL_FUNCTION);
7694 __ push(eax); // Save the result of ToNumber to use as the old value.
7695 left = eax;
ager@chromium.orge2902be2009-06-08 12:21:35 +00007696 }
lrn@chromium.org25156de2010-04-06 13:10:27 +00007697
7698 GenericBinaryOpStub stub(is_increment_ ? Token::ADD : Token::SUB,
7699 NO_OVERWRITE,
7700 NO_GENERIC_BINARY_FLAGS,
7701 TypeInfo::Number());
7702 stub.GenerateCall(masm_, left, Smi::FromInt(1));
7703
ager@chromium.orge2902be2009-06-08 12:21:35 +00007704 if (!dst_.is(eax)) __ mov(dst_, eax);
7705 __ pop(old_);
7706}
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00007707
7708
ager@chromium.org7c537e22008-10-16 08:43:32 +00007709void CodeGenerator::VisitCountOperation(CountOperation* node) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007710 ASSERT(!in_safe_int32_mode());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007711 Comment cmnt(masm_, "[ CountOperation");
7712
7713 bool is_postfix = node->is_postfix();
7714 bool is_increment = node->op() == Token::INC;
7715
7716 Variable* var = node->expression()->AsVariableProxy()->AsVariable();
7717 bool is_const = (var != NULL && var->mode() == Variable::CONST);
7718
ager@chromium.orge2902be2009-06-08 12:21:35 +00007719 // Postfix operations need a stack slot under the reference to hold
7720 // the old value while the new value is being stored. This is so that
7721 // in the case that storing the new value requires a call, the old
7722 // value will be in the frame to be spilled.
7723 if (is_postfix) frame_->Push(Smi::FromInt(0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007724
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007725 // A constant reference is not saved to, so a constant reference is not a
7726 // compound assignment reference.
7727 { Reference target(this, node->expression(), !is_const);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00007728 if (target.is_illegal()) {
7729 // Spoof the virtual frame to have the expected height (one higher
7730 // than on entry).
ager@chromium.orge2902be2009-06-08 12:21:35 +00007731 if (!is_postfix) frame_->Push(Smi::FromInt(0));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00007732 return;
7733 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007734 target.TakeValue();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007735
ager@chromium.orge2902be2009-06-08 12:21:35 +00007736 Result new_value = frame_->Pop();
7737 new_value.ToRegister();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007738
ager@chromium.orge2902be2009-06-08 12:21:35 +00007739 Result old_value; // Only allocated in the postfix case.
ager@chromium.org7c537e22008-10-16 08:43:32 +00007740 if (is_postfix) {
ager@chromium.orge2902be2009-06-08 12:21:35 +00007741 // Allocate a temporary to preserve the old value.
7742 old_value = allocator_->Allocate();
kasperl@chromium.org8ccb0be2009-04-07 07:21:39 +00007743 ASSERT(old_value.is_valid());
ager@chromium.orge2902be2009-06-08 12:21:35 +00007744 __ mov(old_value.reg(), new_value.reg());
fschneider@chromium.org5a49b7a2010-03-18 10:06:01 +00007745
lrn@chromium.org25156de2010-04-06 13:10:27 +00007746 // The return value for postfix operations is ToNumber(input).
7747 // Keep more precise type info if the input is some kind of
7748 // number already. If the input is not a number we have to wait
7749 // for the deferred code to convert it.
7750 if (new_value.type_info().IsNumber()) {
7751 old_value.set_type_info(new_value.type_info());
7752 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00007753 }
fschneider@chromium.org5a49b7a2010-03-18 10:06:01 +00007754
ager@chromium.orge2902be2009-06-08 12:21:35 +00007755 // Ensure the new value is writable.
7756 frame_->Spill(new_value.reg());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007757
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007758 Result tmp;
7759 if (new_value.is_smi()) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00007760 if (FLAG_debug_code) __ AbortIfNotSmi(new_value.reg());
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007761 } else {
7762 // We don't know statically if the input is a smi.
7763 // In order to combine the overflow and the smi tag check, we need
7764 // to be able to allocate a byte register. We attempt to do so
7765 // without spilling. If we fail, we will generate separate overflow
7766 // and smi tag checks.
7767 // We allocate and clear a temporary byte register before performing
7768 // the count operation since clearing the register using xor will clear
7769 // the overflow flag.
7770 tmp = allocator_->AllocateByteRegisterWithoutSpilling();
7771 if (tmp.is_valid()) {
7772 __ Set(tmp.reg(), Immediate(0));
7773 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00007774 }
7775
ager@chromium.orge2902be2009-06-08 12:21:35 +00007776 if (is_increment) {
7777 __ add(Operand(new_value.reg()), Immediate(Smi::FromInt(1)));
7778 } else {
7779 __ sub(Operand(new_value.reg()), Immediate(Smi::FromInt(1)));
7780 }
7781
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007782 DeferredCode* deferred = NULL;
7783 if (is_postfix) {
7784 deferred = new DeferredPostfixCountOperation(new_value.reg(),
7785 old_value.reg(),
lrn@chromium.org25156de2010-04-06 13:10:27 +00007786 is_increment,
7787 new_value.type_info());
ager@chromium.orge2902be2009-06-08 12:21:35 +00007788 } else {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007789 deferred = new DeferredPrefixCountOperation(new_value.reg(),
lrn@chromium.org25156de2010-04-06 13:10:27 +00007790 is_increment,
7791 new_value.type_info());
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007792 }
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00007793
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007794 if (new_value.is_smi()) {
7795 // In case we have a smi as input just check for overflow.
7796 deferred->Branch(overflow);
7797 } else {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00007798 // If the count operation didn't overflow and the result is a valid
7799 // smi, we're done. Otherwise, we jump to the deferred slow-case
7800 // code.
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007801 // We combine the overflow and the smi tag check if we could
7802 // successfully allocate a temporary byte register.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00007803 if (tmp.is_valid()) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00007804 __ setcc(overflow, tmp.reg());
7805 __ or_(Operand(tmp.reg()), new_value.reg());
7806 __ test(tmp.reg(), Immediate(kSmiTagMask));
7807 tmp.Unuse();
7808 deferred->Branch(not_zero);
7809 } else {
7810 // Otherwise we test separately for overflow and smi tag.
7811 deferred->Branch(overflow);
7812 __ test(new_value.reg(), Immediate(kSmiTagMask));
7813 deferred->Branch(not_zero);
7814 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00007815 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007816 deferred->BindExit();
7817
lrn@chromium.org25156de2010-04-06 13:10:27 +00007818 // Postfix count operations return their input converted to
7819 // number. The case when the input is already a number is covered
7820 // above in the allocation code for old_value.
7821 if (is_postfix && !new_value.type_info().IsNumber()) {
7822 old_value.set_type_info(TypeInfo::Number());
7823 }
7824
fschneider@chromium.org5a49b7a2010-03-18 10:06:01 +00007825 // The result of ++ or -- is an Integer32 if the
7826 // input is a smi. Otherwise it is a number.
7827 if (new_value.is_smi()) {
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00007828 new_value.set_type_info(TypeInfo::Integer32());
fschneider@chromium.org5a49b7a2010-03-18 10:06:01 +00007829 } else {
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00007830 new_value.set_type_info(TypeInfo::Number());
fschneider@chromium.org5a49b7a2010-03-18 10:06:01 +00007831 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007832
ager@chromium.orge2902be2009-06-08 12:21:35 +00007833 // Postfix: store the old value in the allocated slot under the
7834 // reference.
7835 if (is_postfix) frame_->SetElementAt(target.size(), &old_value);
7836
7837 frame_->Push(&new_value);
7838 // Non-constant: update the reference.
7839 if (!is_const) target.SetValue(NOT_CONST_INIT);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007840 }
7841
ager@chromium.orge2902be2009-06-08 12:21:35 +00007842 // Postfix: drop the new value and use the old.
7843 if (is_postfix) frame_->Drop();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007844}
7845
7846
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007847void CodeGenerator::Int32BinaryOperation(BinaryOperation* node) {
7848 Token::Value op = node->op();
7849 Comment cmnt(masm_, "[ Int32BinaryOperation");
7850 ASSERT(in_safe_int32_mode());
7851 ASSERT(safe_int32_mode_enabled());
7852 ASSERT(FLAG_safe_int32_compiler);
7853
7854 if (op == Token::COMMA) {
7855 // Discard left value.
7856 frame_->Nip(1);
7857 return;
7858 }
7859
7860 Result right = frame_->Pop();
7861 Result left = frame_->Pop();
7862
7863 ASSERT(right.is_untagged_int32());
7864 ASSERT(left.is_untagged_int32());
7865 // Registers containing an int32 value are not multiply used.
7866 ASSERT(!left.is_register() || !frame_->is_used(left.reg()));
7867 ASSERT(!right.is_register() || !frame_->is_used(right.reg()));
7868
7869 switch (op) {
7870 case Token::COMMA:
7871 case Token::OR:
7872 case Token::AND:
7873 UNREACHABLE();
7874 break;
7875 case Token::BIT_OR:
7876 case Token::BIT_XOR:
7877 case Token::BIT_AND:
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007878 if (left.is_constant() || right.is_constant()) {
7879 int32_t value; // Put constant in value, non-constant in left.
7880 // Constants are known to be int32 values, from static analysis,
7881 // or else will be converted to int32 by implicit ECMA [[ToInt32]].
7882 if (left.is_constant()) {
7883 ASSERT(left.handle()->IsSmi() || left.handle()->IsHeapNumber());
7884 value = NumberToInt32(*left.handle());
7885 left = right;
7886 } else {
7887 ASSERT(right.handle()->IsSmi() || right.handle()->IsHeapNumber());
7888 value = NumberToInt32(*right.handle());
7889 }
7890
7891 left.ToRegister();
7892 if (op == Token::BIT_OR) {
7893 __ or_(Operand(left.reg()), Immediate(value));
7894 } else if (op == Token::BIT_XOR) {
7895 __ xor_(Operand(left.reg()), Immediate(value));
7896 } else {
7897 ASSERT(op == Token::BIT_AND);
7898 __ and_(Operand(left.reg()), Immediate(value));
7899 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007900 } else {
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007901 ASSERT(left.is_register());
7902 ASSERT(right.is_register());
7903 if (op == Token::BIT_OR) {
7904 __ or_(left.reg(), Operand(right.reg()));
7905 } else if (op == Token::BIT_XOR) {
7906 __ xor_(left.reg(), Operand(right.reg()));
7907 } else {
7908 ASSERT(op == Token::BIT_AND);
7909 __ and_(left.reg(), Operand(right.reg()));
7910 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007911 }
7912 frame_->Push(&left);
7913 right.Unuse();
7914 break;
7915 case Token::SAR:
7916 case Token::SHL:
7917 case Token::SHR: {
7918 bool test_shr_overflow = false;
7919 left.ToRegister();
7920 if (right.is_constant()) {
7921 ASSERT(right.handle()->IsSmi() || right.handle()->IsHeapNumber());
7922 int shift_amount = NumberToInt32(*right.handle()) & 0x1F;
7923 if (op == Token::SAR) {
7924 __ sar(left.reg(), shift_amount);
7925 } else if (op == Token::SHL) {
7926 __ shl(left.reg(), shift_amount);
7927 } else {
7928 ASSERT(op == Token::SHR);
7929 __ shr(left.reg(), shift_amount);
7930 if (shift_amount == 0) test_shr_overflow = true;
7931 }
7932 } else {
7933 // Move right to ecx
7934 if (left.is_register() && left.reg().is(ecx)) {
7935 right.ToRegister();
7936 __ xchg(left.reg(), right.reg());
7937 left = right; // Left is unused here, copy of right unused by Push.
7938 } else {
7939 right.ToRegister(ecx);
7940 left.ToRegister();
7941 }
7942 if (op == Token::SAR) {
7943 __ sar_cl(left.reg());
7944 } else if (op == Token::SHL) {
7945 __ shl_cl(left.reg());
7946 } else {
7947 ASSERT(op == Token::SHR);
7948 __ shr_cl(left.reg());
7949 test_shr_overflow = true;
7950 }
7951 }
7952 {
7953 Register left_reg = left.reg();
7954 frame_->Push(&left);
7955 right.Unuse();
7956 if (test_shr_overflow && !node->to_int32()) {
7957 // Uint32 results with top bit set are not Int32 values.
7958 // If they will be forced to Int32, skip the test.
7959 // Test is needed because shr with shift amount 0 does not set flags.
7960 __ test(left_reg, Operand(left_reg));
7961 unsafe_bailout_->Branch(sign);
7962 }
7963 }
7964 break;
7965 }
7966 case Token::ADD:
7967 case Token::SUB:
7968 case Token::MUL:
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007969 if ((left.is_constant() && op != Token::SUB) || right.is_constant()) {
7970 int32_t value; // Put constant in value, non-constant in left.
7971 if (right.is_constant()) {
7972 ASSERT(right.handle()->IsSmi() || right.handle()->IsHeapNumber());
7973 value = NumberToInt32(*right.handle());
7974 } else {
7975 ASSERT(left.handle()->IsSmi() || left.handle()->IsHeapNumber());
7976 value = NumberToInt32(*left.handle());
7977 left = right;
7978 }
7979
7980 left.ToRegister();
7981 if (op == Token::ADD) {
7982 __ add(Operand(left.reg()), Immediate(value));
7983 } else if (op == Token::SUB) {
7984 __ sub(Operand(left.reg()), Immediate(value));
7985 } else {
7986 ASSERT(op == Token::MUL);
7987 __ imul(left.reg(), left.reg(), value);
7988 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007989 } else {
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007990 left.ToRegister();
7991 ASSERT(left.is_register());
7992 ASSERT(right.is_register());
7993 if (op == Token::ADD) {
7994 __ add(left.reg(), Operand(right.reg()));
7995 } else if (op == Token::SUB) {
7996 __ sub(left.reg(), Operand(right.reg()));
7997 } else {
7998 ASSERT(op == Token::MUL);
7999 // We have statically verified that a negative zero can be ignored.
8000 __ imul(left.reg(), Operand(right.reg()));
8001 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00008002 }
8003 right.Unuse();
8004 frame_->Push(&left);
8005 if (!node->to_int32()) {
8006 // If ToInt32 is called on the result of ADD, SUB, or MUL, we don't
8007 // care about overflows.
8008 unsafe_bailout_->Branch(overflow);
8009 }
8010 break;
8011 case Token::DIV:
8012 case Token::MOD: {
8013 if (right.is_register() && (right.reg().is(eax) || right.reg().is(edx))) {
8014 if (left.is_register() && left.reg().is(edi)) {
8015 right.ToRegister(ebx);
8016 } else {
8017 right.ToRegister(edi);
8018 }
8019 }
8020 left.ToRegister(eax);
8021 Result edx_reg = allocator_->Allocate(edx);
8022 right.ToRegister();
8023 // The results are unused here because BreakTarget::Branch cannot handle
8024 // live results.
8025 Register right_reg = right.reg();
8026 left.Unuse();
8027 right.Unuse();
8028 edx_reg.Unuse();
8029 __ cmp(right_reg, 0);
8030 // Ensure divisor is positive: no chance of non-int32 or -0 result.
8031 unsafe_bailout_->Branch(less_equal);
8032 __ cdq(); // Sign-extend eax into edx:eax
8033 __ idiv(right_reg);
8034 if (op == Token::MOD) {
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00008035 // Negative zero can arise as a negative divident with a zero result.
8036 if (!node->no_negative_zero()) {
8037 Label not_negative_zero;
8038 __ test(edx, Operand(edx));
8039 __ j(not_zero, &not_negative_zero);
8040 __ test(eax, Operand(eax));
8041 unsafe_bailout_->Branch(negative);
8042 __ bind(&not_negative_zero);
8043 }
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00008044 Result edx_result(edx, TypeInfo::Integer32());
fschneider@chromium.org086aac62010-03-17 13:18:24 +00008045 edx_result.set_untagged_int32(true);
8046 frame_->Push(&edx_result);
8047 } else {
8048 ASSERT(op == Token::DIV);
8049 __ test(edx, Operand(edx));
8050 unsafe_bailout_->Branch(not_equal);
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00008051 Result eax_result(eax, TypeInfo::Integer32());
fschneider@chromium.org086aac62010-03-17 13:18:24 +00008052 eax_result.set_untagged_int32(true);
8053 frame_->Push(&eax_result);
8054 }
8055 break;
8056 }
8057 default:
8058 UNREACHABLE();
8059 break;
8060 }
8061}
8062
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008063
ager@chromium.orgb26c50a2010-03-26 09:27:16 +00008064void CodeGenerator::GenerateLogicalBooleanOperation(BinaryOperation* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008065 // According to ECMA-262 section 11.11, page 58, the binary logical
8066 // operators must yield the result of one of the two expressions
8067 // before any ToBoolean() conversions. This means that the value
8068 // produced by a && or || operator is not necessarily a boolean.
8069
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008070 // NOTE: If the left hand side produces a materialized value (not
8071 // control flow), we force the right hand side to do the same. This
8072 // is necessary because we assume that if we get control flow on the
8073 // last path out of an expression we got it on all paths.
ager@chromium.orgb26c50a2010-03-26 09:27:16 +00008074 if (node->op() == Token::AND) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00008075 ASSERT(!in_safe_int32_mode());
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008076 JumpTarget is_true;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008077 ControlDestination dest(&is_true, destination()->false_target(), true);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00008078 LoadCondition(node->left(), &dest, false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008079
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008080 if (dest.false_was_fall_through()) {
8081 // The current false target was used as the fall-through. If
8082 // there are no dangling jumps to is_true then the left
8083 // subexpression was unconditionally false. Otherwise we have
8084 // paths where we do have to evaluate the right subexpression.
8085 if (is_true.is_linked()) {
8086 // We need to compile the right subexpression. If the jump to
8087 // the current false target was a forward jump then we have a
8088 // valid frame, we have just bound the false target, and we
8089 // have to jump around the code for the right subexpression.
8090 if (has_valid_frame()) {
8091 destination()->false_target()->Unuse();
8092 destination()->false_target()->Jump();
8093 }
8094 is_true.Bind();
8095 // The left subexpression compiled to control flow, so the
8096 // right one is free to do so as well.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00008097 LoadCondition(node->right(), destination(), false);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008098 } else {
8099 // We have actually just jumped to or bound the current false
8100 // target but the current control destination is not marked as
8101 // used.
8102 destination()->Use(false);
8103 }
8104
8105 } else if (dest.is_used()) {
8106 // The left subexpression compiled to control flow (and is_true
8107 // was just bound), so the right is free to do so as well.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00008108 LoadCondition(node->right(), destination(), false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008109
8110 } else {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008111 // We have a materialized value on the frame, so we exit with
8112 // one on all paths. There are possibly also jumps to is_true
8113 // from nested subexpressions.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008114 JumpTarget pop_and_continue;
8115 JumpTarget exit;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008116
8117 // Avoid popping the result if it converts to 'false' using the
8118 // standard ToBoolean() conversion as described in ECMA-262,
8119 // section 9.2, page 30.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008120 //
8121 // Duplicate the TOS value. The duplicate will be popped by
8122 // ToBoolean.
8123 frame_->Dup();
8124 ControlDestination dest(&pop_and_continue, &exit, true);
8125 ToBoolean(&dest);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008126
8127 // Pop the result of evaluating the first part.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008128 frame_->Drop();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008129
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008130 // Compile right side expression.
8131 is_true.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008132 Load(node->right());
8133
8134 // Exit (always with a materialized value).
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008135 exit.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008136 }
8137
ager@chromium.orgb26c50a2010-03-26 09:27:16 +00008138 } else {
8139 ASSERT(node->op() == Token::OR);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00008140 ASSERT(!in_safe_int32_mode());
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008141 JumpTarget is_false;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008142 ControlDestination dest(destination()->true_target(), &is_false, false);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00008143 LoadCondition(node->left(), &dest, false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008144
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008145 if (dest.true_was_fall_through()) {
8146 // The current true target was used as the fall-through. If
8147 // there are no dangling jumps to is_false then the left
8148 // subexpression was unconditionally true. Otherwise we have
8149 // paths where we do have to evaluate the right subexpression.
8150 if (is_false.is_linked()) {
8151 // We need to compile the right subexpression. If the jump to
8152 // the current true target was a forward jump then we have a
8153 // valid frame, we have just bound the true target, and we
8154 // have to jump around the code for the right subexpression.
8155 if (has_valid_frame()) {
8156 destination()->true_target()->Unuse();
8157 destination()->true_target()->Jump();
8158 }
8159 is_false.Bind();
8160 // The left subexpression compiled to control flow, so the
8161 // right one is free to do so as well.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00008162 LoadCondition(node->right(), destination(), false);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008163 } else {
8164 // We have just jumped to or bound the current true target but
8165 // the current control destination is not marked as used.
8166 destination()->Use(true);
8167 }
8168
8169 } else if (dest.is_used()) {
8170 // The left subexpression compiled to control flow (and is_false
8171 // was just bound), so the right is free to do so as well.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00008172 LoadCondition(node->right(), destination(), false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008173
8174 } else {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008175 // We have a materialized value on the frame, so we exit with
8176 // one on all paths. There are possibly also jumps to is_false
8177 // from nested subexpressions.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008178 JumpTarget pop_and_continue;
8179 JumpTarget exit;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008180
8181 // Avoid popping the result if it converts to 'true' using the
8182 // standard ToBoolean() conversion as described in ECMA-262,
8183 // section 9.2, page 30.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008184 //
8185 // Duplicate the TOS value. The duplicate will be popped by
8186 // ToBoolean.
8187 frame_->Dup();
8188 ControlDestination dest(&exit, &pop_and_continue, false);
8189 ToBoolean(&dest);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008190
8191 // Pop the result of evaluating the first part.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008192 frame_->Drop();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008193
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008194 // Compile right side expression.
8195 is_false.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008196 Load(node->right());
8197
8198 // Exit (always with a materialized value).
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008199 exit.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008200 }
ager@chromium.orgb26c50a2010-03-26 09:27:16 +00008201 }
8202}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008203
ager@chromium.orgb26c50a2010-03-26 09:27:16 +00008204
8205void CodeGenerator::VisitBinaryOperation(BinaryOperation* node) {
8206 Comment cmnt(masm_, "[ BinaryOperation");
8207
8208 if (node->op() == Token::AND || node->op() == Token::OR) {
8209 GenerateLogicalBooleanOperation(node);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00008210 } else if (in_safe_int32_mode()) {
8211 Visit(node->left());
8212 Visit(node->right());
8213 Int32BinaryOperation(node);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008214 } else {
kasper.lund7276f142008-07-30 08:49:36 +00008215 // NOTE: The code below assumes that the slow cases (calls to runtime)
8216 // never return a constant/immutable object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008217 OverwriteMode overwrite_mode = NO_OVERWRITE;
8218 if (node->left()->AsBinaryOperation() != NULL &&
8219 node->left()->AsBinaryOperation()->ResultOverwriteAllowed()) {
8220 overwrite_mode = OVERWRITE_LEFT;
8221 } else if (node->right()->AsBinaryOperation() != NULL &&
8222 node->right()->AsBinaryOperation()->ResultOverwriteAllowed()) {
8223 overwrite_mode = OVERWRITE_RIGHT;
8224 }
8225
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00008226 if (node->left()->IsTrivial()) {
8227 Load(node->right());
8228 Result right = frame_->Pop();
8229 frame_->Push(node->left());
8230 frame_->Push(&right);
8231 } else {
8232 Load(node->left());
8233 Load(node->right());
8234 }
lrn@chromium.org25156de2010-04-06 13:10:27 +00008235 GenericBinaryOperation(node, overwrite_mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008236 }
8237}
8238
8239
ager@chromium.org7c537e22008-10-16 08:43:32 +00008240void CodeGenerator::VisitThisFunction(ThisFunction* node) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00008241 ASSERT(!in_safe_int32_mode());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008242 frame_->PushFunction();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008243}
8244
8245
ager@chromium.org7c537e22008-10-16 08:43:32 +00008246void CodeGenerator::VisitCompareOperation(CompareOperation* node) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00008247 ASSERT(!in_safe_int32_mode());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008248 Comment cmnt(masm_, "[ CompareOperation");
8249
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00008250 bool left_already_loaded = false;
8251
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008252 // Get the expressions from the node.
8253 Expression* left = node->left();
8254 Expression* right = node->right();
8255 Token::Value op = node->op();
ager@chromium.org3bf7b912008-11-17 09:09:45 +00008256 // To make typeof testing for natives implemented in JavaScript really
8257 // efficient, we generate special code for expressions of the form:
8258 // 'typeof <expression> == <string>'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008259 UnaryOperation* operation = left->AsUnaryOperation();
8260 if ((op == Token::EQ || op == Token::EQ_STRICT) &&
8261 (operation != NULL && operation->op() == Token::TYPEOF) &&
8262 (right->AsLiteral() != NULL &&
8263 right->AsLiteral()->handle()->IsString())) {
8264 Handle<String> check(String::cast(*right->AsLiteral()->handle()));
8265
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008266 // Load the operand and move it to a register.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008267 LoadTypeofExpression(operation->expression());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008268 Result answer = frame_->Pop();
8269 answer.ToRegister();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008270
8271 if (check->Equals(Heap::number_symbol())) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008272 __ test(answer.reg(), Immediate(kSmiTagMask));
8273 destination()->true_target()->Branch(zero);
8274 frame_->Spill(answer.reg());
8275 __ mov(answer.reg(), FieldOperand(answer.reg(), HeapObject::kMapOffset));
8276 __ cmp(answer.reg(), Factory::heap_number_map());
8277 answer.Unuse();
8278 destination()->Split(equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008279
8280 } else if (check->Equals(Heap::string_symbol())) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008281 __ test(answer.reg(), Immediate(kSmiTagMask));
8282 destination()->false_target()->Branch(zero);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008283
ager@chromium.org3bf7b912008-11-17 09:09:45 +00008284 // It can be an undetectable string object.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008285 Result temp = allocator()->Allocate();
8286 ASSERT(temp.is_valid());
8287 __ mov(temp.reg(), FieldOperand(answer.reg(), HeapObject::kMapOffset));
8288 __ movzx_b(temp.reg(), FieldOperand(temp.reg(), Map::kBitFieldOffset));
8289 __ test(temp.reg(), Immediate(1 << Map::kIsUndetectable));
8290 destination()->false_target()->Branch(not_zero);
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00008291 __ CmpObjectType(answer.reg(), FIRST_NONSTRING_TYPE, temp.reg());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008292 temp.Unuse();
8293 answer.Unuse();
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00008294 destination()->Split(below);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008295
8296 } else if (check->Equals(Heap::boolean_symbol())) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008297 __ cmp(answer.reg(), Factory::true_value());
8298 destination()->true_target()->Branch(equal);
8299 __ cmp(answer.reg(), Factory::false_value());
8300 answer.Unuse();
8301 destination()->Split(equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008302
8303 } else if (check->Equals(Heap::undefined_symbol())) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008304 __ cmp(answer.reg(), Factory::undefined_value());
8305 destination()->true_target()->Branch(equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008306
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008307 __ test(answer.reg(), Immediate(kSmiTagMask));
8308 destination()->false_target()->Branch(zero);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008309
ager@chromium.org3bf7b912008-11-17 09:09:45 +00008310 // It can be an undetectable object.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008311 frame_->Spill(answer.reg());
8312 __ mov(answer.reg(), FieldOperand(answer.reg(), HeapObject::kMapOffset));
8313 __ movzx_b(answer.reg(),
8314 FieldOperand(answer.reg(), Map::kBitFieldOffset));
8315 __ test(answer.reg(), Immediate(1 << Map::kIsUndetectable));
8316 answer.Unuse();
8317 destination()->Split(not_zero);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008318
8319 } else if (check->Equals(Heap::function_symbol())) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008320 __ test(answer.reg(), Immediate(kSmiTagMask));
8321 destination()->false_target()->Branch(zero);
8322 frame_->Spill(answer.reg());
8323 __ CmpObjectType(answer.reg(), JS_FUNCTION_TYPE, answer.reg());
ager@chromium.orgc4c92722009-11-18 14:12:51 +00008324 destination()->true_target()->Branch(equal);
8325 // Regular expressions are callable so typeof == 'function'.
8326 __ CmpInstanceType(answer.reg(), JS_REGEXP_TYPE);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008327 answer.Unuse();
8328 destination()->Split(equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008329 } else if (check->Equals(Heap::object_symbol())) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008330 __ test(answer.reg(), Immediate(kSmiTagMask));
8331 destination()->false_target()->Branch(zero);
8332 __ cmp(answer.reg(), Factory::null_value());
8333 destination()->true_target()->Branch(equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008334
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008335 Result map = allocator()->Allocate();
8336 ASSERT(map.is_valid());
ager@chromium.orgc4c92722009-11-18 14:12:51 +00008337 // Regular expressions are typeof == 'function', not 'object'.
8338 __ CmpObjectType(answer.reg(), JS_REGEXP_TYPE, map.reg());
8339 destination()->false_target()->Branch(equal);
8340
8341 // It can be an undetectable object.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008342 __ movzx_b(map.reg(), FieldOperand(map.reg(), Map::kBitFieldOffset));
8343 __ test(map.reg(), Immediate(1 << Map::kIsUndetectable));
8344 destination()->false_target()->Branch(not_zero);
8345 __ mov(map.reg(), FieldOperand(answer.reg(), HeapObject::kMapOffset));
8346 __ movzx_b(map.reg(), FieldOperand(map.reg(), Map::kInstanceTypeOffset));
8347 __ cmp(map.reg(), FIRST_JS_OBJECT_TYPE);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00008348 destination()->false_target()->Branch(below);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008349 __ cmp(map.reg(), LAST_JS_OBJECT_TYPE);
8350 answer.Unuse();
8351 map.Unuse();
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00008352 destination()->Split(below_equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008353 } else {
ager@chromium.org3bf7b912008-11-17 09:09:45 +00008354 // Uncommon case: typeof testing against a string literal that is
8355 // never returned from the typeof operator.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008356 answer.Unuse();
8357 destination()->Goto(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008358 }
8359 return;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00008360 } else if (op == Token::LT &&
8361 right->AsLiteral() != NULL &&
8362 right->AsLiteral()->handle()->IsHeapNumber()) {
8363 Handle<HeapNumber> check(HeapNumber::cast(*right->AsLiteral()->handle()));
8364 if (check->value() == 2147483648.0) { // 0x80000000.
8365 Load(left);
8366 left_already_loaded = true;
8367 Result lhs = frame_->Pop();
8368 lhs.ToRegister();
8369 __ test(lhs.reg(), Immediate(kSmiTagMask));
8370 destination()->true_target()->Branch(zero); // All Smis are less.
8371 Result scratch = allocator()->Allocate();
8372 ASSERT(scratch.is_valid());
8373 __ mov(scratch.reg(), FieldOperand(lhs.reg(), HeapObject::kMapOffset));
8374 __ cmp(scratch.reg(), Factory::heap_number_map());
8375 JumpTarget not_a_number;
8376 not_a_number.Branch(not_equal, &lhs);
8377 __ mov(scratch.reg(),
8378 FieldOperand(lhs.reg(), HeapNumber::kExponentOffset));
8379 __ cmp(Operand(scratch.reg()), Immediate(0xfff00000));
8380 not_a_number.Branch(above_equal, &lhs); // It's a negative NaN or -Inf.
8381 const uint32_t borderline_exponent =
8382 (HeapNumber::kExponentBias + 31) << HeapNumber::kExponentShift;
8383 __ cmp(Operand(scratch.reg()), Immediate(borderline_exponent));
8384 scratch.Unuse();
8385 lhs.Unuse();
8386 destination()->true_target()->Branch(less);
8387 destination()->false_target()->Jump();
8388
8389 not_a_number.Bind(&lhs);
8390 frame_->Push(&lhs);
8391 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008392 }
8393
8394 Condition cc = no_condition;
8395 bool strict = false;
8396 switch (op) {
8397 case Token::EQ_STRICT:
8398 strict = true;
8399 // Fall through
8400 case Token::EQ:
8401 cc = equal;
8402 break;
8403 case Token::LT:
8404 cc = less;
8405 break;
8406 case Token::GT:
8407 cc = greater;
8408 break;
8409 case Token::LTE:
8410 cc = less_equal;
8411 break;
8412 case Token::GTE:
8413 cc = greater_equal;
8414 break;
8415 case Token::IN: {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00008416 if (!left_already_loaded) Load(left);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008417 Load(right);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008418 Result answer = frame_->InvokeBuiltin(Builtins::IN, CALL_FUNCTION, 2);
8419 frame_->Push(&answer); // push the result
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008420 return;
8421 }
8422 case Token::INSTANCEOF: {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00008423 if (!left_already_loaded) Load(left);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008424 Load(right);
ager@chromium.org7c537e22008-10-16 08:43:32 +00008425 InstanceofStub stub;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008426 Result answer = frame_->CallStub(&stub, 2);
8427 answer.ToRegister();
8428 __ test(answer.reg(), Operand(answer.reg()));
8429 answer.Unuse();
8430 destination()->Split(zero);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008431 return;
8432 }
8433 default:
8434 UNREACHABLE();
8435 }
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00008436
8437 if (left->IsTrivial()) {
8438 if (!left_already_loaded) {
8439 Load(right);
8440 Result right_result = frame_->Pop();
8441 frame_->Push(left);
8442 frame_->Push(&right_result);
8443 } else {
8444 Load(right);
8445 }
8446 } else {
8447 if (!left_already_loaded) Load(left);
8448 Load(right);
8449 }
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00008450 Comparison(node, cc, strict, destination());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008451}
8452
8453
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008454#ifdef DEBUG
8455bool CodeGenerator::HasValidEntryRegisters() {
ager@chromium.org41826e72009-03-30 13:30:57 +00008456 return (allocator()->count(eax) == (frame()->is_used(eax) ? 1 : 0))
8457 && (allocator()->count(ebx) == (frame()->is_used(ebx) ? 1 : 0))
8458 && (allocator()->count(ecx) == (frame()->is_used(ecx) ? 1 : 0))
8459 && (allocator()->count(edx) == (frame()->is_used(edx) ? 1 : 0))
8460 && (allocator()->count(edi) == (frame()->is_used(edi) ? 1 : 0));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008461}
8462#endif
8463
8464
ager@chromium.orge2902be2009-06-08 12:21:35 +00008465// Emit a LoadIC call to get the value from receiver and leave it in
ager@chromium.org5c838252010-02-19 08:53:10 +00008466// dst.
ager@chromium.org5ec48922009-05-05 07:25:34 +00008467class DeferredReferenceGetNamedValue: public DeferredCode {
8468 public:
ager@chromium.orge2902be2009-06-08 12:21:35 +00008469 DeferredReferenceGetNamedValue(Register dst,
8470 Register receiver,
8471 Handle<String> name)
8472 : dst_(dst), receiver_(receiver), name_(name) {
ager@chromium.org5ec48922009-05-05 07:25:34 +00008473 set_comment("[ DeferredReferenceGetNamedValue");
8474 }
8475
8476 virtual void Generate();
8477
8478 Label* patch_site() { return &patch_site_; }
8479
8480 private:
8481 Label patch_site_;
ager@chromium.orge2902be2009-06-08 12:21:35 +00008482 Register dst_;
8483 Register receiver_;
ager@chromium.org5ec48922009-05-05 07:25:34 +00008484 Handle<String> name_;
8485};
8486
8487
ager@chromium.orge2902be2009-06-08 12:21:35 +00008488void DeferredReferenceGetNamedValue::Generate() {
ager@chromium.org5c838252010-02-19 08:53:10 +00008489 if (!receiver_.is(eax)) {
8490 __ mov(eax, receiver_);
8491 }
ager@chromium.orge2902be2009-06-08 12:21:35 +00008492 __ Set(ecx, Immediate(name_));
8493 Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize));
8494 __ call(ic, RelocInfo::CODE_TARGET);
8495 // The call must be followed by a test eax instruction to indicate
8496 // that the inobject property case was inlined.
8497 //
8498 // Store the delta to the map check instruction here in the test
8499 // instruction. Use masm_-> instead of the __ macro since the
8500 // latter can't return a value.
8501 int delta_to_patch_site = masm_->SizeOfCodeGeneratedSince(patch_site());
8502 // Here we use masm_-> instead of the __ macro because this is the
8503 // instruction that gets patched and coverage code gets in the way.
8504 masm_->test(eax, Immediate(-delta_to_patch_site));
8505 __ IncrementCounter(&Counters::named_load_inline_miss, 1);
8506
8507 if (!dst_.is(eax)) __ mov(dst_, eax);
ager@chromium.orge2902be2009-06-08 12:21:35 +00008508}
8509
8510
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00008511class DeferredReferenceGetKeyedValue: public DeferredCode {
8512 public:
ager@chromium.orge2902be2009-06-08 12:21:35 +00008513 explicit DeferredReferenceGetKeyedValue(Register dst,
8514 Register receiver,
ager@chromium.org5c838252010-02-19 08:53:10 +00008515 Register key)
8516 : dst_(dst), receiver_(receiver), key_(key) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00008517 set_comment("[ DeferredReferenceGetKeyedValue");
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008518 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00008519
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008520 virtual void Generate();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00008521
8522 Label* patch_site() { return &patch_site_; }
8523
8524 private:
8525 Label patch_site_;
ager@chromium.orge2902be2009-06-08 12:21:35 +00008526 Register dst_;
8527 Register receiver_;
8528 Register key_;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00008529};
8530
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008531
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008532void DeferredReferenceGetKeyedValue::Generate() {
ager@chromium.org5c838252010-02-19 08:53:10 +00008533 if (!receiver_.is(eax)) {
8534 // Register eax is available for key.
8535 if (!key_.is(eax)) {
8536 __ mov(eax, key_);
8537 }
8538 if (!receiver_.is(edx)) {
8539 __ mov(edx, receiver_);
8540 }
8541 } else if (!key_.is(edx)) {
8542 // Register edx is available for receiver.
8543 if (!receiver_.is(edx)) {
8544 __ mov(edx, receiver_);
8545 }
8546 if (!key_.is(eax)) {
8547 __ mov(eax, key_);
8548 }
8549 } else {
8550 __ xchg(edx, eax);
8551 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008552 // Calculate the delta from the IC call instruction to the map check
8553 // cmp instruction in the inlined version. This delta is stored in
8554 // a test(eax, delta) instruction after the call so that we can find
8555 // it in the IC initialization code and patch the cmp instruction.
8556 // This means that we cannot allow test instructions after calls to
8557 // KeyedLoadIC stubs in other places.
ager@chromium.orge2902be2009-06-08 12:21:35 +00008558 Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
ager@chromium.org5c838252010-02-19 08:53:10 +00008559 __ call(ic, RelocInfo::CODE_TARGET);
ager@chromium.orge2902be2009-06-08 12:21:35 +00008560 // The delta from the start of the map-compare instruction to the
8561 // test instruction. We use masm_-> directly here instead of the __
8562 // macro because the macro sometimes uses macro expansion to turn
8563 // into something that can't return a value. This is encountered
8564 // when doing generated code coverage tests.
8565 int delta_to_patch_site = masm_->SizeOfCodeGeneratedSince(patch_site());
8566 // Here we use masm_-> instead of the __ macro because this is the
8567 // instruction that gets patched and coverage code gets in the way.
8568 masm_->test(eax, Immediate(-delta_to_patch_site));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008569 __ IncrementCounter(&Counters::keyed_load_inline_miss, 1);
8570
ager@chromium.orge2902be2009-06-08 12:21:35 +00008571 if (!dst_.is(eax)) __ mov(dst_, eax);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008572}
8573
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008574
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008575class DeferredReferenceSetKeyedValue: public DeferredCode {
8576 public:
8577 DeferredReferenceSetKeyedValue(Register value,
8578 Register key,
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00008579 Register receiver,
8580 Register scratch)
8581 : value_(value),
8582 key_(key),
8583 receiver_(receiver),
8584 scratch_(scratch) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008585 set_comment("[ DeferredReferenceSetKeyedValue");
8586 }
8587
8588 virtual void Generate();
8589
8590 Label* patch_site() { return &patch_site_; }
8591
8592 private:
8593 Register value_;
8594 Register key_;
8595 Register receiver_;
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00008596 Register scratch_;
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008597 Label patch_site_;
8598};
8599
8600
8601void DeferredReferenceSetKeyedValue::Generate() {
8602 __ IncrementCounter(&Counters::keyed_store_inline_miss, 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00008603 // Move value_ to eax, key_ to ecx, and receiver_ to edx.
8604 Register old_value = value_;
8605
8606 // First, move value to eax.
8607 if (!value_.is(eax)) {
8608 if (key_.is(eax)) {
8609 // Move key_ out of eax, preferably to ecx.
8610 if (!value_.is(ecx) && !receiver_.is(ecx)) {
8611 __ mov(ecx, key_);
8612 key_ = ecx;
8613 } else {
8614 __ mov(scratch_, key_);
8615 key_ = scratch_;
8616 }
8617 }
8618 if (receiver_.is(eax)) {
8619 // Move receiver_ out of eax, preferably to edx.
8620 if (!value_.is(edx) && !key_.is(edx)) {
8621 __ mov(edx, receiver_);
8622 receiver_ = edx;
8623 } else {
8624 // Both moves to scratch are from eax, also, no valid path hits both.
8625 __ mov(scratch_, receiver_);
8626 receiver_ = scratch_;
8627 }
8628 }
8629 __ mov(eax, value_);
8630 value_ = eax;
8631 }
8632
8633 // Now value_ is in eax. Move the other two to the right positions.
8634 // We do not update the variables key_ and receiver_ to ecx and edx.
8635 if (key_.is(ecx)) {
8636 if (!receiver_.is(edx)) {
8637 __ mov(edx, receiver_);
8638 }
8639 } else if (key_.is(edx)) {
8640 if (receiver_.is(ecx)) {
8641 __ xchg(edx, ecx);
8642 } else {
8643 __ mov(ecx, key_);
8644 if (!receiver_.is(edx)) {
8645 __ mov(edx, receiver_);
8646 }
8647 }
8648 } else { // Key is not in edx or ecx.
8649 if (!receiver_.is(edx)) {
8650 __ mov(edx, receiver_);
8651 }
8652 __ mov(ecx, key_);
8653 }
8654
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008655 // Call the IC stub.
8656 Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize));
8657 __ call(ic, RelocInfo::CODE_TARGET);
8658 // The delta from the start of the map-compare instruction to the
8659 // test instruction. We use masm_-> directly here instead of the
8660 // __ macro because the macro sometimes uses macro expansion to turn
8661 // into something that can't return a value. This is encountered
8662 // when doing generated code coverage tests.
8663 int delta_to_patch_site = masm_->SizeOfCodeGeneratedSince(patch_site());
8664 // Here we use masm_-> instead of the __ macro because this is the
8665 // instruction that gets patched and coverage code gets in the way.
8666 masm_->test(eax, Immediate(-delta_to_patch_site));
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00008667 // Restore value (returned from store IC) register.
8668 if (!old_value.is(eax)) __ mov(old_value, eax);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008669}
8670
8671
ager@chromium.org5c838252010-02-19 08:53:10 +00008672Result CodeGenerator::EmitNamedLoad(Handle<String> name, bool is_contextual) {
8673#ifdef DEBUG
8674 int original_height = frame()->height();
8675#endif
8676 Result result;
8677 // Do not inline the inobject property case for loads from the global
8678 // object. Also do not inline for unoptimized code. This saves time in
8679 // the code generator. Unoptimized code is toplevel code or code that is
8680 // not in a loop.
8681 if (is_contextual || scope()->is_global_scope() || loop_nesting() == 0) {
8682 Comment cmnt(masm(), "[ Load from named Property");
8683 frame()->Push(name);
8684
8685 RelocInfo::Mode mode = is_contextual
8686 ? RelocInfo::CODE_TARGET_CONTEXT
8687 : RelocInfo::CODE_TARGET;
8688 result = frame()->CallLoadIC(mode);
8689 // A test eax instruction following the call signals that the inobject
8690 // property case was inlined. Ensure that there is not a test eax
8691 // instruction here.
8692 __ nop();
8693 } else {
8694 // Inline the inobject property case.
8695 Comment cmnt(masm(), "[ Inlined named property load");
8696 Result receiver = frame()->Pop();
8697 receiver.ToRegister();
8698
8699 result = allocator()->Allocate();
8700 ASSERT(result.is_valid());
8701 DeferredReferenceGetNamedValue* deferred =
8702 new DeferredReferenceGetNamedValue(result.reg(), receiver.reg(), name);
8703
8704 // Check that the receiver is a heap object.
8705 __ test(receiver.reg(), Immediate(kSmiTagMask));
8706 deferred->Branch(zero);
8707
8708 __ bind(deferred->patch_site());
8709 // This is the map check instruction that will be patched (so we can't
8710 // use the double underscore macro that may insert instructions).
8711 // Initially use an invalid map to force a failure.
8712 masm()->cmp(FieldOperand(receiver.reg(), HeapObject::kMapOffset),
8713 Immediate(Factory::null_value()));
8714 // This branch is always a forwards branch so it's always a fixed size
8715 // which allows the assert below to succeed and patching to work.
8716 deferred->Branch(not_equal);
8717
8718 // The delta from the patch label to the load offset must be statically
8719 // known.
8720 ASSERT(masm()->SizeOfCodeGeneratedSince(deferred->patch_site()) ==
8721 LoadIC::kOffsetToLoadInstruction);
8722 // The initial (invalid) offset has to be large enough to force a 32-bit
8723 // instruction encoding to allow patching with an arbitrary offset. Use
8724 // kMaxInt (minus kHeapObjectTag).
8725 int offset = kMaxInt;
8726 masm()->mov(result.reg(), FieldOperand(receiver.reg(), offset));
8727
8728 __ IncrementCounter(&Counters::named_load_inline, 1);
8729 deferred->BindExit();
8730 }
8731 ASSERT(frame()->height() == original_height - 1);
8732 return result;
8733}
8734
8735
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00008736Result CodeGenerator::EmitNamedStore(Handle<String> name, bool is_contextual) {
ager@chromium.org5c838252010-02-19 08:53:10 +00008737#ifdef DEBUG
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00008738 int expected_height = frame()->height() - (is_contextual ? 1 : 2);
ager@chromium.org5c838252010-02-19 08:53:10 +00008739#endif
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00008740 Result result = frame()->CallStoreIC(name, is_contextual);
ager@chromium.org5c838252010-02-19 08:53:10 +00008741
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00008742 ASSERT_EQ(expected_height, frame()->height());
ager@chromium.org5c838252010-02-19 08:53:10 +00008743 return result;
8744}
8745
8746
8747Result CodeGenerator::EmitKeyedLoad() {
8748#ifdef DEBUG
8749 int original_height = frame()->height();
8750#endif
8751 Result result;
8752 // Inline array load code if inside of a loop. We do not know the
8753 // receiver map yet, so we initially generate the code with a check
8754 // against an invalid map. In the inline cache code, we patch the map
8755 // check if appropriate.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008756 if (loop_nesting() > 0) {
8757 Comment cmnt(masm_, "[ Inlined load from keyed Property");
8758
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008759 // Use a fresh temporary to load the elements without destroying
8760 // the receiver which is needed for the deferred slow case.
8761 Result elements = allocator()->Allocate();
8762 ASSERT(elements.is_valid());
8763
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00008764 Result key = frame_->Pop();
8765 Result receiver = frame_->Pop();
8766 key.ToRegister();
8767 receiver.ToRegister();
8768
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008769 DeferredReferenceGetKeyedValue* deferred =
ricow@chromium.org30ce4112010-05-31 10:38:25 +00008770 new DeferredReferenceGetKeyedValue(elements.reg(),
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008771 receiver.reg(),
ager@chromium.org5c838252010-02-19 08:53:10 +00008772 key.reg());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008773
ager@chromium.org5c838252010-02-19 08:53:10 +00008774 __ test(receiver.reg(), Immediate(kSmiTagMask));
8775 deferred->Branch(zero);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008776
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00008777 // Check that the receiver has the expected map.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008778 // Initially, use an invalid map. The map is patched in the IC
8779 // initialization code.
8780 __ bind(deferred->patch_site());
8781 // Use masm-> here instead of the double underscore macro since extra
8782 // coverage code can interfere with the patching.
8783 masm_->cmp(FieldOperand(receiver.reg(), HeapObject::kMapOffset),
8784 Immediate(Factory::null_value()));
8785 deferred->Branch(not_equal);
8786
8787 // Check that the key is a smi.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00008788 if (!key.is_smi()) {
8789 __ test(key.reg(), Immediate(kSmiTagMask));
8790 deferred->Branch(not_zero);
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00008791 } else {
8792 if (FLAG_debug_code) __ AbortIfNotSmi(key.reg());
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00008793 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008794
8795 // Get the elements array from the receiver and check that it
8796 // is not a dictionary.
8797 __ mov(elements.reg(),
8798 FieldOperand(receiver.reg(), JSObject::kElementsOffset));
8799 __ cmp(FieldOperand(elements.reg(), HeapObject::kMapOffset),
8800 Immediate(Factory::fixed_array_map()));
8801 deferred->Branch(not_equal);
8802
ricow@chromium.org30ce4112010-05-31 10:38:25 +00008803 // Check that the key is within bounds.
8804 __ cmp(key.reg(),
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008805 FieldOperand(elements.reg(), FixedArray::kLengthOffset));
8806 deferred->Branch(above_equal);
8807
ricow@chromium.org30ce4112010-05-31 10:38:25 +00008808 // Load and check that the result is not the hole.
8809 // Key holds a smi.
8810 ASSERT((kSmiTag == 0) && (kSmiTagSize == 1));
8811 __ mov(elements.reg(),
8812 FieldOperand(elements.reg(),
8813 key.reg(),
8814 times_2,
8815 FixedArray::kHeaderSize));
8816 result = elements;
ager@chromium.org5c838252010-02-19 08:53:10 +00008817 __ cmp(Operand(result.reg()), Immediate(Factory::the_hole_value()));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008818 deferred->Branch(equal);
8819 __ IncrementCounter(&Counters::keyed_load_inline, 1);
8820
8821 deferred->BindExit();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008822 } else {
8823 Comment cmnt(masm_, "[ Load from keyed Property");
ager@chromium.org5c838252010-02-19 08:53:10 +00008824 result = frame_->CallKeyedLoadIC(RelocInfo::CODE_TARGET);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008825 // Make sure that we do not have a test instruction after the
8826 // call. A test instruction after the call is used to
8827 // indicate that we have generated an inline version of the
8828 // keyed load. The explicit nop instruction is here because
8829 // the push that follows might be peep-hole optimized away.
8830 __ nop();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008831 }
ager@chromium.org5c838252010-02-19 08:53:10 +00008832 ASSERT(frame()->height() == original_height - 2);
8833 return result;
8834}
8835
8836
8837Result CodeGenerator::EmitKeyedStore(StaticType* key_type) {
8838#ifdef DEBUG
8839 int original_height = frame()->height();
8840#endif
8841 Result result;
8842 // Generate inlined version of the keyed store if the code is in a loop
8843 // and the key is likely to be a smi.
8844 if (loop_nesting() > 0 && key_type->IsLikelySmi()) {
8845 Comment cmnt(masm(), "[ Inlined store to keyed Property");
8846
8847 // Get the receiver, key and value into registers.
8848 result = frame()->Pop();
8849 Result key = frame()->Pop();
8850 Result receiver = frame()->Pop();
8851
8852 Result tmp = allocator_->Allocate();
8853 ASSERT(tmp.is_valid());
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00008854 Result tmp2 = allocator_->Allocate();
8855 ASSERT(tmp2.is_valid());
ager@chromium.org5c838252010-02-19 08:53:10 +00008856
8857 // Determine whether the value is a constant before putting it in a
8858 // register.
8859 bool value_is_constant = result.is_constant();
8860
8861 // Make sure that value, key and receiver are in registers.
8862 result.ToRegister();
8863 key.ToRegister();
8864 receiver.ToRegister();
8865
8866 DeferredReferenceSetKeyedValue* deferred =
8867 new DeferredReferenceSetKeyedValue(result.reg(),
8868 key.reg(),
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00008869 receiver.reg(),
8870 tmp.reg());
ager@chromium.org5c838252010-02-19 08:53:10 +00008871
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00008872 // Check that the receiver is not a smi.
8873 __ test(receiver.reg(), Immediate(kSmiTagMask));
8874 deferred->Branch(zero);
ager@chromium.org5c838252010-02-19 08:53:10 +00008875
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00008876 // Check that the key is a smi.
8877 if (!key.is_smi()) {
8878 __ test(key.reg(), Immediate(kSmiTagMask));
8879 deferred->Branch(not_zero);
8880 } else {
8881 if (FLAG_debug_code) __ AbortIfNotSmi(key.reg());
8882 }
ager@chromium.org5c838252010-02-19 08:53:10 +00008883
ager@chromium.org5c838252010-02-19 08:53:10 +00008884 // Check that the receiver is a JSArray.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00008885 __ CmpObjectType(receiver.reg(), JS_ARRAY_TYPE, tmp.reg());
ager@chromium.org5c838252010-02-19 08:53:10 +00008886 deferred->Branch(not_equal);
8887
8888 // Check that the key is within bounds. Both the key and the length of
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00008889 // the JSArray are smis. Use unsigned comparison to handle negative keys.
ager@chromium.org5c838252010-02-19 08:53:10 +00008890 __ cmp(key.reg(),
8891 FieldOperand(receiver.reg(), JSArray::kLengthOffset));
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00008892 deferred->Branch(above_equal);
ager@chromium.org5c838252010-02-19 08:53:10 +00008893
8894 // Get the elements array from the receiver and check that it is not a
8895 // dictionary.
8896 __ mov(tmp.reg(),
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00008897 FieldOperand(receiver.reg(), JSArray::kElementsOffset));
8898
8899 // Check whether it is possible to omit the write barrier. If the elements
8900 // array is in new space or the value written is a smi we can safely update
ricow@chromium.org30ce4112010-05-31 10:38:25 +00008901 // the elements array without write barrier.
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00008902 Label in_new_space;
8903 __ InNewSpace(tmp.reg(), tmp2.reg(), equal, &in_new_space);
8904 if (!value_is_constant) {
8905 __ test(result.reg(), Immediate(kSmiTagMask));
8906 deferred->Branch(not_zero);
8907 }
8908
8909 __ bind(&in_new_space);
ager@chromium.org5c838252010-02-19 08:53:10 +00008910 // Bind the deferred code patch site to be able to locate the fixed
8911 // array map comparison. When debugging, we patch this comparison to
8912 // always fail so that we will hit the IC call in the deferred code
8913 // which will allow the debugger to break for fast case stores.
8914 __ bind(deferred->patch_site());
8915 __ cmp(FieldOperand(tmp.reg(), HeapObject::kMapOffset),
8916 Immediate(Factory::fixed_array_map()));
8917 deferred->Branch(not_equal);
8918
8919 // Store the value.
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00008920 __ mov(FixedArrayElementOperand(tmp.reg(), key.reg()), result.reg());
ager@chromium.org5c838252010-02-19 08:53:10 +00008921 __ IncrementCounter(&Counters::keyed_store_inline, 1);
8922
8923 deferred->BindExit();
8924 } else {
8925 result = frame()->CallKeyedStoreIC();
8926 // Make sure that we do not have a test instruction after the
8927 // call. A test instruction after the call is used to
8928 // indicate that we have generated an inline version of the
8929 // keyed store.
8930 __ nop();
ager@chromium.org5c838252010-02-19 08:53:10 +00008931 }
8932 ASSERT(frame()->height() == original_height - 3);
8933 return result;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008934}
8935
8936
ager@chromium.org7c537e22008-10-16 08:43:32 +00008937#undef __
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008938#define __ ACCESS_MASM(masm)
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008939
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008940
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00008941static void CheckTwoForSminess(MacroAssembler* masm,
8942 Register left, Register right, Register scratch,
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00008943 TypeInfo left_info, TypeInfo right_info,
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00008944 DeferredInlineBinaryOperation* deferred) {
8945 if (left.is(right)) {
8946 if (!left_info.IsSmi()) {
8947 __ test(left, Immediate(kSmiTagMask));
8948 deferred->Branch(not_zero);
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00008949 } else {
8950 if (FLAG_debug_code) __ AbortIfNotSmi(left);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00008951 }
8952 } else if (!left_info.IsSmi()) {
8953 if (!right_info.IsSmi()) {
8954 __ mov(scratch, left);
8955 __ or_(scratch, Operand(right));
8956 __ test(scratch, Immediate(kSmiTagMask));
8957 deferred->Branch(not_zero);
8958 } else {
8959 __ test(left, Immediate(kSmiTagMask));
8960 deferred->Branch(not_zero);
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00008961 if (FLAG_debug_code) __ AbortIfNotSmi(right);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00008962 }
8963 } else {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00008964 if (FLAG_debug_code) __ AbortIfNotSmi(left);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00008965 if (!right_info.IsSmi()) {
8966 __ test(right, Immediate(kSmiTagMask));
8967 deferred->Branch(not_zero);
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00008968 } else {
8969 if (FLAG_debug_code) __ AbortIfNotSmi(right);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00008970 }
8971 }
8972}
8973
8974
ager@chromium.org7c537e22008-10-16 08:43:32 +00008975Handle<String> Reference::GetName() {
8976 ASSERT(type_ == NAMED);
8977 Property* property = expression_->AsProperty();
8978 if (property == NULL) {
8979 // Global variable reference treated as a named property reference.
8980 VariableProxy* proxy = expression_->AsVariableProxy();
8981 ASSERT(proxy->AsVariable() != NULL);
8982 ASSERT(proxy->AsVariable()->is_global());
8983 return proxy->name();
8984 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +00008985 Literal* raw_name = property->key()->AsLiteral();
8986 ASSERT(raw_name != NULL);
ager@chromium.org5c838252010-02-19 08:53:10 +00008987 return Handle<String>::cast(raw_name->handle());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008988 }
8989}
8990
8991
ager@chromium.orgc4c92722009-11-18 14:12:51 +00008992void Reference::GetValue() {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008993 ASSERT(!cgen_->in_spilled_code());
8994 ASSERT(cgen_->HasValidEntryRegisters());
ager@chromium.org7c537e22008-10-16 08:43:32 +00008995 ASSERT(!is_illegal());
ager@chromium.org7c537e22008-10-16 08:43:32 +00008996 MacroAssembler* masm = cgen_->masm();
ager@chromium.org3e875802009-06-29 08:26:34 +00008997
8998 // Record the source position for the property load.
8999 Property* property = expression_->AsProperty();
9000 if (property != NULL) {
9001 cgen_->CodeForSourcePosition(property->position());
9002 }
9003
ager@chromium.org7c537e22008-10-16 08:43:32 +00009004 switch (type_) {
9005 case SLOT: {
9006 Comment cmnt(masm, "[ Load from Slot");
9007 Slot* slot = expression_->AsVariableProxy()->AsVariable()->slot();
9008 ASSERT(slot != NULL);
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00009009 cgen_->LoadFromSlotCheckForArguments(slot, NOT_INSIDE_TYPEOF);
ager@chromium.org5c838252010-02-19 08:53:10 +00009010 if (!persist_after_get_) set_unloaded();
ager@chromium.org7c537e22008-10-16 08:43:32 +00009011 break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009012 }
9013
ager@chromium.org7c537e22008-10-16 08:43:32 +00009014 case NAMED: {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00009015 Variable* var = expression_->AsVariableProxy()->AsVariable();
ager@chromium.org5ec48922009-05-05 07:25:34 +00009016 bool is_global = var != NULL;
9017 ASSERT(!is_global || var->is_global());
ager@chromium.org5c838252010-02-19 08:53:10 +00009018 if (persist_after_get_) cgen_->frame()->Dup();
9019 Result result = cgen_->EmitNamedLoad(GetName(), is_global);
9020 if (!persist_after_get_) set_unloaded();
9021 cgen_->frame()->Push(&result);
ager@chromium.org7c537e22008-10-16 08:43:32 +00009022 break;
9023 }
9024
9025 case KEYED: {
ager@chromium.org5c838252010-02-19 08:53:10 +00009026 if (persist_after_get_) {
9027 cgen_->frame()->PushElementAt(1);
9028 cgen_->frame()->PushElementAt(1);
9029 }
9030 Result value = cgen_->EmitKeyedLoad();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009031 cgen_->frame()->Push(&value);
ager@chromium.org5c838252010-02-19 08:53:10 +00009032 if (!persist_after_get_) set_unloaded();
ager@chromium.org7c537e22008-10-16 08:43:32 +00009033 break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009034 }
9035
9036 default:
9037 UNREACHABLE();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009038 }
9039}
9040
9041
ager@chromium.orgc4c92722009-11-18 14:12:51 +00009042void Reference::TakeValue() {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00009043 // For non-constant frame-allocated slots, we invalidate the value in the
9044 // slot. For all others, we fall back on GetValue.
9045 ASSERT(!cgen_->in_spilled_code());
ager@chromium.org7c537e22008-10-16 08:43:32 +00009046 ASSERT(!is_illegal());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00009047 if (type_ != SLOT) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00009048 GetValue();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00009049 return;
9050 }
9051
9052 Slot* slot = expression_->AsVariableProxy()->AsVariable()->slot();
9053 ASSERT(slot != NULL);
9054 if (slot->type() == Slot::LOOKUP ||
9055 slot->type() == Slot::CONTEXT ||
ager@chromium.org3e875802009-06-29 08:26:34 +00009056 slot->var()->mode() == Variable::CONST ||
9057 slot->is_arguments()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00009058 GetValue();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00009059 return;
9060 }
9061
ager@chromium.org3e875802009-06-29 08:26:34 +00009062 // Only non-constant, frame-allocated parameters and locals can
9063 // reach here. Be careful not to use the optimizations for arguments
9064 // object access since it may not have been initialized yet.
9065 ASSERT(!slot->is_arguments());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00009066 if (slot->type() == Slot::PARAMETER) {
9067 cgen_->frame()->TakeParameterAt(slot->index());
9068 } else {
9069 ASSERT(slot->type() == Slot::LOCAL);
9070 cgen_->frame()->TakeLocalAt(slot->index());
9071 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009072
9073 ASSERT(persist_after_get_);
9074 // Do not unload the reference, because it is used in SetValue.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00009075}
9076
9077
9078void Reference::SetValue(InitState init_state) {
9079 ASSERT(cgen_->HasValidEntryRegisters());
9080 ASSERT(!is_illegal());
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009081 MacroAssembler* masm = cgen_->masm();
ager@chromium.org7c537e22008-10-16 08:43:32 +00009082 switch (type_) {
9083 case SLOT: {
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009084 Comment cmnt(masm, "[ Store to Slot");
ager@chromium.org7c537e22008-10-16 08:43:32 +00009085 Slot* slot = expression_->AsVariableProxy()->AsVariable()->slot();
9086 ASSERT(slot != NULL);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00009087 cgen_->StoreToSlot(slot, init_state);
ager@chromium.org5c838252010-02-19 08:53:10 +00009088 set_unloaded();
ager@chromium.org7c537e22008-10-16 08:43:32 +00009089 break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009090 }
9091
ager@chromium.org7c537e22008-10-16 08:43:32 +00009092 case NAMED: {
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009093 Comment cmnt(masm, "[ Store to named Property");
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00009094 Result answer = cgen_->EmitNamedStore(GetName(), false);
ager@chromium.org41826e72009-03-30 13:30:57 +00009095 cgen_->frame()->Push(&answer);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009096 set_unloaded();
ager@chromium.org7c537e22008-10-16 08:43:32 +00009097 break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009098 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00009099
9100 case KEYED: {
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009101 Comment cmnt(masm, "[ Store to keyed Property");
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009102 Property* property = expression()->AsProperty();
9103 ASSERT(property != NULL);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009104
ager@chromium.org5c838252010-02-19 08:53:10 +00009105 Result answer = cgen_->EmitKeyedStore(property->key()->type());
9106 cgen_->frame()->Push(&answer);
9107 set_unloaded();
ager@chromium.org7c537e22008-10-16 08:43:32 +00009108 break;
9109 }
9110
ager@chromium.org5c838252010-02-19 08:53:10 +00009111 case UNLOADED:
9112 case ILLEGAL:
ager@chromium.org7c537e22008-10-16 08:43:32 +00009113 UNREACHABLE();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009114 }
9115}
9116
9117
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00009118void FastNewClosureStub::Generate(MacroAssembler* masm) {
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009119 // Create a new closure from the given function info in new
9120 // space. Set the context to the current context in esi.
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00009121 Label gc;
9122 __ AllocateInNewSpace(JSFunction::kSize, eax, ebx, ecx, &gc, TAG_OBJECT);
9123
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009124 // Get the function info from the stack.
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00009125 __ mov(edx, Operand(esp, 1 * kPointerSize));
9126
9127 // Compute the function map in the current global context and set that
9128 // as the map of the allocated object.
9129 __ mov(ecx, Operand(esi, Context::SlotOffset(Context::GLOBAL_INDEX)));
9130 __ mov(ecx, FieldOperand(ecx, GlobalObject::kGlobalContextOffset));
9131 __ mov(ecx, Operand(ecx, Context::SlotOffset(Context::FUNCTION_MAP_INDEX)));
9132 __ mov(FieldOperand(eax, JSObject::kMapOffset), ecx);
9133
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009134 // Initialize the rest of the function. We don't have to update the
9135 // write barrier because the allocated object is in new space.
9136 __ mov(ebx, Immediate(Factory::empty_fixed_array()));
9137 __ mov(FieldOperand(eax, JSObject::kPropertiesOffset), ebx);
9138 __ mov(FieldOperand(eax, JSObject::kElementsOffset), ebx);
9139 __ mov(FieldOperand(eax, JSFunction::kPrototypeOrInitialMapOffset),
9140 Immediate(Factory::the_hole_value()));
9141 __ mov(FieldOperand(eax, JSFunction::kSharedFunctionInfoOffset), edx);
9142 __ mov(FieldOperand(eax, JSFunction::kContextOffset), esi);
9143 __ mov(FieldOperand(eax, JSFunction::kLiteralsOffset), ebx);
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00009144
9145 // Return and remove the on-stack parameter.
9146 __ ret(1 * kPointerSize);
9147
9148 // Create a new closure through the slower runtime call.
9149 __ bind(&gc);
9150 __ pop(ecx); // Temporarily remove return address.
9151 __ pop(edx);
9152 __ push(esi);
9153 __ push(edx);
9154 __ push(ecx); // Restore return address.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009155 __ TailCallRuntime(Runtime::kNewClosure, 2, 1);
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00009156}
9157
9158
9159void FastNewContextStub::Generate(MacroAssembler* masm) {
9160 // Try to allocate the context in new space.
9161 Label gc;
9162 int length = slots_ + Context::MIN_CONTEXT_SLOTS;
9163 __ AllocateInNewSpace((length * kPointerSize) + FixedArray::kHeaderSize,
9164 eax, ebx, ecx, &gc, TAG_OBJECT);
9165
9166 // Get the function from the stack.
9167 __ mov(ecx, Operand(esp, 1 * kPointerSize));
9168
9169 // Setup the object header.
9170 __ mov(FieldOperand(eax, HeapObject::kMapOffset), Factory::context_map());
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009171 __ mov(FieldOperand(eax, Context::kLengthOffset),
9172 Immediate(Smi::FromInt(length)));
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00009173
9174 // Setup the fixed slots.
9175 __ xor_(ebx, Operand(ebx)); // Set to NULL.
9176 __ mov(Operand(eax, Context::SlotOffset(Context::CLOSURE_INDEX)), ecx);
9177 __ mov(Operand(eax, Context::SlotOffset(Context::FCONTEXT_INDEX)), eax);
9178 __ mov(Operand(eax, Context::SlotOffset(Context::PREVIOUS_INDEX)), ebx);
9179 __ mov(Operand(eax, Context::SlotOffset(Context::EXTENSION_INDEX)), ebx);
9180
kasperl@chromium.org7b9eafd2009-12-21 15:20:30 +00009181 // Copy the global object from the surrounding context. We go through the
9182 // context in the function (ecx) to match the allocation behavior we have
9183 // in the runtime system (see Heap::AllocateFunctionContext).
9184 __ mov(ebx, FieldOperand(ecx, JSFunction::kContextOffset));
9185 __ mov(ebx, Operand(ebx, Context::SlotOffset(Context::GLOBAL_INDEX)));
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00009186 __ mov(Operand(eax, Context::SlotOffset(Context::GLOBAL_INDEX)), ebx);
9187
9188 // Initialize the rest of the slots to undefined.
9189 __ mov(ebx, Factory::undefined_value());
9190 for (int i = Context::MIN_CONTEXT_SLOTS; i < length; i++) {
9191 __ mov(Operand(eax, Context::SlotOffset(i)), ebx);
9192 }
9193
9194 // Return and remove the on-stack parameter.
9195 __ mov(esi, Operand(eax));
9196 __ ret(1 * kPointerSize);
9197
9198 // Need to collect. Call into runtime system.
9199 __ bind(&gc);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009200 __ TailCallRuntime(Runtime::kNewContext, 1, 1);
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00009201}
9202
9203
9204void FastCloneShallowArrayStub::Generate(MacroAssembler* masm) {
ager@chromium.org5c838252010-02-19 08:53:10 +00009205 // Stack layout on entry:
9206 //
9207 // [esp + kPointerSize]: constant elements.
9208 // [esp + (2 * kPointerSize)]: literal index.
9209 // [esp + (3 * kPointerSize)]: literals array.
9210
9211 // All sizes here are multiples of kPointerSize.
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00009212 int elements_size = (length_ > 0) ? FixedArray::SizeFor(length_) : 0;
9213 int size = JSArray::kSize + elements_size;
9214
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00009215 // Load boilerplate object into ecx and check if we need to create a
9216 // boilerplate.
9217 Label slow_case;
9218 __ mov(ecx, Operand(esp, 3 * kPointerSize));
9219 __ mov(eax, Operand(esp, 2 * kPointerSize));
9220 ASSERT((kPointerSize == 4) && (kSmiTagSize == 1) && (kSmiTag == 0));
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00009221 __ mov(ecx, CodeGenerator::FixedArrayElementOperand(ecx, eax));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00009222 __ cmp(ecx, Factory::undefined_value());
9223 __ j(equal, &slow_case);
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00009224
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00009225 // Allocate both the JS array and the elements array in one big
9226 // allocation. This avoids multiple limit checks.
9227 __ AllocateInNewSpace(size, eax, ebx, edx, &slow_case, TAG_OBJECT);
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00009228
9229 // Copy the JS array part.
9230 for (int i = 0; i < JSArray::kSize; i += kPointerSize) {
9231 if ((i != JSArray::kElementsOffset) || (length_ == 0)) {
9232 __ mov(ebx, FieldOperand(ecx, i));
9233 __ mov(FieldOperand(eax, i), ebx);
9234 }
9235 }
9236
9237 if (length_ > 0) {
9238 // Get hold of the elements array of the boilerplate and setup the
9239 // elements pointer in the resulting object.
9240 __ mov(ecx, FieldOperand(ecx, JSArray::kElementsOffset));
9241 __ lea(edx, Operand(eax, JSArray::kSize));
9242 __ mov(FieldOperand(eax, JSArray::kElementsOffset), edx);
9243
9244 // Copy the elements array.
9245 for (int i = 0; i < elements_size; i += kPointerSize) {
9246 __ mov(ebx, FieldOperand(ecx, i));
9247 __ mov(FieldOperand(edx, i), ebx);
9248 }
9249 }
9250
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00009251 // Return and remove the on-stack parameters.
9252 __ ret(3 * kPointerSize);
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00009253
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00009254 __ bind(&slow_case);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009255 __ TailCallRuntime(Runtime::kCreateArrayLiteralShallow, 3, 1);
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00009256}
9257
9258
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009259// NOTE: The stub does not handle the inlined cases (Smis, Booleans, undefined).
9260void ToBooleanStub::Generate(MacroAssembler* masm) {
9261 Label false_result, true_result, not_string;
9262 __ mov(eax, Operand(esp, 1 * kPointerSize));
9263
9264 // 'null' => false.
9265 __ cmp(eax, Factory::null_value());
9266 __ j(equal, &false_result);
9267
9268 // Get the map and type of the heap object.
9269 __ mov(edx, FieldOperand(eax, HeapObject::kMapOffset));
9270 __ movzx_b(ecx, FieldOperand(edx, Map::kInstanceTypeOffset));
9271
9272 // Undetectable => false.
9273 __ movzx_b(ebx, FieldOperand(edx, Map::kBitFieldOffset));
9274 __ and_(ebx, 1 << Map::kIsUndetectable);
9275 __ j(not_zero, &false_result);
9276
9277 // JavaScript object => true.
9278 __ cmp(ecx, FIRST_JS_OBJECT_TYPE);
9279 __ j(above_equal, &true_result);
9280
9281 // String value => false iff empty.
9282 __ cmp(ecx, FIRST_NONSTRING_TYPE);
9283 __ j(above_equal, &not_string);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009284 __ mov(edx, FieldOperand(eax, String::kLengthOffset));
ager@chromium.orgac091b72010-05-05 07:34:42 +00009285 ASSERT(kSmiTag == 0);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009286 __ test(edx, Operand(edx));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009287 __ j(zero, &false_result);
9288 __ jmp(&true_result);
9289
9290 __ bind(&not_string);
9291 // HeapNumber => false iff +0, -0, or NaN.
9292 __ cmp(edx, Factory::heap_number_map());
9293 __ j(not_equal, &true_result);
9294 __ fldz();
9295 __ fld_d(FieldOperand(eax, HeapNumber::kValueOffset));
ager@chromium.org3811b432009-10-28 14:53:37 +00009296 __ FCmp();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009297 __ j(zero, &false_result);
9298 // Fall through to |true_result|.
9299
9300 // Return 1/0 for true/false in eax.
9301 __ bind(&true_result);
9302 __ mov(eax, 1);
9303 __ ret(1 * kPointerSize);
9304 __ bind(&false_result);
9305 __ mov(eax, 0);
9306 __ ret(1 * kPointerSize);
9307}
9308
9309
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00009310void GenericBinaryOpStub::GenerateCall(
9311 MacroAssembler* masm,
9312 Register left,
9313 Register right) {
9314 if (!ArgsInRegistersSupported()) {
sgjesse@chromium.org97c28312009-10-20 12:45:56 +00009315 // Pass arguments on the stack.
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00009316 __ push(left);
9317 __ push(right);
9318 } else {
9319 // The calling convention with registers is left in edx and right in eax.
ager@chromium.org3811b432009-10-28 14:53:37 +00009320 Register left_arg = edx;
9321 Register right_arg = eax;
9322 if (!(left.is(left_arg) && right.is(right_arg))) {
9323 if (left.is(right_arg) && right.is(left_arg)) {
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00009324 if (IsOperationCommutative()) {
9325 SetArgsReversed();
9326 } else {
9327 __ xchg(left, right);
9328 }
ager@chromium.org3811b432009-10-28 14:53:37 +00009329 } else if (left.is(left_arg)) {
9330 __ mov(right_arg, right);
ager@chromium.org951093b2010-02-11 12:59:04 +00009331 } else if (right.is(right_arg)) {
9332 __ mov(left_arg, left);
ager@chromium.org3811b432009-10-28 14:53:37 +00009333 } else if (left.is(right_arg)) {
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00009334 if (IsOperationCommutative()) {
ager@chromium.org3811b432009-10-28 14:53:37 +00009335 __ mov(left_arg, right);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00009336 SetArgsReversed();
9337 } else {
ager@chromium.org3811b432009-10-28 14:53:37 +00009338 // Order of moves important to avoid destroying left argument.
9339 __ mov(left_arg, left);
9340 __ mov(right_arg, right);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00009341 }
ager@chromium.org3811b432009-10-28 14:53:37 +00009342 } else if (right.is(left_arg)) {
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00009343 if (IsOperationCommutative()) {
ager@chromium.org3811b432009-10-28 14:53:37 +00009344 __ mov(right_arg, left);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00009345 SetArgsReversed();
9346 } else {
ager@chromium.org3811b432009-10-28 14:53:37 +00009347 // Order of moves important to avoid destroying right argument.
9348 __ mov(right_arg, right);
9349 __ mov(left_arg, left);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00009350 }
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00009351 } else {
ager@chromium.org3811b432009-10-28 14:53:37 +00009352 // Order of moves is not important.
9353 __ mov(left_arg, left);
9354 __ mov(right_arg, right);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00009355 }
9356 }
9357
9358 // Update flags to indicate that arguments are in registers.
9359 SetArgsInRegisters();
ager@chromium.org3811b432009-10-28 14:53:37 +00009360 __ IncrementCounter(&Counters::generic_binary_stub_calls_regs, 1);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00009361 }
9362
9363 // Call the stub.
9364 __ CallStub(this);
9365}
9366
9367
9368void GenericBinaryOpStub::GenerateCall(
9369 MacroAssembler* masm,
9370 Register left,
9371 Smi* right) {
9372 if (!ArgsInRegistersSupported()) {
sgjesse@chromium.org97c28312009-10-20 12:45:56 +00009373 // Pass arguments on the stack.
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00009374 __ push(left);
9375 __ push(Immediate(right));
9376 } else {
ager@chromium.org3811b432009-10-28 14:53:37 +00009377 // The calling convention with registers is left in edx and right in eax.
9378 Register left_arg = edx;
9379 Register right_arg = eax;
9380 if (left.is(left_arg)) {
9381 __ mov(right_arg, Immediate(right));
9382 } else if (left.is(right_arg) && IsOperationCommutative()) {
9383 __ mov(left_arg, Immediate(right));
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00009384 SetArgsReversed();
9385 } else {
ager@chromium.org951093b2010-02-11 12:59:04 +00009386 // For non-commutative operations, left and right_arg might be
9387 // the same register. Therefore, the order of the moves is
9388 // important here in order to not overwrite left before moving
9389 // it to left_arg.
ager@chromium.org3811b432009-10-28 14:53:37 +00009390 __ mov(left_arg, left);
9391 __ mov(right_arg, Immediate(right));
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00009392 }
9393
9394 // Update flags to indicate that arguments are in registers.
9395 SetArgsInRegisters();
ager@chromium.org3811b432009-10-28 14:53:37 +00009396 __ IncrementCounter(&Counters::generic_binary_stub_calls_regs, 1);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00009397 }
9398
9399 // Call the stub.
9400 __ CallStub(this);
9401}
9402
9403
9404void GenericBinaryOpStub::GenerateCall(
9405 MacroAssembler* masm,
9406 Smi* left,
9407 Register right) {
sgjesse@chromium.org97c28312009-10-20 12:45:56 +00009408 if (!ArgsInRegistersSupported()) {
9409 // Pass arguments on the stack.
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00009410 __ push(Immediate(left));
9411 __ push(right);
9412 } else {
ager@chromium.org3811b432009-10-28 14:53:37 +00009413 // The calling convention with registers is left in edx and right in eax.
9414 Register left_arg = edx;
9415 Register right_arg = eax;
9416 if (right.is(right_arg)) {
9417 __ mov(left_arg, Immediate(left));
9418 } else if (right.is(left_arg) && IsOperationCommutative()) {
9419 __ mov(right_arg, Immediate(left));
9420 SetArgsReversed();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00009421 } else {
ager@chromium.org951093b2010-02-11 12:59:04 +00009422 // For non-commutative operations, right and left_arg might be
9423 // the same register. Therefore, the order of the moves is
9424 // important here in order to not overwrite right before moving
9425 // it to right_arg.
ager@chromium.org3811b432009-10-28 14:53:37 +00009426 __ mov(right_arg, right);
ager@chromium.org951093b2010-02-11 12:59:04 +00009427 __ mov(left_arg, Immediate(left));
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00009428 }
9429 // Update flags to indicate that arguments are in registers.
9430 SetArgsInRegisters();
ager@chromium.org3811b432009-10-28 14:53:37 +00009431 __ IncrementCounter(&Counters::generic_binary_stub_calls_regs, 1);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00009432 }
9433
9434 // Call the stub.
9435 __ CallStub(this);
9436}
9437
9438
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009439Result GenericBinaryOpStub::GenerateCall(MacroAssembler* masm,
9440 VirtualFrame* frame,
9441 Result* left,
9442 Result* right) {
9443 if (ArgsInRegistersSupported()) {
9444 SetArgsInRegisters();
9445 return frame->CallStub(this, left, right);
9446 } else {
9447 frame->Push(left);
9448 frame->Push(right);
9449 return frame->CallStub(this, 2);
9450 }
9451}
9452
9453
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00009454void GenericBinaryOpStub::GenerateSmiCode(MacroAssembler* masm, Label* slow) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009455 // 1. Move arguments into edx, eax except for DIV and MOD, which need the
9456 // dividend in eax and edx free for the division. Use eax, ebx for those.
9457 Comment load_comment(masm, "-- Load arguments");
9458 Register left = edx;
9459 Register right = eax;
9460 if (op_ == Token::DIV || op_ == Token::MOD) {
9461 left = eax;
9462 right = ebx;
9463 if (HasArgsInRegisters()) {
9464 __ mov(ebx, eax);
9465 __ mov(eax, edx);
9466 }
9467 }
9468 if (!HasArgsInRegisters()) {
9469 __ mov(right, Operand(esp, 1 * kPointerSize));
9470 __ mov(left, Operand(esp, 2 * kPointerSize));
9471 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009472
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009473 if (static_operands_type_.IsSmi()) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00009474 if (FLAG_debug_code) {
9475 __ AbortIfNotSmi(left);
9476 __ AbortIfNotSmi(right);
9477 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009478 if (op_ == Token::BIT_OR) {
9479 __ or_(right, Operand(left));
9480 GenerateReturn(masm);
9481 return;
9482 } else if (op_ == Token::BIT_AND) {
9483 __ and_(right, Operand(left));
9484 GenerateReturn(masm);
9485 return;
9486 } else if (op_ == Token::BIT_XOR) {
9487 __ xor_(right, Operand(left));
9488 GenerateReturn(masm);
9489 return;
9490 }
9491 }
9492
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009493 // 2. Prepare the smi check of both operands by oring them together.
9494 Comment smi_check_comment(masm, "-- Smi check arguments");
9495 Label not_smis;
9496 Register combined = ecx;
9497 ASSERT(!left.is(combined) && !right.is(combined));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009498 switch (op_) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009499 case Token::BIT_OR:
9500 // Perform the operation into eax and smi check the result. Preserve
9501 // eax in case the result is not a smi.
9502 ASSERT(!left.is(ecx) && !right.is(ecx));
9503 __ mov(ecx, right);
9504 __ or_(right, Operand(left)); // Bitwise or is commutative.
9505 combined = right;
9506 break;
9507
9508 case Token::BIT_XOR:
9509 case Token::BIT_AND:
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00009510 case Token::ADD:
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00009511 case Token::SUB:
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009512 case Token::MUL:
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00009513 case Token::DIV:
9514 case Token::MOD:
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009515 __ mov(combined, right);
9516 __ or_(combined, Operand(left));
9517 break;
9518
9519 case Token::SHL:
9520 case Token::SAR:
9521 case Token::SHR:
9522 // Move the right operand into ecx for the shift operation, use eax
9523 // for the smi check register.
9524 ASSERT(!left.is(ecx) && !right.is(ecx));
9525 __ mov(ecx, right);
9526 __ or_(right, Operand(left));
9527 combined = right;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009528 break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009529
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00009530 default:
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009531 break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009532 }
9533
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009534 // 3. Perform the smi check of the operands.
9535 ASSERT(kSmiTag == 0); // Adjust zero check if not the case.
9536 __ test(combined, Immediate(kSmiTagMask));
9537 __ j(not_zero, &not_smis, not_taken);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00009538
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009539 // 4. Operands are both smis, perform the operation leaving the result in
9540 // eax and check the result if necessary.
9541 Comment perform_smi(masm, "-- Perform smi operation");
9542 Label use_fp_on_smis;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00009543 switch (op_) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009544 case Token::BIT_OR:
9545 // Nothing to do.
9546 break;
9547
9548 case Token::BIT_XOR:
9549 ASSERT(right.is(eax));
9550 __ xor_(right, Operand(left)); // Bitwise xor is commutative.
9551 break;
9552
9553 case Token::BIT_AND:
9554 ASSERT(right.is(eax));
9555 __ and_(right, Operand(left)); // Bitwise and is commutative.
9556 break;
9557
9558 case Token::SHL:
9559 // Remove tags from operands (but keep sign).
9560 __ SmiUntag(left);
9561 __ SmiUntag(ecx);
9562 // Perform the operation.
9563 __ shl_cl(left);
9564 // Check that the *signed* result fits in a smi.
9565 __ cmp(left, 0xc0000000);
9566 __ j(sign, &use_fp_on_smis, not_taken);
9567 // Tag the result and store it in register eax.
9568 __ SmiTag(left);
9569 __ mov(eax, left);
9570 break;
9571
9572 case Token::SAR:
9573 // Remove tags from operands (but keep sign).
9574 __ SmiUntag(left);
9575 __ SmiUntag(ecx);
9576 // Perform the operation.
9577 __ sar_cl(left);
9578 // Tag the result and store it in register eax.
9579 __ SmiTag(left);
9580 __ mov(eax, left);
9581 break;
9582
9583 case Token::SHR:
9584 // Remove tags from operands (but keep sign).
9585 __ SmiUntag(left);
9586 __ SmiUntag(ecx);
9587 // Perform the operation.
9588 __ shr_cl(left);
9589 // Check that the *unsigned* result fits in a smi.
9590 // Neither of the two high-order bits can be set:
9591 // - 0x80000000: high bit would be lost when smi tagging.
9592 // - 0x40000000: this number would convert to negative when
9593 // Smi tagging these two cases can only happen with shifts
9594 // by 0 or 1 when handed a valid smi.
9595 __ test(left, Immediate(0xc0000000));
9596 __ j(not_zero, slow, not_taken);
9597 // Tag the result and store it in register eax.
9598 __ SmiTag(left);
9599 __ mov(eax, left);
9600 break;
9601
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00009602 case Token::ADD:
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009603 ASSERT(right.is(eax));
9604 __ add(right, Operand(left)); // Addition is commutative.
9605 __ j(overflow, &use_fp_on_smis, not_taken);
9606 break;
9607
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00009608 case Token::SUB:
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009609 __ sub(left, Operand(right));
9610 __ j(overflow, &use_fp_on_smis, not_taken);
9611 __ mov(eax, left);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00009612 break;
9613
9614 case Token::MUL:
9615 // If the smi tag is 0 we can just leave the tag on one operand.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009616 ASSERT(kSmiTag == 0); // Adjust code below if not the case.
9617 // We can't revert the multiplication if the result is not a smi
9618 // so save the right operand.
9619 __ mov(ebx, right);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00009620 // Remove tag from one of the operands (but keep sign).
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009621 __ SmiUntag(right);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00009622 // Do multiplication.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009623 __ imul(right, Operand(left)); // Multiplication is commutative.
9624 __ j(overflow, &use_fp_on_smis, not_taken);
9625 // Check for negative zero result. Use combined = left | right.
9626 __ NegativeZeroTest(right, combined, &use_fp_on_smis);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00009627 break;
9628
9629 case Token::DIV:
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009630 // We can't revert the division if the result is not a smi so
9631 // save the left operand.
9632 __ mov(edi, left);
9633 // Check for 0 divisor.
9634 __ test(right, Operand(right));
9635 __ j(zero, &use_fp_on_smis, not_taken);
9636 // Sign extend left into edx:eax.
9637 ASSERT(left.is(eax));
9638 __ cdq();
9639 // Divide edx:eax by right.
9640 __ idiv(right);
9641 // Check for the corner case of dividing the most negative smi by
9642 // -1. We cannot use the overflow flag, since it is not set by idiv
9643 // instruction.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00009644 ASSERT(kSmiTag == 0 && kSmiTagSize == 1);
9645 __ cmp(eax, 0x40000000);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009646 __ j(equal, &use_fp_on_smis);
9647 // Check for negative zero result. Use combined = left | right.
9648 __ NegativeZeroTest(eax, combined, &use_fp_on_smis);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00009649 // Check that the remainder is zero.
9650 __ test(edx, Operand(edx));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009651 __ j(not_zero, &use_fp_on_smis);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00009652 // Tag the result and store it in register eax.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00009653 __ SmiTag(eax);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00009654 break;
9655
9656 case Token::MOD:
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009657 // Check for 0 divisor.
9658 __ test(right, Operand(right));
9659 __ j(zero, &not_smis, not_taken);
9660
9661 // Sign extend left into edx:eax.
9662 ASSERT(left.is(eax));
9663 __ cdq();
9664 // Divide edx:eax by right.
9665 __ idiv(right);
9666 // Check for negative zero result. Use combined = left | right.
9667 __ NegativeZeroTest(edx, combined, slow);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00009668 // Move remainder to register eax.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009669 __ mov(eax, edx);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00009670 break;
9671
9672 default:
9673 UNREACHABLE();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009674 }
9675
9676 // 5. Emit return of result in eax.
9677 GenerateReturn(masm);
9678
9679 // 6. For some operations emit inline code to perform floating point
9680 // operations on known smis (e.g., if the result of the operation
9681 // overflowed the smi range).
9682 switch (op_) {
9683 case Token::SHL: {
9684 Comment perform_float(masm, "-- Perform float operation on smis");
9685 __ bind(&use_fp_on_smis);
9686 // Result we want is in left == edx, so we can put the allocated heap
9687 // number in eax.
9688 __ AllocateHeapNumber(eax, ecx, ebx, slow);
9689 // Store the result in the HeapNumber and return.
9690 if (CpuFeatures::IsSupported(SSE2)) {
9691 CpuFeatures::Scope use_sse2(SSE2);
9692 __ cvtsi2sd(xmm0, Operand(left));
9693 __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0);
9694 } else {
9695 // It's OK to overwrite the right argument on the stack because we
9696 // are about to return.
9697 __ mov(Operand(esp, 1 * kPointerSize), left);
9698 __ fild_s(Operand(esp, 1 * kPointerSize));
9699 __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
9700 }
9701 GenerateReturn(masm);
9702 break;
9703 }
9704
9705 case Token::ADD:
9706 case Token::SUB:
9707 case Token::MUL:
9708 case Token::DIV: {
9709 Comment perform_float(masm, "-- Perform float operation on smis");
9710 __ bind(&use_fp_on_smis);
9711 // Restore arguments to edx, eax.
9712 switch (op_) {
9713 case Token::ADD:
9714 // Revert right = right + left.
9715 __ sub(right, Operand(left));
9716 break;
9717 case Token::SUB:
9718 // Revert left = left - right.
9719 __ add(left, Operand(right));
9720 break;
9721 case Token::MUL:
9722 // Right was clobbered but a copy is in ebx.
9723 __ mov(right, ebx);
9724 break;
9725 case Token::DIV:
9726 // Left was clobbered but a copy is in edi. Right is in ebx for
9727 // division.
9728 __ mov(edx, edi);
9729 __ mov(eax, right);
9730 break;
9731 default: UNREACHABLE();
9732 break;
9733 }
9734 __ AllocateHeapNumber(ecx, ebx, no_reg, slow);
9735 if (CpuFeatures::IsSupported(SSE2)) {
9736 CpuFeatures::Scope use_sse2(SSE2);
9737 FloatingPointHelper::LoadSSE2Smis(masm, ebx);
9738 switch (op_) {
9739 case Token::ADD: __ addsd(xmm0, xmm1); break;
9740 case Token::SUB: __ subsd(xmm0, xmm1); break;
9741 case Token::MUL: __ mulsd(xmm0, xmm1); break;
9742 case Token::DIV: __ divsd(xmm0, xmm1); break;
9743 default: UNREACHABLE();
9744 }
9745 __ movdbl(FieldOperand(ecx, HeapNumber::kValueOffset), xmm0);
9746 } else { // SSE2 not available, use FPU.
9747 FloatingPointHelper::LoadFloatSmis(masm, ebx);
9748 switch (op_) {
9749 case Token::ADD: __ faddp(1); break;
9750 case Token::SUB: __ fsubp(1); break;
9751 case Token::MUL: __ fmulp(1); break;
9752 case Token::DIV: __ fdivp(1); break;
9753 default: UNREACHABLE();
9754 }
9755 __ fstp_d(FieldOperand(ecx, HeapNumber::kValueOffset));
9756 }
9757 __ mov(eax, ecx);
9758 GenerateReturn(masm);
9759 break;
9760 }
9761
9762 default:
9763 break;
9764 }
9765
9766 // 7. Non-smi operands, fall out to the non-smi code with the operands in
9767 // edx and eax.
9768 Comment done_comment(masm, "-- Enter non-smi code");
9769 __ bind(&not_smis);
9770 switch (op_) {
9771 case Token::BIT_OR:
9772 case Token::SHL:
9773 case Token::SAR:
9774 case Token::SHR:
9775 // Right operand is saved in ecx and eax was destroyed by the smi
9776 // check.
9777 __ mov(eax, ecx);
9778 break;
9779
9780 case Token::DIV:
9781 case Token::MOD:
9782 // Operands are in eax, ebx at this point.
9783 __ mov(edx, eax);
9784 __ mov(eax, ebx);
9785 break;
9786
9787 default:
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00009788 break;
9789 }
9790}
9791
9792
9793void GenericBinaryOpStub::Generate(MacroAssembler* masm) {
9794 Label call_runtime;
9795
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00009796 __ IncrementCounter(&Counters::generic_binary_stub_calls, 1);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00009797
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00009798 // Generate fast case smi code if requested. This flag is set when the fast
9799 // case smi code is not generated by the caller. Generating it here will speed
9800 // up common operations.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009801 if (ShouldGenerateSmiCode()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009802 GenerateSmiCode(masm, &call_runtime);
9803 } else if (op_ != Token::MOD) { // MOD goes straight to runtime.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009804 if (!HasArgsInRegisters()) {
9805 GenerateLoadArguments(masm);
9806 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00009807 }
9808
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00009809 // Floating point case.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009810 if (ShouldGenerateFPCode()) {
9811 switch (op_) {
9812 case Token::ADD:
9813 case Token::SUB:
9814 case Token::MUL:
9815 case Token::DIV: {
9816 if (runtime_operands_type_ == BinaryOpIC::DEFAULT &&
9817 HasSmiCodeInStub()) {
9818 // Execution reaches this point when the first non-smi argument occurs
9819 // (and only if smi code is generated). This is the right moment to
9820 // patch to HEAP_NUMBERS state. The transition is attempted only for
9821 // the four basic operations. The stub stays in the DEFAULT state
9822 // forever for all other operations (also if smi code is skipped).
9823 GenerateTypeTransition(masm);
ager@chromium.org5c838252010-02-19 08:53:10 +00009824 }
ager@chromium.org4af710e2009-09-15 12:20:11 +00009825
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009826 Label not_floats;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00009827 if (CpuFeatures::IsSupported(SSE2)) {
9828 CpuFeatures::Scope use_sse2(SSE2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009829 if (static_operands_type_.IsNumber()) {
9830 if (FLAG_debug_code) {
9831 // Assert at runtime that inputs are only numbers.
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00009832 __ AbortIfNotNumber(edx);
9833 __ AbortIfNotNumber(eax);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009834 }
9835 if (static_operands_type_.IsSmi()) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00009836 if (FLAG_debug_code) {
9837 __ AbortIfNotSmi(edx);
9838 __ AbortIfNotSmi(eax);
9839 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009840 FloatingPointHelper::LoadSSE2Smis(masm, ecx);
9841 } else {
9842 FloatingPointHelper::LoadSSE2Operands(masm);
9843 }
9844 } else {
9845 FloatingPointHelper::LoadSSE2Operands(masm, &call_runtime);
9846 }
9847
9848 switch (op_) {
9849 case Token::ADD: __ addsd(xmm0, xmm1); break;
9850 case Token::SUB: __ subsd(xmm0, xmm1); break;
9851 case Token::MUL: __ mulsd(xmm0, xmm1); break;
9852 case Token::DIV: __ divsd(xmm0, xmm1); break;
9853 default: UNREACHABLE();
9854 }
9855 GenerateHeapResultAllocation(masm, &call_runtime);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00009856 __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009857 GenerateReturn(masm);
9858 } else { // SSE2 not available, use FPU.
9859 if (static_operands_type_.IsNumber()) {
9860 if (FLAG_debug_code) {
9861 // Assert at runtime that inputs are only numbers.
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00009862 __ AbortIfNotNumber(edx);
9863 __ AbortIfNotNumber(eax);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009864 }
9865 } else {
9866 FloatingPointHelper::CheckFloatOperands(masm, &call_runtime, ebx);
9867 }
9868 FloatingPointHelper::LoadFloatOperands(
9869 masm,
9870 ecx,
9871 FloatingPointHelper::ARGS_IN_REGISTERS);
9872 switch (op_) {
9873 case Token::ADD: __ faddp(1); break;
9874 case Token::SUB: __ fsubp(1); break;
9875 case Token::MUL: __ fmulp(1); break;
9876 case Token::DIV: __ fdivp(1); break;
9877 default: UNREACHABLE();
9878 }
9879 Label after_alloc_failure;
9880 GenerateHeapResultAllocation(masm, &after_alloc_failure);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00009881 __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009882 GenerateReturn(masm);
9883 __ bind(&after_alloc_failure);
9884 __ ffree();
9885 __ jmp(&call_runtime);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00009886 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009887 __ bind(&not_floats);
9888 if (runtime_operands_type_ == BinaryOpIC::DEFAULT &&
9889 !HasSmiCodeInStub()) {
9890 // Execution reaches this point when the first non-number argument
9891 // occurs (and only if smi code is skipped from the stub, otherwise
9892 // the patching has already been done earlier in this case branch).
9893 // Try patching to STRINGS for ADD operation.
9894 if (op_ == Token::ADD) {
9895 GenerateTypeTransition(masm);
9896 }
9897 }
9898 break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009899 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009900 case Token::MOD: {
9901 // For MOD we go directly to runtime in the non-smi case.
9902 break;
9903 }
9904 case Token::BIT_OR:
9905 case Token::BIT_AND:
9906 case Token::BIT_XOR:
9907 case Token::SAR:
9908 case Token::SHL:
9909 case Token::SHR: {
9910 Label non_smi_result;
9911 FloatingPointHelper::LoadAsIntegers(masm,
9912 static_operands_type_,
9913 use_sse3_,
9914 &call_runtime);
9915 switch (op_) {
9916 case Token::BIT_OR: __ or_(eax, Operand(ecx)); break;
9917 case Token::BIT_AND: __ and_(eax, Operand(ecx)); break;
9918 case Token::BIT_XOR: __ xor_(eax, Operand(ecx)); break;
9919 case Token::SAR: __ sar_cl(eax); break;
9920 case Token::SHL: __ shl_cl(eax); break;
9921 case Token::SHR: __ shr_cl(eax); break;
9922 default: UNREACHABLE();
9923 }
9924 if (op_ == Token::SHR) {
9925 // Check if result is non-negative and fits in a smi.
9926 __ test(eax, Immediate(0xc0000000));
9927 __ j(not_zero, &call_runtime);
9928 } else {
9929 // Check if result fits in a smi.
9930 __ cmp(eax, 0xc0000000);
9931 __ j(negative, &non_smi_result);
9932 }
9933 // Tag smi result and return.
9934 __ SmiTag(eax);
9935 GenerateReturn(masm);
9936
9937 // All ops except SHR return a signed int32 that we load in
9938 // a HeapNumber.
9939 if (op_ != Token::SHR) {
9940 __ bind(&non_smi_result);
9941 // Allocate a heap number if needed.
9942 __ mov(ebx, Operand(eax)); // ebx: result
9943 Label skip_allocation;
9944 switch (mode_) {
9945 case OVERWRITE_LEFT:
9946 case OVERWRITE_RIGHT:
9947 // If the operand was an object, we skip the
9948 // allocation of a heap number.
9949 __ mov(eax, Operand(esp, mode_ == OVERWRITE_RIGHT ?
9950 1 * kPointerSize : 2 * kPointerSize));
9951 __ test(eax, Immediate(kSmiTagMask));
9952 __ j(not_zero, &skip_allocation, not_taken);
9953 // Fall through!
9954 case NO_OVERWRITE:
9955 __ AllocateHeapNumber(eax, ecx, edx, &call_runtime);
9956 __ bind(&skip_allocation);
9957 break;
9958 default: UNREACHABLE();
9959 }
9960 // Store the result in the HeapNumber and return.
9961 if (CpuFeatures::IsSupported(SSE2)) {
9962 CpuFeatures::Scope use_sse2(SSE2);
9963 __ cvtsi2sd(xmm0, Operand(ebx));
9964 __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0);
9965 } else {
9966 __ mov(Operand(esp, 1 * kPointerSize), ebx);
9967 __ fild_s(Operand(esp, 1 * kPointerSize));
9968 __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
9969 }
9970 GenerateReturn(masm);
9971 }
9972 break;
9973 }
9974 default: UNREACHABLE(); break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009975 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009976 }
9977
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00009978 // If all else fails, use the runtime system to get the correct
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00009979 // result. If arguments was passed in registers now place them on the
ager@chromium.org3811b432009-10-28 14:53:37 +00009980 // stack in the correct order below the return address.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009981 __ bind(&call_runtime);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009982 if (HasArgsInRegisters()) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009983 GenerateRegisterArgsPush(masm);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00009984 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009985
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009986 switch (op_) {
ager@chromium.org3e875802009-06-29 08:26:34 +00009987 case Token::ADD: {
9988 // Test for string arguments before calling runtime.
ager@chromium.org5c838252010-02-19 08:53:10 +00009989 Label not_strings, not_string1, string1, string1_smi2;
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009990
9991 // If this stub has already generated FP-specific code then the arguments
9992 // are already in edx, eax
9993 if (!ShouldGenerateFPCode() && !HasArgsInRegisters()) {
9994 GenerateLoadArguments(masm);
9995 }
9996
sgjesse@chromium.orga24fdd52010-03-10 08:33:57 +00009997 // Registers containing left and right operands respectively.
9998 Register lhs, rhs;
9999 if (HasArgsReversed()) {
10000 lhs = eax;
10001 rhs = edx;
10002 } else {
10003 lhs = edx;
10004 rhs = eax;
10005 }
10006
10007 // Test if first argument is a string.
10008 __ test(lhs, Immediate(kSmiTagMask));
ager@chromium.org3e875802009-06-29 08:26:34 +000010009 __ j(zero, &not_string1);
sgjesse@chromium.orga24fdd52010-03-10 08:33:57 +000010010 __ CmpObjectType(lhs, FIRST_NONSTRING_TYPE, ecx);
ager@chromium.org3e875802009-06-29 08:26:34 +000010011 __ j(above_equal, &not_string1);
10012
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010013 // First argument is a string, test second.
sgjesse@chromium.orga24fdd52010-03-10 08:33:57 +000010014 __ test(rhs, Immediate(kSmiTagMask));
ager@chromium.org5c838252010-02-19 08:53:10 +000010015 __ j(zero, &string1_smi2);
sgjesse@chromium.orga24fdd52010-03-10 08:33:57 +000010016 __ CmpObjectType(rhs, FIRST_NONSTRING_TYPE, ecx);
ager@chromium.org3e875802009-06-29 08:26:34 +000010017 __ j(above_equal, &string1);
10018
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010019 // First and second argument are strings. Jump to the string add stub.
ager@chromium.org5c838252010-02-19 08:53:10 +000010020 StringAddStub string_add_stub(NO_STRING_CHECK_IN_STUB);
10021 __ TailCallStub(&string_add_stub);
ager@chromium.org3e875802009-06-29 08:26:34 +000010022
ager@chromium.org5c838252010-02-19 08:53:10 +000010023 __ bind(&string1_smi2);
10024 // First argument is a string, second is a smi. Try to lookup the number
10025 // string for the smi in the number string cache.
10026 NumberToStringStub::GenerateLookupNumberStringCache(
sgjesse@chromium.orga24fdd52010-03-10 08:33:57 +000010027 masm, rhs, edi, ebx, ecx, true, &string1);
ager@chromium.org5c838252010-02-19 08:53:10 +000010028
sgjesse@chromium.orga24fdd52010-03-10 08:33:57 +000010029 // Replace second argument on stack and tailcall string add stub to make
10030 // the result.
10031 __ mov(Operand(esp, 1 * kPointerSize), edi);
10032 __ TailCallStub(&string_add_stub);
ager@chromium.org5c838252010-02-19 08:53:10 +000010033
sgjesse@chromium.orga24fdd52010-03-10 08:33:57 +000010034 // Only first argument is a string.
ager@chromium.org3e875802009-06-29 08:26:34 +000010035 __ bind(&string1);
sgjesse@chromium.orga24fdd52010-03-10 08:33:57 +000010036 __ InvokeBuiltin(Builtins::STRING_ADD_LEFT, JUMP_FUNCTION);
ager@chromium.org3e875802009-06-29 08:26:34 +000010037
10038 // First argument was not a string, test second.
10039 __ bind(&not_string1);
sgjesse@chromium.orga24fdd52010-03-10 08:33:57 +000010040 __ test(rhs, Immediate(kSmiTagMask));
ager@chromium.org3e875802009-06-29 08:26:34 +000010041 __ j(zero, &not_strings);
sgjesse@chromium.orga24fdd52010-03-10 08:33:57 +000010042 __ CmpObjectType(rhs, FIRST_NONSTRING_TYPE, ecx);
ager@chromium.org3e875802009-06-29 08:26:34 +000010043 __ j(above_equal, &not_strings);
10044
10045 // Only second argument is a string.
sgjesse@chromium.orga24fdd52010-03-10 08:33:57 +000010046 __ InvokeBuiltin(Builtins::STRING_ADD_RIGHT, JUMP_FUNCTION);
ager@chromium.org3e875802009-06-29 08:26:34 +000010047
10048 __ bind(&not_strings);
10049 // Neither argument is a string.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000010050 __ InvokeBuiltin(Builtins::ADD, JUMP_FUNCTION);
10051 break;
ager@chromium.org3e875802009-06-29 08:26:34 +000010052 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000010053 case Token::SUB:
10054 __ InvokeBuiltin(Builtins::SUB, JUMP_FUNCTION);
10055 break;
10056 case Token::MUL:
10057 __ InvokeBuiltin(Builtins::MUL, JUMP_FUNCTION);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010058 break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000010059 case Token::DIV:
10060 __ InvokeBuiltin(Builtins::DIV, JUMP_FUNCTION);
10061 break;
10062 case Token::MOD:
10063 __ InvokeBuiltin(Builtins::MOD, JUMP_FUNCTION);
10064 break;
10065 case Token::BIT_OR:
10066 __ InvokeBuiltin(Builtins::BIT_OR, JUMP_FUNCTION);
10067 break;
10068 case Token::BIT_AND:
10069 __ InvokeBuiltin(Builtins::BIT_AND, JUMP_FUNCTION);
10070 break;
10071 case Token::BIT_XOR:
10072 __ InvokeBuiltin(Builtins::BIT_XOR, JUMP_FUNCTION);
10073 break;
10074 case Token::SAR:
10075 __ InvokeBuiltin(Builtins::SAR, JUMP_FUNCTION);
10076 break;
10077 case Token::SHL:
10078 __ InvokeBuiltin(Builtins::SHL, JUMP_FUNCTION);
10079 break;
10080 case Token::SHR:
10081 __ InvokeBuiltin(Builtins::SHR, JUMP_FUNCTION);
10082 break;
10083 default:
10084 UNREACHABLE();
10085 }
10086}
10087
10088
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010089void GenericBinaryOpStub::GenerateHeapResultAllocation(MacroAssembler* masm,
10090 Label* alloc_failure) {
10091 Label skip_allocation;
10092 OverwriteMode mode = mode_;
10093 if (HasArgsReversed()) {
10094 if (mode == OVERWRITE_RIGHT) {
10095 mode = OVERWRITE_LEFT;
10096 } else if (mode == OVERWRITE_LEFT) {
10097 mode = OVERWRITE_RIGHT;
10098 }
10099 }
10100 switch (mode) {
10101 case OVERWRITE_LEFT: {
10102 // If the argument in edx is already an object, we skip the
10103 // allocation of a heap number.
10104 __ test(edx, Immediate(kSmiTagMask));
10105 __ j(not_zero, &skip_allocation, not_taken);
10106 // Allocate a heap number for the result. Keep eax and edx intact
10107 // for the possible runtime call.
10108 __ AllocateHeapNumber(ebx, ecx, no_reg, alloc_failure);
10109 // Now edx can be overwritten losing one of the arguments as we are
10110 // now done and will not need it any more.
10111 __ mov(edx, Operand(ebx));
10112 __ bind(&skip_allocation);
10113 // Use object in edx as a result holder
10114 __ mov(eax, Operand(edx));
10115 break;
10116 }
10117 case OVERWRITE_RIGHT:
10118 // If the argument in eax is already an object, we skip the
10119 // allocation of a heap number.
10120 __ test(eax, Immediate(kSmiTagMask));
10121 __ j(not_zero, &skip_allocation, not_taken);
10122 // Fall through!
10123 case NO_OVERWRITE:
10124 // Allocate a heap number for the result. Keep eax and edx intact
10125 // for the possible runtime call.
10126 __ AllocateHeapNumber(ebx, ecx, no_reg, alloc_failure);
10127 // Now eax can be overwritten losing one of the arguments as we are
10128 // now done and will not need it any more.
10129 __ mov(eax, ebx);
10130 __ bind(&skip_allocation);
10131 break;
10132 default: UNREACHABLE();
10133 }
10134}
10135
10136
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +000010137void GenericBinaryOpStub::GenerateLoadArguments(MacroAssembler* masm) {
10138 // If arguments are not passed in registers read them from the stack.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010139 ASSERT(!HasArgsInRegisters());
10140 __ mov(eax, Operand(esp, 1 * kPointerSize));
10141 __ mov(edx, Operand(esp, 2 * kPointerSize));
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +000010142}
10143
10144
10145void GenericBinaryOpStub::GenerateReturn(MacroAssembler* masm) {
10146 // If arguments are not passed in registers remove them from the stack before
10147 // returning.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010148 if (!HasArgsInRegisters()) {
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +000010149 __ ret(2 * kPointerSize); // Remove both operands
10150 } else {
10151 __ ret(0);
10152 }
10153}
10154
10155
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010156void GenericBinaryOpStub::GenerateRegisterArgsPush(MacroAssembler* masm) {
10157 ASSERT(HasArgsInRegisters());
10158 __ pop(ecx);
10159 if (HasArgsReversed()) {
10160 __ push(eax);
10161 __ push(edx);
10162 } else {
10163 __ push(edx);
10164 __ push(eax);
10165 }
10166 __ push(ecx);
10167}
10168
10169
10170void GenericBinaryOpStub::GenerateTypeTransition(MacroAssembler* masm) {
10171 Label get_result;
10172
10173 // Keep a copy of operands on the stack and make sure they are also in
10174 // edx, eax.
10175 if (HasArgsInRegisters()) {
10176 GenerateRegisterArgsPush(masm);
10177 } else {
10178 GenerateLoadArguments(masm);
10179 }
10180
10181 // Internal frame is necessary to handle exceptions properly.
10182 __ EnterInternalFrame();
10183
10184 // Push arguments on stack if the stub expects them there.
10185 if (!HasArgsInRegisters()) {
10186 __ push(edx);
10187 __ push(eax);
10188 }
10189 // Call the stub proper to get the result in eax.
10190 __ call(&get_result);
10191 __ LeaveInternalFrame();
10192
10193 __ pop(ecx); // Return address.
10194 // Left and right arguments are now on top.
10195 // Push the operation result. The tail call to BinaryOp_Patch will
10196 // return it to the original caller.
10197 __ push(eax);
10198 // Push this stub's key. Although the operation and the type info are
10199 // encoded into the key, the encoding is opaque, so push them too.
10200 __ push(Immediate(Smi::FromInt(MinorKey())));
10201 __ push(Immediate(Smi::FromInt(op_)));
10202 __ push(Immediate(Smi::FromInt(runtime_operands_type_)));
10203
10204 __ push(ecx); // Return address.
10205
10206 // Patch the caller to an appropriate specialized stub
10207 // and return the operation result.
10208 __ TailCallExternalReference(
10209 ExternalReference(IC_Utility(IC::kBinaryOp_Patch)),
10210 6,
10211 1);
10212
10213 // The entry point for the result calculation is assumed to be immediately
10214 // after this sequence.
10215 __ bind(&get_result);
10216}
10217
fschneider@chromium.org086aac62010-03-17 13:18:24 +000010218
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010219Handle<Code> GetBinaryOpStub(int key, BinaryOpIC::TypeInfo type_info) {
10220 GenericBinaryOpStub stub(key, type_info);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010221 return stub.GetCode();
10222}
10223
10224
10225void TranscendentalCacheStub::Generate(MacroAssembler* masm) {
10226 // Input on stack:
10227 // esp[4]: argument (should be number).
10228 // esp[0]: return address.
10229 // Test that eax is a number.
10230 Label runtime_call;
10231 Label runtime_call_clear_stack;
10232 Label input_not_smi;
10233 Label loaded;
10234 __ mov(eax, Operand(esp, kPointerSize));
10235 __ test(eax, Immediate(kSmiTagMask));
10236 __ j(not_zero, &input_not_smi);
10237 // Input is a smi. Untag and load it onto the FPU stack.
10238 // Then load the low and high words of the double into ebx, edx.
10239 ASSERT_EQ(1, kSmiTagSize);
10240 __ sar(eax, 1);
10241 __ sub(Operand(esp), Immediate(2 * kPointerSize));
10242 __ mov(Operand(esp, 0), eax);
10243 __ fild_s(Operand(esp, 0));
10244 __ fst_d(Operand(esp, 0));
10245 __ pop(edx);
10246 __ pop(ebx);
10247 __ jmp(&loaded);
10248 __ bind(&input_not_smi);
10249 // Check if input is a HeapNumber.
10250 __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset));
10251 __ cmp(Operand(ebx), Immediate(Factory::heap_number_map()));
10252 __ j(not_equal, &runtime_call);
10253 // Input is a HeapNumber. Push it on the FPU stack and load its
10254 // low and high words into ebx, edx.
10255 __ fld_d(FieldOperand(eax, HeapNumber::kValueOffset));
10256 __ mov(edx, FieldOperand(eax, HeapNumber::kExponentOffset));
10257 __ mov(ebx, FieldOperand(eax, HeapNumber::kMantissaOffset));
10258
10259 __ bind(&loaded);
10260 // ST[0] == double value
10261 // ebx = low 32 bits of double value
10262 // edx = high 32 bits of double value
10263 // Compute hash:
10264 // h = (low ^ high); h ^= h >> 16; h ^= h >> 8; h = h & (cacheSize - 1);
10265 __ mov(ecx, ebx);
10266 __ xor_(ecx, Operand(edx));
10267 __ mov(eax, ecx);
10268 __ sar(eax, 16);
10269 __ xor_(ecx, Operand(eax));
10270 __ mov(eax, ecx);
10271 __ sar(eax, 8);
10272 __ xor_(ecx, Operand(eax));
10273 ASSERT(IsPowerOf2(TranscendentalCache::kCacheSize));
10274 __ and_(Operand(ecx), Immediate(TranscendentalCache::kCacheSize - 1));
10275 // ST[0] == double value.
10276 // ebx = low 32 bits of double value.
10277 // edx = high 32 bits of double value.
10278 // ecx = TranscendentalCache::hash(double value).
10279 __ mov(eax,
10280 Immediate(ExternalReference::transcendental_cache_array_address()));
10281 // Eax points to cache array.
10282 __ mov(eax, Operand(eax, type_ * sizeof(TranscendentalCache::caches_[0])));
10283 // Eax points to the cache for the type type_.
10284 // If NULL, the cache hasn't been initialized yet, so go through runtime.
10285 __ test(eax, Operand(eax));
10286 __ j(zero, &runtime_call_clear_stack);
10287#ifdef DEBUG
10288 // Check that the layout of cache elements match expectations.
fschneider@chromium.org013f3e12010-04-26 13:27:52 +000010289 { TranscendentalCache::Element test_elem[2];
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010290 char* elem_start = reinterpret_cast<char*>(&test_elem[0]);
10291 char* elem2_start = reinterpret_cast<char*>(&test_elem[1]);
10292 char* elem_in0 = reinterpret_cast<char*>(&(test_elem[0].in[0]));
10293 char* elem_in1 = reinterpret_cast<char*>(&(test_elem[0].in[1]));
10294 char* elem_out = reinterpret_cast<char*>(&(test_elem[0].output));
10295 CHECK_EQ(12, elem2_start - elem_start); // Two uint_32's and a pointer.
10296 CHECK_EQ(0, elem_in0 - elem_start);
10297 CHECK_EQ(kIntSize, elem_in1 - elem_start);
10298 CHECK_EQ(2 * kIntSize, elem_out - elem_start);
10299 }
10300#endif
10301 // Find the address of the ecx'th entry in the cache, i.e., &eax[ecx*12].
10302 __ lea(ecx, Operand(ecx, ecx, times_2, 0));
10303 __ lea(ecx, Operand(eax, ecx, times_4, 0));
10304 // Check if cache matches: Double value is stored in uint32_t[2] array.
10305 Label cache_miss;
10306 __ cmp(ebx, Operand(ecx, 0));
10307 __ j(not_equal, &cache_miss);
10308 __ cmp(edx, Operand(ecx, kIntSize));
10309 __ j(not_equal, &cache_miss);
10310 // Cache hit!
10311 __ mov(eax, Operand(ecx, 2 * kIntSize));
10312 __ fstp(0);
10313 __ ret(kPointerSize);
10314
10315 __ bind(&cache_miss);
10316 // Update cache with new value.
10317 // We are short on registers, so use no_reg as scratch.
10318 // This gives slightly larger code.
10319 __ AllocateHeapNumber(eax, edi, no_reg, &runtime_call_clear_stack);
10320 GenerateOperation(masm);
10321 __ mov(Operand(ecx, 0), ebx);
10322 __ mov(Operand(ecx, kIntSize), edx);
10323 __ mov(Operand(ecx, 2 * kIntSize), eax);
10324 __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
10325 __ ret(kPointerSize);
10326
10327 __ bind(&runtime_call_clear_stack);
10328 __ fstp(0);
10329 __ bind(&runtime_call);
10330 __ TailCallExternalReference(ExternalReference(RuntimeFunction()), 1, 1);
10331}
10332
10333
10334Runtime::FunctionId TranscendentalCacheStub::RuntimeFunction() {
10335 switch (type_) {
10336 // Add more cases when necessary.
10337 case TranscendentalCache::SIN: return Runtime::kMath_sin;
10338 case TranscendentalCache::COS: return Runtime::kMath_cos;
10339 default:
10340 UNIMPLEMENTED();
10341 return Runtime::kAbort;
10342 }
10343}
10344
10345
10346void TranscendentalCacheStub::GenerateOperation(MacroAssembler* masm) {
10347 // Only free register is edi.
10348 Label done;
10349 ASSERT(type_ == TranscendentalCache::SIN ||
10350 type_ == TranscendentalCache::COS);
10351 // More transcendental types can be added later.
10352
10353 // Both fsin and fcos require arguments in the range +/-2^63 and
10354 // return NaN for infinities and NaN. They can share all code except
10355 // the actual fsin/fcos operation.
10356 Label in_range;
10357 // If argument is outside the range -2^63..2^63, fsin/cos doesn't
10358 // work. We must reduce it to the appropriate range.
10359 __ mov(edi, edx);
10360 __ and_(Operand(edi), Immediate(0x7ff00000)); // Exponent only.
10361 int supported_exponent_limit =
10362 (63 + HeapNumber::kExponentBias) << HeapNumber::kExponentShift;
10363 __ cmp(Operand(edi), Immediate(supported_exponent_limit));
10364 __ j(below, &in_range, taken);
10365 // Check for infinity and NaN. Both return NaN for sin.
10366 __ cmp(Operand(edi), Immediate(0x7ff00000));
10367 Label non_nan_result;
10368 __ j(not_equal, &non_nan_result, taken);
10369 // Input is +/-Infinity or NaN. Result is NaN.
10370 __ fstp(0);
10371 // NaN is represented by 0x7ff8000000000000.
10372 __ push(Immediate(0x7ff80000));
10373 __ push(Immediate(0));
10374 __ fld_d(Operand(esp, 0));
10375 __ add(Operand(esp), Immediate(2 * kPointerSize));
10376 __ jmp(&done);
10377
10378 __ bind(&non_nan_result);
10379
10380 // Use fpmod to restrict argument to the range +/-2*PI.
10381 __ mov(edi, eax); // Save eax before using fnstsw_ax.
10382 __ fldpi();
10383 __ fadd(0);
10384 __ fld(1);
10385 // FPU Stack: input, 2*pi, input.
10386 {
10387 Label no_exceptions;
10388 __ fwait();
10389 __ fnstsw_ax();
10390 // Clear if Illegal Operand or Zero Division exceptions are set.
10391 __ test(Operand(eax), Immediate(5));
10392 __ j(zero, &no_exceptions);
10393 __ fnclex();
10394 __ bind(&no_exceptions);
10395 }
10396
10397 // Compute st(0) % st(1)
10398 {
10399 Label partial_remainder_loop;
10400 __ bind(&partial_remainder_loop);
10401 __ fprem1();
10402 __ fwait();
10403 __ fnstsw_ax();
10404 __ test(Operand(eax), Immediate(0x400 /* C2 */));
10405 // If C2 is set, computation only has partial result. Loop to
10406 // continue computation.
10407 __ j(not_zero, &partial_remainder_loop);
10408 }
10409 // FPU Stack: input, 2*pi, input % 2*pi
10410 __ fstp(2);
10411 __ fstp(0);
10412 __ mov(eax, edi); // Restore eax (allocated HeapNumber pointer).
10413
10414 // FPU Stack: input % 2*pi
10415 __ bind(&in_range);
10416 switch (type_) {
10417 case TranscendentalCache::SIN:
10418 __ fsin();
10419 break;
10420 case TranscendentalCache::COS:
10421 __ fcos();
10422 break;
10423 default:
10424 UNREACHABLE();
10425 }
10426 __ bind(&done);
10427}
10428
10429
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000010430// Get the integer part of a heap number. Surprisingly, all this bit twiddling
10431// is faster than using the built-in instructions on floating point registers.
10432// Trashes edi and ebx. Dest is ecx. Source cannot be ecx or one of the
10433// trashed registers.
10434void IntegerConvert(MacroAssembler* masm,
10435 Register source,
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +000010436 TypeInfo type_info,
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000010437 bool use_sse3,
10438 Label* conversion_failure) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010439 ASSERT(!source.is(ecx) && !source.is(edi) && !source.is(ebx));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000010440 Label done, right_exponent, normal_exponent;
10441 Register scratch = ebx;
10442 Register scratch2 = edi;
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +000010443 if (type_info.IsInteger32() && CpuFeatures::IsEnabled(SSE2)) {
10444 CpuFeatures::Scope scope(SSE2);
10445 __ cvttsd2si(ecx, FieldOperand(source, HeapNumber::kValueOffset));
10446 return;
10447 }
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +000010448 if (!type_info.IsInteger32() || !use_sse3) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010449 // Get exponent word.
10450 __ mov(scratch, FieldOperand(source, HeapNumber::kExponentOffset));
10451 // Get exponent alone in scratch2.
10452 __ mov(scratch2, scratch);
10453 __ and_(scratch2, HeapNumber::kExponentMask);
10454 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000010455 if (use_sse3) {
10456 CpuFeatures::Scope scope(SSE3);
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +000010457 if (!type_info.IsInteger32()) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010458 // Check whether the exponent is too big for a 64 bit signed integer.
10459 static const uint32_t kTooBigExponent =
10460 (HeapNumber::kExponentBias + 63) << HeapNumber::kExponentShift;
10461 __ cmp(Operand(scratch2), Immediate(kTooBigExponent));
10462 __ j(greater_equal, conversion_failure);
10463 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000010464 // Load x87 register with heap number.
10465 __ fld_d(FieldOperand(source, HeapNumber::kValueOffset));
10466 // Reserve space for 64 bit answer.
10467 __ sub(Operand(esp), Immediate(sizeof(uint64_t))); // Nolint.
10468 // Do conversion, which cannot fail because we checked the exponent.
10469 __ fisttp_d(Operand(esp, 0));
10470 __ mov(ecx, Operand(esp, 0)); // Load low word of answer into ecx.
10471 __ add(Operand(esp), Immediate(sizeof(uint64_t))); // Nolint.
10472 } else {
10473 // Load ecx with zero. We use this either for the final shift or
10474 // for the answer.
10475 __ xor_(ecx, Operand(ecx));
10476 // Check whether the exponent matches a 32 bit signed int that cannot be
10477 // represented by a Smi. A non-smi 32 bit integer is 1.xxx * 2^30 so the
10478 // exponent is 30 (biased). This is the exponent that we are fastest at and
10479 // also the highest exponent we can handle here.
10480 const uint32_t non_smi_exponent =
10481 (HeapNumber::kExponentBias + 30) << HeapNumber::kExponentShift;
10482 __ cmp(Operand(scratch2), Immediate(non_smi_exponent));
10483 // If we have a match of the int32-but-not-Smi exponent then skip some
10484 // logic.
10485 __ j(equal, &right_exponent);
10486 // If the exponent is higher than that then go to slow case. This catches
10487 // numbers that don't fit in a signed int32, infinities and NaNs.
10488 __ j(less, &normal_exponent);
10489
10490 {
10491 // Handle a big exponent. The only reason we have this code is that the
10492 // >>> operator has a tendency to generate numbers with an exponent of 31.
10493 const uint32_t big_non_smi_exponent =
10494 (HeapNumber::kExponentBias + 31) << HeapNumber::kExponentShift;
10495 __ cmp(Operand(scratch2), Immediate(big_non_smi_exponent));
10496 __ j(not_equal, conversion_failure);
10497 // We have the big exponent, typically from >>>. This means the number is
10498 // in the range 2^31 to 2^32 - 1. Get the top bits of the mantissa.
10499 __ mov(scratch2, scratch);
10500 __ and_(scratch2, HeapNumber::kMantissaMask);
10501 // Put back the implicit 1.
10502 __ or_(scratch2, 1 << HeapNumber::kExponentShift);
10503 // Shift up the mantissa bits to take up the space the exponent used to
10504 // take. We just orred in the implicit bit so that took care of one and
10505 // we want to use the full unsigned range so we subtract 1 bit from the
10506 // shift distance.
10507 const int big_shift_distance = HeapNumber::kNonMantissaBitsInTopWord - 1;
10508 __ shl(scratch2, big_shift_distance);
10509 // Get the second half of the double.
10510 __ mov(ecx, FieldOperand(source, HeapNumber::kMantissaOffset));
10511 // Shift down 21 bits to get the most significant 11 bits or the low
10512 // mantissa word.
10513 __ shr(ecx, 32 - big_shift_distance);
10514 __ or_(ecx, Operand(scratch2));
10515 // We have the answer in ecx, but we may need to negate it.
10516 __ test(scratch, Operand(scratch));
10517 __ j(positive, &done);
10518 __ neg(ecx);
10519 __ jmp(&done);
10520 }
10521
10522 __ bind(&normal_exponent);
10523 // Exponent word in scratch, exponent part of exponent word in scratch2.
10524 // Zero in ecx.
10525 // We know the exponent is smaller than 30 (biased). If it is less than
10526 // 0 (biased) then the number is smaller in magnitude than 1.0 * 2^0, ie
10527 // it rounds to zero.
10528 const uint32_t zero_exponent =
10529 (HeapNumber::kExponentBias + 0) << HeapNumber::kExponentShift;
10530 __ sub(Operand(scratch2), Immediate(zero_exponent));
10531 // ecx already has a Smi zero.
10532 __ j(less, &done);
10533
10534 // We have a shifted exponent between 0 and 30 in scratch2.
10535 __ shr(scratch2, HeapNumber::kExponentShift);
10536 __ mov(ecx, Immediate(30));
10537 __ sub(ecx, Operand(scratch2));
10538
10539 __ bind(&right_exponent);
10540 // Here ecx is the shift, scratch is the exponent word.
10541 // Get the top bits of the mantissa.
10542 __ and_(scratch, HeapNumber::kMantissaMask);
10543 // Put back the implicit 1.
10544 __ or_(scratch, 1 << HeapNumber::kExponentShift);
10545 // Shift up the mantissa bits to take up the space the exponent used to
10546 // take. We have kExponentShift + 1 significant bits int he low end of the
10547 // word. Shift them to the top bits.
10548 const int shift_distance = HeapNumber::kNonMantissaBitsInTopWord - 2;
10549 __ shl(scratch, shift_distance);
10550 // Get the second half of the double. For some exponents we don't
10551 // actually need this because the bits get shifted out again, but
10552 // it's probably slower to test than just to do it.
10553 __ mov(scratch2, FieldOperand(source, HeapNumber::kMantissaOffset));
10554 // Shift down 22 bits to get the most significant 10 bits or the low
10555 // mantissa word.
10556 __ shr(scratch2, 32 - shift_distance);
10557 __ or_(scratch2, Operand(scratch));
10558 // Move down according to the exponent.
10559 __ shr_cl(scratch2);
10560 // Now the unsigned answer is in scratch2. We need to move it to ecx and
10561 // we may need to fix the sign.
10562 Label negative;
10563 __ xor_(ecx, Operand(ecx));
10564 __ cmp(ecx, FieldOperand(source, HeapNumber::kExponentOffset));
10565 __ j(greater, &negative);
10566 __ mov(ecx, scratch2);
10567 __ jmp(&done);
10568 __ bind(&negative);
10569 __ sub(ecx, Operand(scratch2));
10570 __ bind(&done);
10571 }
10572}
10573
10574
10575// Input: edx, eax are the left and right objects of a bit op.
10576// Output: eax, ecx are left and right integers for a bit op.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010577void FloatingPointHelper::LoadNumbersAsIntegers(MacroAssembler* masm,
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +000010578 TypeInfo type_info,
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010579 bool use_sse3,
10580 Label* conversion_failure) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000010581 // Check float operands.
10582 Label arg1_is_object, check_undefined_arg1;
10583 Label arg2_is_object, check_undefined_arg2;
10584 Label load_arg2, done;
10585
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +000010586 if (!type_info.IsDouble()) {
10587 if (!type_info.IsSmi()) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010588 __ test(edx, Immediate(kSmiTagMask));
10589 __ j(not_zero, &arg1_is_object);
whesse@chromium.orgcec079d2010-03-22 14:44:04 +000010590 } else {
10591 if (FLAG_debug_code) __ AbortIfNotSmi(edx);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010592 }
10593 __ SmiUntag(edx);
10594 __ jmp(&load_arg2);
10595 }
10596
10597 __ bind(&arg1_is_object);
10598
10599 // Get the untagged integer version of the edx heap number in ecx.
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +000010600 IntegerConvert(masm, edx, type_info, use_sse3, conversion_failure);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010601 __ mov(edx, ecx);
10602
10603 // Here edx has the untagged integer, eax has a Smi or a heap number.
10604 __ bind(&load_arg2);
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +000010605 if (!type_info.IsDouble()) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010606 // Test if arg2 is a Smi.
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +000010607 if (!type_info.IsSmi()) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010608 __ test(eax, Immediate(kSmiTagMask));
10609 __ j(not_zero, &arg2_is_object);
whesse@chromium.orgcec079d2010-03-22 14:44:04 +000010610 } else {
10611 if (FLAG_debug_code) __ AbortIfNotSmi(eax);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010612 }
10613 __ SmiUntag(eax);
10614 __ mov(ecx, eax);
10615 __ jmp(&done);
10616 }
10617
10618 __ bind(&arg2_is_object);
10619
10620 // Get the untagged integer version of the eax heap number in ecx.
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +000010621 IntegerConvert(masm, eax, type_info, use_sse3, conversion_failure);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010622 __ bind(&done);
10623 __ mov(eax, edx);
10624}
10625
10626
10627// Input: edx, eax are the left and right objects of a bit op.
10628// Output: eax, ecx are left and right integers for a bit op.
10629void FloatingPointHelper::LoadUnknownsAsIntegers(MacroAssembler* masm,
10630 bool use_sse3,
10631 Label* conversion_failure) {
10632 // Check float operands.
10633 Label arg1_is_object, check_undefined_arg1;
10634 Label arg2_is_object, check_undefined_arg2;
10635 Label load_arg2, done;
10636
10637 // Test if arg1 is a Smi.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000010638 __ test(edx, Immediate(kSmiTagMask));
10639 __ j(not_zero, &arg1_is_object);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010640
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000010641 __ SmiUntag(edx);
10642 __ jmp(&load_arg2);
10643
10644 // If the argument is undefined it converts to zero (ECMA-262, section 9.5).
10645 __ bind(&check_undefined_arg1);
10646 __ cmp(edx, Factory::undefined_value());
10647 __ j(not_equal, conversion_failure);
10648 __ mov(edx, Immediate(0));
10649 __ jmp(&load_arg2);
10650
10651 __ bind(&arg1_is_object);
10652 __ mov(ebx, FieldOperand(edx, HeapObject::kMapOffset));
10653 __ cmp(ebx, Factory::heap_number_map());
10654 __ j(not_equal, &check_undefined_arg1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010655
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000010656 // Get the untagged integer version of the edx heap number in ecx.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010657 IntegerConvert(masm,
10658 edx,
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +000010659 TypeInfo::Unknown(),
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010660 use_sse3,
10661 conversion_failure);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000010662 __ mov(edx, ecx);
10663
10664 // Here edx has the untagged integer, eax has a Smi or a heap number.
10665 __ bind(&load_arg2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010666
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000010667 // Test if arg2 is a Smi.
10668 __ test(eax, Immediate(kSmiTagMask));
10669 __ j(not_zero, &arg2_is_object);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010670
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000010671 __ SmiUntag(eax);
10672 __ mov(ecx, eax);
10673 __ jmp(&done);
10674
10675 // If the argument is undefined it converts to zero (ECMA-262, section 9.5).
10676 __ bind(&check_undefined_arg2);
10677 __ cmp(eax, Factory::undefined_value());
10678 __ j(not_equal, conversion_failure);
10679 __ mov(ecx, Immediate(0));
10680 __ jmp(&done);
10681
10682 __ bind(&arg2_is_object);
10683 __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset));
10684 __ cmp(ebx, Factory::heap_number_map());
10685 __ j(not_equal, &check_undefined_arg2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010686
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000010687 // Get the untagged integer version of the eax heap number in ecx.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010688 IntegerConvert(masm,
10689 eax,
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +000010690 TypeInfo::Unknown(),
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010691 use_sse3,
10692 conversion_failure);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000010693 __ bind(&done);
10694 __ mov(eax, edx);
10695}
10696
10697
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010698void FloatingPointHelper::LoadAsIntegers(MacroAssembler* masm,
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +000010699 TypeInfo type_info,
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010700 bool use_sse3,
10701 Label* conversion_failure) {
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +000010702 if (type_info.IsNumber()) {
10703 LoadNumbersAsIntegers(masm, type_info, use_sse3, conversion_failure);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010704 } else {
10705 LoadUnknownsAsIntegers(masm, use_sse3, conversion_failure);
10706 }
10707}
10708
10709
ager@chromium.orgeadaf222009-06-16 09:43:10 +000010710void FloatingPointHelper::LoadFloatOperand(MacroAssembler* masm,
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +000010711 Register number) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +000010712 Label load_smi, done;
10713
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +000010714 __ test(number, Immediate(kSmiTagMask));
ager@chromium.orgeadaf222009-06-16 09:43:10 +000010715 __ j(zero, &load_smi, not_taken);
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +000010716 __ fld_d(FieldOperand(number, HeapNumber::kValueOffset));
ager@chromium.orgeadaf222009-06-16 09:43:10 +000010717 __ jmp(&done);
10718
10719 __ bind(&load_smi);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000010720 __ SmiUntag(number);
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +000010721 __ push(number);
ager@chromium.orgeadaf222009-06-16 09:43:10 +000010722 __ fild_s(Operand(esp, 0));
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +000010723 __ pop(number);
ager@chromium.orgeadaf222009-06-16 09:43:10 +000010724
10725 __ bind(&done);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000010726}
10727
10728
ager@chromium.org5c838252010-02-19 08:53:10 +000010729void FloatingPointHelper::LoadSSE2Operands(MacroAssembler* masm) {
10730 Label load_smi_edx, load_eax, load_smi_eax, done;
10731 // Load operand in edx into xmm0.
10732 __ test(edx, Immediate(kSmiTagMask));
10733 __ j(zero, &load_smi_edx, not_taken); // Argument in edx is a smi.
10734 __ movdbl(xmm0, FieldOperand(edx, HeapNumber::kValueOffset));
10735
10736 __ bind(&load_eax);
10737 // Load operand in eax into xmm1.
10738 __ test(eax, Immediate(kSmiTagMask));
10739 __ j(zero, &load_smi_eax, not_taken); // Argument in eax is a smi.
10740 __ movdbl(xmm1, FieldOperand(eax, HeapNumber::kValueOffset));
10741 __ jmp(&done);
10742
10743 __ bind(&load_smi_edx);
10744 __ SmiUntag(edx); // Untag smi before converting to float.
10745 __ cvtsi2sd(xmm0, Operand(edx));
10746 __ SmiTag(edx); // Retag smi for heap number overwriting test.
10747 __ jmp(&load_eax);
10748
10749 __ bind(&load_smi_eax);
10750 __ SmiUntag(eax); // Untag smi before converting to float.
10751 __ cvtsi2sd(xmm1, Operand(eax));
10752 __ SmiTag(eax); // Retag smi for heap number overwriting test.
10753
10754 __ bind(&done);
10755}
10756
10757
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010758void FloatingPointHelper::LoadSSE2Operands(MacroAssembler* masm,
ager@chromium.org4af710e2009-09-15 12:20:11 +000010759 Label* not_numbers) {
10760 Label load_smi_edx, load_eax, load_smi_eax, load_float_eax, done;
10761 // Load operand in edx into xmm0, or branch to not_numbers.
10762 __ test(edx, Immediate(kSmiTagMask));
10763 __ j(zero, &load_smi_edx, not_taken); // Argument in edx is a smi.
10764 __ cmp(FieldOperand(edx, HeapObject::kMapOffset), Factory::heap_number_map());
10765 __ j(not_equal, not_numbers); // Argument in edx is not a number.
10766 __ movdbl(xmm0, FieldOperand(edx, HeapNumber::kValueOffset));
10767 __ bind(&load_eax);
10768 // Load operand in eax into xmm1, or branch to not_numbers.
10769 __ test(eax, Immediate(kSmiTagMask));
10770 __ j(zero, &load_smi_eax, not_taken); // Argument in eax is a smi.
10771 __ cmp(FieldOperand(eax, HeapObject::kMapOffset), Factory::heap_number_map());
10772 __ j(equal, &load_float_eax);
10773 __ jmp(not_numbers); // Argument in eax is not a number.
10774 __ bind(&load_smi_edx);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000010775 __ SmiUntag(edx); // Untag smi before converting to float.
ager@chromium.org4af710e2009-09-15 12:20:11 +000010776 __ cvtsi2sd(xmm0, Operand(edx));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000010777 __ SmiTag(edx); // Retag smi for heap number overwriting test.
ager@chromium.org4af710e2009-09-15 12:20:11 +000010778 __ jmp(&load_eax);
10779 __ bind(&load_smi_eax);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000010780 __ SmiUntag(eax); // Untag smi before converting to float.
ager@chromium.org4af710e2009-09-15 12:20:11 +000010781 __ cvtsi2sd(xmm1, Operand(eax));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000010782 __ SmiTag(eax); // Retag smi for heap number overwriting test.
ager@chromium.org4af710e2009-09-15 12:20:11 +000010783 __ jmp(&done);
10784 __ bind(&load_float_eax);
10785 __ movdbl(xmm1, FieldOperand(eax, HeapNumber::kValueOffset));
10786 __ bind(&done);
10787}
10788
10789
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010790void FloatingPointHelper::LoadSSE2Smis(MacroAssembler* masm,
10791 Register scratch) {
10792 const Register left = edx;
10793 const Register right = eax;
10794 __ mov(scratch, left);
10795 ASSERT(!scratch.is(right)); // We're about to clobber scratch.
10796 __ SmiUntag(scratch);
10797 __ cvtsi2sd(xmm0, Operand(scratch));
10798
10799 __ mov(scratch, right);
10800 __ SmiUntag(scratch);
10801 __ cvtsi2sd(xmm1, Operand(scratch));
10802}
10803
10804
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000010805void FloatingPointHelper::LoadFloatOperands(MacroAssembler* masm,
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010806 Register scratch,
10807 ArgLocation arg_location) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000010808 Label load_smi_1, load_smi_2, done_load_1, done;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010809 if (arg_location == ARGS_IN_REGISTERS) {
10810 __ mov(scratch, edx);
10811 } else {
10812 __ mov(scratch, Operand(esp, 2 * kPointerSize));
10813 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000010814 __ test(scratch, Immediate(kSmiTagMask));
10815 __ j(zero, &load_smi_1, not_taken);
10816 __ fld_d(FieldOperand(scratch, HeapNumber::kValueOffset));
10817 __ bind(&done_load_1);
10818
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010819 if (arg_location == ARGS_IN_REGISTERS) {
10820 __ mov(scratch, eax);
10821 } else {
10822 __ mov(scratch, Operand(esp, 1 * kPointerSize));
10823 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000010824 __ test(scratch, Immediate(kSmiTagMask));
10825 __ j(zero, &load_smi_2, not_taken);
10826 __ fld_d(FieldOperand(scratch, HeapNumber::kValueOffset));
10827 __ jmp(&done);
10828
10829 __ bind(&load_smi_1);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000010830 __ SmiUntag(scratch);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000010831 __ push(scratch);
10832 __ fild_s(Operand(esp, 0));
10833 __ pop(scratch);
10834 __ jmp(&done_load_1);
10835
10836 __ bind(&load_smi_2);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000010837 __ SmiUntag(scratch);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000010838 __ push(scratch);
10839 __ fild_s(Operand(esp, 0));
10840 __ pop(scratch);
10841
10842 __ bind(&done);
10843}
10844
10845
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010846void FloatingPointHelper::LoadFloatSmis(MacroAssembler* masm,
10847 Register scratch) {
10848 const Register left = edx;
10849 const Register right = eax;
10850 __ mov(scratch, left);
10851 ASSERT(!scratch.is(right)); // We're about to clobber scratch.
10852 __ SmiUntag(scratch);
10853 __ push(scratch);
10854 __ fild_s(Operand(esp, 0));
10855
10856 __ mov(scratch, right);
10857 __ SmiUntag(scratch);
10858 __ mov(Operand(esp, 0), scratch);
10859 __ fild_s(Operand(esp, 0));
10860 __ pop(scratch);
10861}
10862
10863
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000010864void FloatingPointHelper::CheckFloatOperands(MacroAssembler* masm,
10865 Label* non_float,
10866 Register scratch) {
10867 Label test_other, done;
10868 // Test if both operands are floats or smi -> scratch=k_is_float;
10869 // Otherwise scratch = k_not_float.
10870 __ test(edx, Immediate(kSmiTagMask));
10871 __ j(zero, &test_other, not_taken); // argument in edx is OK
10872 __ mov(scratch, FieldOperand(edx, HeapObject::kMapOffset));
10873 __ cmp(scratch, Factory::heap_number_map());
10874 __ j(not_equal, non_float); // argument in edx is not a number -> NaN
10875
10876 __ bind(&test_other);
10877 __ test(eax, Immediate(kSmiTagMask));
10878 __ j(zero, &done); // argument in eax is OK
10879 __ mov(scratch, FieldOperand(eax, HeapObject::kMapOffset));
10880 __ cmp(scratch, Factory::heap_number_map());
10881 __ j(not_equal, non_float); // argument in eax is not a number -> NaN
10882
10883 // Fall-through: Both operands are numbers.
10884 __ bind(&done);
10885}
10886
10887
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000010888void GenericUnaryOpStub::Generate(MacroAssembler* masm) {
10889 Label slow, done;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000010890
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000010891 if (op_ == Token::SUB) {
10892 // Check whether the value is a smi.
10893 Label try_float;
10894 __ test(eax, Immediate(kSmiTagMask));
10895 __ j(not_zero, &try_float, not_taken);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000010896
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000010897 // Go slow case if the value of the expression is zero
10898 // to make sure that we switch between 0 and -0.
10899 __ test(eax, Operand(eax));
10900 __ j(zero, &slow, not_taken);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000010901
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000010902 // The value of the expression is a smi that is not zero. Try
10903 // optimistic subtraction '0 - value'.
10904 Label undo;
ager@chromium.orgeadaf222009-06-16 09:43:10 +000010905 __ mov(edx, Operand(eax));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000010906 __ Set(eax, Immediate(0));
10907 __ sub(eax, Operand(edx));
10908 __ j(overflow, &undo, not_taken);
10909
10910 // If result is a smi we are done.
10911 __ test(eax, Immediate(kSmiTagMask));
10912 __ j(zero, &done, taken);
10913
10914 // Restore eax and go slow case.
10915 __ bind(&undo);
10916 __ mov(eax, Operand(edx));
10917 __ jmp(&slow);
10918
10919 // Try floating point case.
10920 __ bind(&try_float);
10921 __ mov(edx, FieldOperand(eax, HeapObject::kMapOffset));
10922 __ cmp(edx, Factory::heap_number_map());
10923 __ j(not_equal, &slow);
10924 if (overwrite_) {
10925 __ mov(edx, FieldOperand(eax, HeapNumber::kExponentOffset));
10926 __ xor_(edx, HeapNumber::kSignMask); // Flip sign.
10927 __ mov(FieldOperand(eax, HeapNumber::kExponentOffset), edx);
10928 } else {
10929 __ mov(edx, Operand(eax));
10930 // edx: operand
10931 __ AllocateHeapNumber(eax, ebx, ecx, &undo);
10932 // eax: allocated 'empty' number
10933 __ mov(ecx, FieldOperand(edx, HeapNumber::kExponentOffset));
10934 __ xor_(ecx, HeapNumber::kSignMask); // Flip sign.
10935 __ mov(FieldOperand(eax, HeapNumber::kExponentOffset), ecx);
10936 __ mov(ecx, FieldOperand(edx, HeapNumber::kMantissaOffset));
10937 __ mov(FieldOperand(eax, HeapNumber::kMantissaOffset), ecx);
10938 }
10939 } else if (op_ == Token::BIT_NOT) {
10940 // Check if the operand is a heap number.
10941 __ mov(edx, FieldOperand(eax, HeapObject::kMapOffset));
10942 __ cmp(edx, Factory::heap_number_map());
10943 __ j(not_equal, &slow, not_taken);
10944
10945 // Convert the heap number in eax to an untagged integer in ecx.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010946 IntegerConvert(masm,
10947 eax,
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +000010948 TypeInfo::Unknown(),
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010949 CpuFeatures::IsSupported(SSE3),
10950 &slow);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000010951
10952 // Do the bitwise operation and check if the result fits in a smi.
10953 Label try_float;
10954 __ not_(ecx);
10955 __ cmp(ecx, 0xc0000000);
10956 __ j(sign, &try_float, not_taken);
10957
10958 // Tag the result as a smi and we're done.
10959 ASSERT(kSmiTagSize == 1);
10960 __ lea(eax, Operand(ecx, times_2, kSmiTag));
10961 __ jmp(&done);
10962
10963 // Try to store the result in a heap number.
10964 __ bind(&try_float);
10965 if (!overwrite_) {
10966 // Allocate a fresh heap number, but don't overwrite eax until
10967 // we're sure we can do it without going through the slow case
10968 // that needs the value in eax.
10969 __ AllocateHeapNumber(ebx, edx, edi, &slow);
10970 __ mov(eax, Operand(ebx));
10971 }
10972 if (CpuFeatures::IsSupported(SSE2)) {
10973 CpuFeatures::Scope use_sse2(SSE2);
10974 __ cvtsi2sd(xmm0, Operand(ecx));
10975 __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0);
10976 } else {
10977 __ push(ecx);
10978 __ fild_s(Operand(esp, 0));
10979 __ pop(ecx);
10980 __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
10981 }
10982 } else {
10983 UNIMPLEMENTED();
ager@chromium.orgeadaf222009-06-16 09:43:10 +000010984 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000010985
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000010986 // Return from the stub.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000010987 __ bind(&done);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000010988 __ StubReturn(1);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000010989
10990 // Handle the slow case by jumping to the JavaScript builtin.
10991 __ bind(&slow);
10992 __ pop(ecx); // pop return address.
10993 __ push(eax);
10994 __ push(ecx); // push return address
10995 switch (op_) {
10996 case Token::SUB:
10997 __ InvokeBuiltin(Builtins::UNARY_MINUS, JUMP_FUNCTION);
10998 break;
10999 case Token::BIT_NOT:
11000 __ InvokeBuiltin(Builtins::BIT_NOT, JUMP_FUNCTION);
11001 break;
11002 default:
11003 UNREACHABLE();
11004 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000011005}
11006
11007
ager@chromium.org7c537e22008-10-16 08:43:32 +000011008void ArgumentsAccessStub::GenerateReadElement(MacroAssembler* masm) {
ager@chromium.org41826e72009-03-30 13:30:57 +000011009 // The key is in edx and the parameter count is in eax.
11010
ager@chromium.org7c537e22008-10-16 08:43:32 +000011011 // The displacement is used for skipping the frame pointer on the
11012 // stack. It is the offset of the last parameter (if any) relative
11013 // to the frame pointer.
11014 static const int kDisplacement = 1 * kPointerSize;
11015
11016 // Check that the key is a smi.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000011017 Label slow;
ager@chromium.org41826e72009-03-30 13:30:57 +000011018 __ test(edx, Immediate(kSmiTagMask));
ager@chromium.org7c537e22008-10-16 08:43:32 +000011019 __ j(not_zero, &slow, not_taken);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000011020
11021 // Check if the calling frame is an arguments adaptor frame.
11022 Label adaptor;
ager@chromium.org41826e72009-03-30 13:30:57 +000011023 __ mov(ebx, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
11024 __ mov(ecx, Operand(ebx, StandardFrameConstants::kContextOffset));
ager@chromium.org18ad94b2009-09-02 08:22:29 +000011025 __ cmp(Operand(ecx), Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
ager@chromium.org7c537e22008-10-16 08:43:32 +000011026 __ j(equal, &adaptor);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000011027
ager@chromium.org7c537e22008-10-16 08:43:32 +000011028 // Check index against formal parameters count limit passed in
11029 // through register eax. Use unsigned comparison to get negative
11030 // check for free.
ager@chromium.org41826e72009-03-30 13:30:57 +000011031 __ cmp(edx, Operand(eax));
ager@chromium.org7c537e22008-10-16 08:43:32 +000011032 __ j(above_equal, &slow, not_taken);
11033
11034 // Read the argument from the stack and return it.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000011035 ASSERT(kSmiTagSize == 1 && kSmiTag == 0); // shifting code depends on this
ager@chromium.org41826e72009-03-30 13:30:57 +000011036 __ lea(ebx, Operand(ebp, eax, times_2, 0));
11037 __ neg(edx);
11038 __ mov(eax, Operand(ebx, edx, times_2, kDisplacement));
ager@chromium.org7c537e22008-10-16 08:43:32 +000011039 __ ret(0);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000011040
ager@chromium.org7c537e22008-10-16 08:43:32 +000011041 // Arguments adaptor case: Check index against actual arguments
11042 // limit found in the arguments adaptor frame. Use unsigned
11043 // comparison to get negative check for free.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000011044 __ bind(&adaptor);
ager@chromium.org41826e72009-03-30 13:30:57 +000011045 __ mov(ecx, Operand(ebx, ArgumentsAdaptorFrameConstants::kLengthOffset));
11046 __ cmp(edx, Operand(ecx));
ager@chromium.org7c537e22008-10-16 08:43:32 +000011047 __ j(above_equal, &slow, not_taken);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000011048
ager@chromium.org7c537e22008-10-16 08:43:32 +000011049 // Read the argument from the stack and return it.
11050 ASSERT(kSmiTagSize == 1 && kSmiTag == 0); // shifting code depends on this
ager@chromium.org41826e72009-03-30 13:30:57 +000011051 __ lea(ebx, Operand(ebx, ecx, times_2, 0));
11052 __ neg(edx);
11053 __ mov(eax, Operand(ebx, edx, times_2, kDisplacement));
ager@chromium.org7c537e22008-10-16 08:43:32 +000011054 __ ret(0);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000011055
11056 // Slow-case: Handle non-smi or out-of-bounds access to arguments
11057 // by calling the runtime system.
ager@chromium.org7c537e22008-10-16 08:43:32 +000011058 __ bind(&slow);
ager@chromium.org41826e72009-03-30 13:30:57 +000011059 __ pop(ebx); // Return address.
11060 __ push(edx);
11061 __ push(ebx);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000011062 __ TailCallRuntime(Runtime::kGetArgumentsProperty, 1, 1);
ager@chromium.org7c537e22008-10-16 08:43:32 +000011063}
11064
11065
11066void ArgumentsAccessStub::GenerateNewObject(MacroAssembler* masm) {
ager@chromium.org5c838252010-02-19 08:53:10 +000011067 // esp[0] : return address
11068 // esp[4] : number of parameters
11069 // esp[8] : receiver displacement
11070 // esp[16] : function
11071
ager@chromium.org7c537e22008-10-16 08:43:32 +000011072 // The displacement is used for skipping the return address and the
11073 // frame pointer on the stack. It is the offset of the last
11074 // parameter (if any) relative to the frame pointer.
11075 static const int kDisplacement = 2 * kPointerSize;
11076
11077 // Check if the calling frame is an arguments adaptor frame.
sgjesse@chromium.org846fb742009-12-18 08:56:33 +000011078 Label adaptor_frame, try_allocate, runtime;
ager@chromium.org7c537e22008-10-16 08:43:32 +000011079 __ mov(edx, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
11080 __ mov(ecx, Operand(edx, StandardFrameConstants::kContextOffset));
ager@chromium.org18ad94b2009-09-02 08:22:29 +000011081 __ cmp(Operand(ecx), Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
sgjesse@chromium.org846fb742009-12-18 08:56:33 +000011082 __ j(equal, &adaptor_frame);
11083
11084 // Get the length from the frame.
11085 __ mov(ecx, Operand(esp, 1 * kPointerSize));
11086 __ jmp(&try_allocate);
ager@chromium.org7c537e22008-10-16 08:43:32 +000011087
11088 // Patch the arguments.length and the parameters pointer.
sgjesse@chromium.org846fb742009-12-18 08:56:33 +000011089 __ bind(&adaptor_frame);
ager@chromium.org7c537e22008-10-16 08:43:32 +000011090 __ mov(ecx, Operand(edx, ArgumentsAdaptorFrameConstants::kLengthOffset));
11091 __ mov(Operand(esp, 1 * kPointerSize), ecx);
11092 __ lea(edx, Operand(edx, ecx, times_2, kDisplacement));
11093 __ mov(Operand(esp, 2 * kPointerSize), edx);
11094
sgjesse@chromium.org846fb742009-12-18 08:56:33 +000011095 // Try the new space allocation. Start out with computing the size of
11096 // the arguments object and the elements array.
11097 Label add_arguments_object;
11098 __ bind(&try_allocate);
11099 __ test(ecx, Operand(ecx));
11100 __ j(zero, &add_arguments_object);
11101 __ lea(ecx, Operand(ecx, times_2, FixedArray::kHeaderSize));
11102 __ bind(&add_arguments_object);
11103 __ add(Operand(ecx), Immediate(Heap::kArgumentsObjectSize));
11104
11105 // Do the allocation of both objects in one go.
11106 __ AllocateInNewSpace(ecx, eax, edx, ebx, &runtime, TAG_OBJECT);
11107
11108 // Get the arguments boilerplate from the current (global) context.
11109 int offset = Context::SlotOffset(Context::ARGUMENTS_BOILERPLATE_INDEX);
11110 __ mov(edi, Operand(esi, Context::SlotOffset(Context::GLOBAL_INDEX)));
11111 __ mov(edi, FieldOperand(edi, GlobalObject::kGlobalContextOffset));
11112 __ mov(edi, Operand(edi, offset));
11113
11114 // Copy the JS object part.
11115 for (int i = 0; i < JSObject::kHeaderSize; i += kPointerSize) {
11116 __ mov(ebx, FieldOperand(edi, i));
11117 __ mov(FieldOperand(eax, i), ebx);
11118 }
11119
11120 // Setup the callee in-object property.
11121 ASSERT(Heap::arguments_callee_index == 0);
11122 __ mov(ebx, Operand(esp, 3 * kPointerSize));
11123 __ mov(FieldOperand(eax, JSObject::kHeaderSize), ebx);
11124
11125 // Get the length (smi tagged) and set that as an in-object property too.
11126 ASSERT(Heap::arguments_length_index == 1);
11127 __ mov(ecx, Operand(esp, 1 * kPointerSize));
11128 __ mov(FieldOperand(eax, JSObject::kHeaderSize + kPointerSize), ecx);
11129
11130 // If there are no actual arguments, we're done.
11131 Label done;
11132 __ test(ecx, Operand(ecx));
11133 __ j(zero, &done);
11134
ricow@chromium.org30ce4112010-05-31 10:38:25 +000011135 // Get the parameters pointer from the stack.
sgjesse@chromium.org846fb742009-12-18 08:56:33 +000011136 __ mov(edx, Operand(esp, 2 * kPointerSize));
sgjesse@chromium.org846fb742009-12-18 08:56:33 +000011137
11138 // Setup the elements pointer in the allocated arguments object and
11139 // initialize the header in the elements fixed array.
11140 __ lea(edi, Operand(eax, Heap::kArgumentsObjectSize));
11141 __ mov(FieldOperand(eax, JSObject::kElementsOffset), edi);
11142 __ mov(FieldOperand(edi, FixedArray::kMapOffset),
11143 Immediate(Factory::fixed_array_map()));
11144 __ mov(FieldOperand(edi, FixedArray::kLengthOffset), ecx);
ricow@chromium.org30ce4112010-05-31 10:38:25 +000011145 // Untag the length for the loop below.
11146 __ SmiUntag(ecx);
sgjesse@chromium.org846fb742009-12-18 08:56:33 +000011147
11148 // Copy the fixed array slots.
11149 Label loop;
11150 __ bind(&loop);
11151 __ mov(ebx, Operand(edx, -1 * kPointerSize)); // Skip receiver.
11152 __ mov(FieldOperand(edi, FixedArray::kHeaderSize), ebx);
11153 __ add(Operand(edi), Immediate(kPointerSize));
11154 __ sub(Operand(edx), Immediate(kPointerSize));
11155 __ dec(ecx);
sgjesse@chromium.org846fb742009-12-18 08:56:33 +000011156 __ j(not_zero, &loop);
11157
11158 // Return and remove the on-stack parameters.
11159 __ bind(&done);
11160 __ ret(3 * kPointerSize);
11161
ager@chromium.org7c537e22008-10-16 08:43:32 +000011162 // Do the runtime call to allocate the arguments object.
11163 __ bind(&runtime);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000011164 __ TailCallRuntime(Runtime::kNewArgumentsFast, 3, 1);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000011165}
11166
11167
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000011168void RegExpExecStub::Generate(MacroAssembler* masm) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000011169 // Just jump directly to runtime if native RegExp is not selected at compile
11170 // time or if regexp entry in generated code is turned off runtime switch or
11171 // at compilation.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000011172#ifdef V8_INTERPRETED_REGEXP
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000011173 __ TailCallRuntime(Runtime::kRegExpExec, 4, 1);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000011174#else // V8_INTERPRETED_REGEXP
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000011175 if (!FLAG_regexp_entry_native) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000011176 __ TailCallRuntime(Runtime::kRegExpExec, 4, 1);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000011177 return;
11178 }
11179
11180 // Stack frame on entry.
11181 // esp[0]: return address
11182 // esp[4]: last_match_info (expected JSArray)
11183 // esp[8]: previous index
11184 // esp[12]: subject string
11185 // esp[16]: JSRegExp object
11186
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000011187 static const int kLastMatchInfoOffset = 1 * kPointerSize;
11188 static const int kPreviousIndexOffset = 2 * kPointerSize;
11189 static const int kSubjectOffset = 3 * kPointerSize;
11190 static const int kJSRegExpOffset = 4 * kPointerSize;
11191
11192 Label runtime, invoke_regexp;
11193
11194 // Ensure that a RegExp stack is allocated.
11195 ExternalReference address_of_regexp_stack_memory_address =
11196 ExternalReference::address_of_regexp_stack_memory_address();
11197 ExternalReference address_of_regexp_stack_memory_size =
11198 ExternalReference::address_of_regexp_stack_memory_size();
11199 __ mov(ebx, Operand::StaticVariable(address_of_regexp_stack_memory_size));
11200 __ test(ebx, Operand(ebx));
11201 __ j(zero, &runtime, not_taken);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000011202
11203 // Check that the first argument is a JSRegExp object.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000011204 __ mov(eax, Operand(esp, kJSRegExpOffset));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000011205 ASSERT_EQ(0, kSmiTag);
11206 __ test(eax, Immediate(kSmiTagMask));
11207 __ j(zero, &runtime);
11208 __ CmpObjectType(eax, JS_REGEXP_TYPE, ecx);
11209 __ j(not_equal, &runtime);
11210 // Check that the RegExp has been compiled (data contains a fixed array).
11211 __ mov(ecx, FieldOperand(eax, JSRegExp::kDataOffset));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000011212 if (FLAG_debug_code) {
11213 __ test(ecx, Immediate(kSmiTagMask));
11214 __ Check(not_zero, "Unexpected type for RegExp data, FixedArray expected");
11215 __ CmpObjectType(ecx, FIXED_ARRAY_TYPE, ebx);
11216 __ Check(equal, "Unexpected type for RegExp data, FixedArray expected");
11217 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000011218
11219 // ecx: RegExp data (FixedArray)
11220 // Check the type of the RegExp. Only continue if type is JSRegExp::IRREGEXP.
11221 __ mov(ebx, FieldOperand(ecx, JSRegExp::kDataTagOffset));
11222 __ cmp(Operand(ebx), Immediate(Smi::FromInt(JSRegExp::IRREGEXP)));
11223 __ j(not_equal, &runtime);
11224
11225 // ecx: RegExp data (FixedArray)
11226 // Check that the number of captures fit in the static offsets vector buffer.
11227 __ mov(edx, FieldOperand(ecx, JSRegExp::kIrregexpCaptureCountOffset));
11228 // Calculate number of capture registers (number_of_captures + 1) * 2. This
11229 // uses the asumption that smis are 2 * their untagged value.
11230 ASSERT_EQ(0, kSmiTag);
11231 ASSERT_EQ(1, kSmiTagSize + kSmiShiftSize);
11232 __ add(Operand(edx), Immediate(2)); // edx was a smi.
11233 // Check that the static offsets vector buffer is large enough.
11234 __ cmp(edx, OffsetsVector::kStaticOffsetsVectorSize);
11235 __ j(above, &runtime);
11236
11237 // ecx: RegExp data (FixedArray)
11238 // edx: Number of capture registers
11239 // Check that the second argument is a string.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000011240 __ mov(eax, Operand(esp, kSubjectOffset));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000011241 __ test(eax, Immediate(kSmiTagMask));
11242 __ j(zero, &runtime);
11243 Condition is_string = masm->IsObjectStringType(eax, ebx, ebx);
11244 __ j(NegateCondition(is_string), &runtime);
11245 // Get the length of the string to ebx.
11246 __ mov(ebx, FieldOperand(eax, String::kLengthOffset));
11247
ager@chromium.orgac091b72010-05-05 07:34:42 +000011248 // ebx: Length of subject string as a smi
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000011249 // ecx: RegExp data (FixedArray)
11250 // edx: Number of capture registers
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000011251 // Check that the third argument is a positive smi less than the subject
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000011252 // string length. A negative value will be greater (unsigned comparison).
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000011253 __ mov(eax, Operand(esp, kPreviousIndexOffset));
ager@chromium.orgac091b72010-05-05 07:34:42 +000011254 __ test(eax, Immediate(kSmiTagMask));
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +000011255 __ j(not_zero, &runtime);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000011256 __ cmp(eax, Operand(ebx));
ager@chromium.orgac091b72010-05-05 07:34:42 +000011257 __ j(above_equal, &runtime);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000011258
11259 // ecx: RegExp data (FixedArray)
11260 // edx: Number of capture registers
11261 // Check that the fourth object is a JSArray object.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000011262 __ mov(eax, Operand(esp, kLastMatchInfoOffset));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000011263 __ test(eax, Immediate(kSmiTagMask));
11264 __ j(zero, &runtime);
11265 __ CmpObjectType(eax, JS_ARRAY_TYPE, ebx);
11266 __ j(not_equal, &runtime);
11267 // Check that the JSArray is in fast case.
11268 __ mov(ebx, FieldOperand(eax, JSArray::kElementsOffset));
11269 __ mov(eax, FieldOperand(ebx, HeapObject::kMapOffset));
11270 __ cmp(eax, Factory::fixed_array_map());
11271 __ j(not_equal, &runtime);
11272 // Check that the last match info has space for the capture registers and the
11273 // additional information.
11274 __ mov(eax, FieldOperand(ebx, FixedArray::kLengthOffset));
ricow@chromium.org30ce4112010-05-31 10:38:25 +000011275 __ SmiUntag(eax);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000011276 __ add(Operand(edx), Immediate(RegExpImpl::kLastMatchOverhead));
11277 __ cmp(edx, Operand(eax));
11278 __ j(greater, &runtime);
11279
11280 // ecx: RegExp data (FixedArray)
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000011281 // Check the representation and encoding of the subject string.
11282 Label seq_string, seq_two_byte_string, check_code;
11283 const int kStringRepresentationEncodingMask =
11284 kIsNotStringMask | kStringRepresentationMask | kStringEncodingMask;
11285 __ mov(eax, Operand(esp, kSubjectOffset));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000011286 __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset));
11287 __ movzx_b(ebx, FieldOperand(ebx, Map::kInstanceTypeOffset));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000011288 __ and_(ebx, kStringRepresentationEncodingMask);
11289 // First check for sequential string.
11290 ASSERT_EQ(0, kStringTag);
11291 ASSERT_EQ(0, kSeqStringTag);
11292 __ test(Operand(ebx),
11293 Immediate(kIsNotStringMask | kStringRepresentationMask));
11294 __ j(zero, &seq_string);
11295
11296 // Check for flat cons string.
11297 // A flat cons string is a cons string where the second part is the empty
11298 // string. In that case the subject string is just the first part of the cons
11299 // string. Also in this case the first part of the cons string is known to be
11300 // a sequential string or an external string.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000011301 __ and_(ebx, kStringRepresentationMask);
11302 __ cmp(ebx, kConsStringTag);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000011303 __ j(not_equal, &runtime);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000011304 __ mov(edx, FieldOperand(eax, ConsString::kSecondOffset));
11305 __ cmp(Operand(edx), Factory::empty_string());
11306 __ j(not_equal, &runtime);
11307 __ mov(eax, FieldOperand(eax, ConsString::kFirstOffset));
11308 __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset));
11309 __ movzx_b(ebx, FieldOperand(ebx, Map::kInstanceTypeOffset));
11310 ASSERT_EQ(0, kSeqStringTag);
11311 __ test(ebx, Immediate(kStringRepresentationMask));
11312 __ j(not_zero, &runtime);
11313 __ and_(ebx, kStringRepresentationEncodingMask);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000011314
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000011315 __ bind(&seq_string);
11316 // eax: subject string (sequential either ascii to two byte)
11317 // ebx: suject string type & kStringRepresentationEncodingMask
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000011318 // ecx: RegExp data (FixedArray)
11319 // Check that the irregexp code has been generated for an ascii string. If
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000011320 // it has, the field contains a code object otherwise it contains the hole.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000011321 const int kSeqTwoByteString = kStringTag | kSeqStringTag | kTwoByteStringTag;
11322 __ cmp(ebx, kSeqTwoByteString);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000011323 __ j(equal, &seq_two_byte_string);
11324 if (FLAG_debug_code) {
11325 __ cmp(ebx, kStringTag | kSeqStringTag | kAsciiStringTag);
11326 __ Check(equal, "Expected sequential ascii string");
11327 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000011328 __ mov(edx, FieldOperand(ecx, JSRegExp::kDataAsciiCodeOffset));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000011329 __ Set(edi, Immediate(1)); // Type is ascii.
11330 __ jmp(&check_code);
11331
11332 __ bind(&seq_two_byte_string);
11333 // eax: subject string
11334 // ecx: RegExp data (FixedArray)
11335 __ mov(edx, FieldOperand(ecx, JSRegExp::kDataUC16CodeOffset));
11336 __ Set(edi, Immediate(0)); // Type is two byte.
11337
11338 __ bind(&check_code);
11339 // Check that the irregexp code has been generated for the actual string
11340 // encoding. If it has, the field contains a code object otherwise it contains
11341 // the hole.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000011342 __ CmpObjectType(edx, CODE_TYPE, ebx);
11343 __ j(not_equal, &runtime);
11344
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000011345 // eax: subject string
11346 // edx: code
11347 // edi: encoding of subject string (1 if ascii, 0 if two_byte);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000011348 // Load used arguments before starting to push arguments for call to native
11349 // RegExp code to avoid handling changing stack height.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000011350 __ mov(ebx, Operand(esp, kPreviousIndexOffset));
11351 __ SmiUntag(ebx); // Previous index from smi.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000011352
11353 // eax: subject string
11354 // ebx: previous index
11355 // edx: code
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000011356 // edi: encoding of subject string (1 if ascii 0 if two_byte);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000011357 // All checks done. Now push arguments for native regexp code.
11358 __ IncrementCounter(&Counters::regexp_entry_native, 1);
11359
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000011360 static const int kRegExpExecuteArguments = 7;
11361 __ PrepareCallCFunction(kRegExpExecuteArguments, ecx);
11362
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000011363 // Argument 7: Indicate that this is a direct call from JavaScript.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000011364 __ mov(Operand(esp, 6 * kPointerSize), Immediate(1));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000011365
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000011366 // Argument 6: Start (high end) of backtracking stack memory area.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000011367 __ mov(ecx, Operand::StaticVariable(address_of_regexp_stack_memory_address));
11368 __ add(ecx, Operand::StaticVariable(address_of_regexp_stack_memory_size));
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000011369 __ mov(Operand(esp, 5 * kPointerSize), ecx);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000011370
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000011371 // Argument 5: static offsets vector buffer.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000011372 __ mov(Operand(esp, 4 * kPointerSize),
11373 Immediate(ExternalReference::address_of_static_offsets_vector()));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000011374
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000011375 // Argument 4: End of string data
11376 // Argument 3: Start of string data
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000011377 Label setup_two_byte, setup_rest;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000011378 __ test(edi, Operand(edi));
11379 __ mov(edi, FieldOperand(eax, String::kLengthOffset));
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000011380 __ j(zero, &setup_two_byte);
ager@chromium.orgac091b72010-05-05 07:34:42 +000011381 __ SmiUntag(edi);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000011382 __ lea(ecx, FieldOperand(eax, edi, times_1, SeqAsciiString::kHeaderSize));
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000011383 __ mov(Operand(esp, 3 * kPointerSize), ecx); // Argument 4.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000011384 __ lea(ecx, FieldOperand(eax, ebx, times_1, SeqAsciiString::kHeaderSize));
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000011385 __ mov(Operand(esp, 2 * kPointerSize), ecx); // Argument 3.
11386 __ jmp(&setup_rest);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000011387
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000011388 __ bind(&setup_two_byte);
ager@chromium.orgac091b72010-05-05 07:34:42 +000011389 ASSERT(kSmiTag == 0 && kSmiTagSize == 1); // edi is smi (powered by 2).
11390 __ lea(ecx, FieldOperand(eax, edi, times_1, SeqTwoByteString::kHeaderSize));
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000011391 __ mov(Operand(esp, 3 * kPointerSize), ecx); // Argument 4.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000011392 __ lea(ecx, FieldOperand(eax, ebx, times_2, SeqTwoByteString::kHeaderSize));
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000011393 __ mov(Operand(esp, 2 * kPointerSize), ecx); // Argument 3.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000011394
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000011395 __ bind(&setup_rest);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000011396
11397 // Argument 2: Previous index.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000011398 __ mov(Operand(esp, 1 * kPointerSize), ebx);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000011399
11400 // Argument 1: Subject string.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000011401 __ mov(Operand(esp, 0 * kPointerSize), eax);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000011402
11403 // Locate the code entry and call it.
11404 __ add(Operand(edx), Immediate(Code::kHeaderSize - kHeapObjectTag));
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000011405 __ CallCFunction(edx, kRegExpExecuteArguments);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000011406
11407 // Check the result.
11408 Label success;
11409 __ cmp(eax, NativeRegExpMacroAssembler::SUCCESS);
11410 __ j(equal, &success, taken);
11411 Label failure;
11412 __ cmp(eax, NativeRegExpMacroAssembler::FAILURE);
11413 __ j(equal, &failure, taken);
11414 __ cmp(eax, NativeRegExpMacroAssembler::EXCEPTION);
11415 // If not exception it can only be retry. Handle that in the runtime system.
11416 __ j(not_equal, &runtime);
11417 // Result must now be exception. If there is no pending exception already a
11418 // stack overflow (on the backtrack stack) was detected in RegExp code but
11419 // haven't created the exception yet. Handle that in the runtime system.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000011420 // TODO(592): Rerunning the RegExp to get the stack overflow exception.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000011421 ExternalReference pending_exception(Top::k_pending_exception_address);
11422 __ mov(eax,
11423 Operand::StaticVariable(ExternalReference::the_hole_value_location()));
11424 __ cmp(eax, Operand::StaticVariable(pending_exception));
11425 __ j(equal, &runtime);
11426 __ bind(&failure);
11427 // For failure and exception return null.
11428 __ mov(Operand(eax), Factory::null_value());
11429 __ ret(4 * kPointerSize);
11430
11431 // Load RegExp data.
11432 __ bind(&success);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000011433 __ mov(eax, Operand(esp, kJSRegExpOffset));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000011434 __ mov(ecx, FieldOperand(eax, JSRegExp::kDataOffset));
11435 __ mov(edx, FieldOperand(ecx, JSRegExp::kIrregexpCaptureCountOffset));
11436 // Calculate number of capture registers (number_of_captures + 1) * 2.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000011437 ASSERT_EQ(0, kSmiTag);
11438 ASSERT_EQ(1, kSmiTagSize + kSmiShiftSize);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000011439 __ add(Operand(edx), Immediate(2)); // edx was a smi.
11440
11441 // edx: Number of capture registers
11442 // Load last_match_info which is still known to be a fast case JSArray.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000011443 __ mov(eax, Operand(esp, kLastMatchInfoOffset));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000011444 __ mov(ebx, FieldOperand(eax, JSArray::kElementsOffset));
11445
11446 // ebx: last_match_info backing store (FixedArray)
11447 // edx: number of capture registers
11448 // Store the capture count.
11449 __ SmiTag(edx); // Number of capture registers to smi.
11450 __ mov(FieldOperand(ebx, RegExpImpl::kLastCaptureCountOffset), edx);
11451 __ SmiUntag(edx); // Number of capture registers back from smi.
11452 // Store last subject and last input.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000011453 __ mov(eax, Operand(esp, kSubjectOffset));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000011454 __ mov(FieldOperand(ebx, RegExpImpl::kLastSubjectOffset), eax);
11455 __ mov(ecx, ebx);
11456 __ RecordWrite(ecx, RegExpImpl::kLastSubjectOffset, eax, edi);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000011457 __ mov(eax, Operand(esp, kSubjectOffset));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000011458 __ mov(FieldOperand(ebx, RegExpImpl::kLastInputOffset), eax);
11459 __ mov(ecx, ebx);
11460 __ RecordWrite(ecx, RegExpImpl::kLastInputOffset, eax, edi);
11461
11462 // Get the static offsets vector filled by the native regexp code.
11463 ExternalReference address_of_static_offsets_vector =
11464 ExternalReference::address_of_static_offsets_vector();
11465 __ mov(ecx, Immediate(address_of_static_offsets_vector));
11466
11467 // ebx: last_match_info backing store (FixedArray)
11468 // ecx: offsets vector
11469 // edx: number of capture registers
11470 Label next_capture, done;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000011471 // Capture register counter starts from number of capture registers and
11472 // counts down until wraping after zero.
11473 __ bind(&next_capture);
11474 __ sub(Operand(edx), Immediate(1));
11475 __ j(negative, &done);
11476 // Read the value from the static offsets vector buffer.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000011477 __ mov(edi, Operand(ecx, edx, times_int_size, 0));
whesse@chromium.orgcec079d2010-03-22 14:44:04 +000011478 __ SmiTag(edi);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000011479 // Store the smi value in the last match info.
11480 __ mov(FieldOperand(ebx,
11481 edx,
11482 times_pointer_size,
11483 RegExpImpl::kFirstCaptureOffset),
11484 edi);
11485 __ jmp(&next_capture);
11486 __ bind(&done);
11487
11488 // Return last match info.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000011489 __ mov(eax, Operand(esp, kLastMatchInfoOffset));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000011490 __ ret(4 * kPointerSize);
11491
11492 // Do the runtime call to execute the regexp.
11493 __ bind(&runtime);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000011494 __ TailCallRuntime(Runtime::kRegExpExec, 4, 1);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000011495#endif // V8_INTERPRETED_REGEXP
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000011496}
11497
11498
ager@chromium.org5c838252010-02-19 08:53:10 +000011499void NumberToStringStub::GenerateLookupNumberStringCache(MacroAssembler* masm,
11500 Register object,
11501 Register result,
11502 Register scratch1,
11503 Register scratch2,
11504 bool object_is_smi,
11505 Label* not_found) {
ager@chromium.org5c838252010-02-19 08:53:10 +000011506 // Use of registers. Register result is used as a temporary.
11507 Register number_string_cache = result;
11508 Register mask = scratch1;
11509 Register scratch = scratch2;
11510
11511 // Load the number string cache.
11512 ExternalReference roots_address = ExternalReference::roots_address();
11513 __ mov(scratch, Immediate(Heap::kNumberStringCacheRootIndex));
11514 __ mov(number_string_cache,
11515 Operand::StaticArray(scratch, times_pointer_size, roots_address));
11516 // Make the hash mask from the length of the number string cache. It
11517 // contains two elements (number and string) for each cache entry.
11518 __ mov(mask, FieldOperand(number_string_cache, FixedArray::kLengthOffset));
ricow@chromium.org30ce4112010-05-31 10:38:25 +000011519 __ shr(mask, kSmiTagSize + 1); // Untag length and divide it by two.
ager@chromium.org5c838252010-02-19 08:53:10 +000011520 __ sub(Operand(mask), Immediate(1)); // Make mask.
ager@chromium.org357bf652010-04-12 11:30:10 +000011521
ager@chromium.org5c838252010-02-19 08:53:10 +000011522 // Calculate the entry in the number string cache. The hash value in the
ager@chromium.org357bf652010-04-12 11:30:10 +000011523 // number string cache for smis is just the smi value, and the hash for
11524 // doubles is the xor of the upper and lower words. See
11525 // Heap::GetNumberStringCache.
11526 Label smi_hash_calculated;
11527 Label load_result_from_cache;
11528 if (object_is_smi) {
11529 __ mov(scratch, object);
11530 __ SmiUntag(scratch);
11531 } else {
11532 Label not_smi, hash_calculated;
11533 ASSERT(kSmiTag == 0);
11534 __ test(object, Immediate(kSmiTagMask));
11535 __ j(not_zero, &not_smi);
11536 __ mov(scratch, object);
11537 __ SmiUntag(scratch);
11538 __ jmp(&smi_hash_calculated);
11539 __ bind(&not_smi);
11540 __ cmp(FieldOperand(object, HeapObject::kMapOffset),
11541 Factory::heap_number_map());
11542 __ j(not_equal, not_found);
11543 ASSERT_EQ(8, kDoubleSize);
11544 __ mov(scratch, FieldOperand(object, HeapNumber::kValueOffset));
11545 __ xor_(scratch, FieldOperand(object, HeapNumber::kValueOffset + 4));
11546 // Object is heap number and hash is now in scratch. Calculate cache index.
11547 __ and_(scratch, Operand(mask));
11548 Register index = scratch;
11549 Register probe = mask;
11550 __ mov(probe,
11551 FieldOperand(number_string_cache,
11552 index,
11553 times_twice_pointer_size,
11554 FixedArray::kHeaderSize));
11555 __ test(probe, Immediate(kSmiTagMask));
11556 __ j(zero, not_found);
11557 if (CpuFeatures::IsSupported(SSE2)) {
11558 CpuFeatures::Scope fscope(SSE2);
11559 __ movdbl(xmm0, FieldOperand(object, HeapNumber::kValueOffset));
11560 __ movdbl(xmm1, FieldOperand(probe, HeapNumber::kValueOffset));
11561 __ comisd(xmm0, xmm1);
11562 } else {
11563 __ fld_d(FieldOperand(object, HeapNumber::kValueOffset));
11564 __ fld_d(FieldOperand(probe, HeapNumber::kValueOffset));
11565 __ FCmp();
11566 }
11567 __ j(parity_even, not_found); // Bail out if NaN is involved.
11568 __ j(not_equal, not_found); // The cache did not contain this value.
11569 __ jmp(&load_result_from_cache);
11570 }
11571
11572 __ bind(&smi_hash_calculated);
11573 // Object is smi and hash is now in scratch. Calculate cache index.
ager@chromium.org5c838252010-02-19 08:53:10 +000011574 __ and_(scratch, Operand(mask));
ager@chromium.org357bf652010-04-12 11:30:10 +000011575 Register index = scratch;
ager@chromium.org5c838252010-02-19 08:53:10 +000011576 // Check if the entry is the smi we are looking for.
11577 __ cmp(object,
11578 FieldOperand(number_string_cache,
ager@chromium.org357bf652010-04-12 11:30:10 +000011579 index,
ager@chromium.org5c838252010-02-19 08:53:10 +000011580 times_twice_pointer_size,
11581 FixedArray::kHeaderSize));
11582 __ j(not_equal, not_found);
11583
11584 // Get the result from the cache.
ager@chromium.org357bf652010-04-12 11:30:10 +000011585 __ bind(&load_result_from_cache);
ager@chromium.org5c838252010-02-19 08:53:10 +000011586 __ mov(result,
11587 FieldOperand(number_string_cache,
ager@chromium.org357bf652010-04-12 11:30:10 +000011588 index,
ager@chromium.org5c838252010-02-19 08:53:10 +000011589 times_twice_pointer_size,
11590 FixedArray::kHeaderSize + kPointerSize));
11591 __ IncrementCounter(&Counters::number_to_string_native, 1);
11592}
11593
11594
11595void NumberToStringStub::Generate(MacroAssembler* masm) {
11596 Label runtime;
11597
11598 __ mov(ebx, Operand(esp, kPointerSize));
11599
11600 // Generate code to lookup number in the number string cache.
11601 GenerateLookupNumberStringCache(masm, ebx, eax, ecx, edx, false, &runtime);
11602 __ ret(1 * kPointerSize);
11603
11604 __ bind(&runtime);
11605 // Handle number to string in the runtime system if not found in the cache.
ager@chromium.org357bf652010-04-12 11:30:10 +000011606 __ TailCallRuntime(Runtime::kNumberToStringSkipCache, 1, 1);
ager@chromium.org5c838252010-02-19 08:53:10 +000011607}
11608
11609
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +000011610static int NegativeComparisonResult(Condition cc) {
11611 ASSERT(cc != equal);
11612 ASSERT((cc == less) || (cc == less_equal)
11613 || (cc == greater) || (cc == greater_equal));
11614 return (cc == greater || cc == greater_equal) ? LESS : GREATER;
11615}
11616
11617
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000011618void CompareStub::Generate(MacroAssembler* masm) {
11619 Label call_builtin, done;
ager@chromium.org7c537e22008-10-16 08:43:32 +000011620
kasperl@chromium.org8ccb0be2009-04-07 07:21:39 +000011621 // NOTICE! This code is only reached after a smi-fast-case check, so
11622 // it is certain that at least one operand isn't a smi.
ager@chromium.org7c537e22008-10-16 08:43:32 +000011623
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +000011624 // Identical objects can be compared fast, but there are some tricky cases
11625 // for NaN and undefined.
11626 {
11627 Label not_identical;
11628 __ cmp(eax, Operand(edx));
11629 __ j(not_equal, &not_identical);
ager@chromium.org7c537e22008-10-16 08:43:32 +000011630
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +000011631 if (cc_ != equal) {
11632 // Check for undefined. undefined OP undefined is false even though
11633 // undefined == undefined.
11634 Label check_for_nan;
11635 __ cmp(edx, Factory::undefined_value());
11636 __ j(not_equal, &check_for_nan);
11637 __ Set(eax, Immediate(Smi::FromInt(NegativeComparisonResult(cc_))));
11638 __ ret(0);
11639 __ bind(&check_for_nan);
11640 }
ager@chromium.org7c537e22008-10-16 08:43:32 +000011641
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +000011642 // Test for NaN. Sadly, we can't just compare to Factory::nan_value(),
11643 // so we do the second best thing - test it ourselves.
11644 // Note: if cc_ != equal, never_nan_nan_ is not used.
11645 if (never_nan_nan_ && (cc_ == equal)) {
11646 __ Set(eax, Immediate(Smi::FromInt(EQUAL)));
11647 __ ret(0);
11648 } else {
11649 Label return_equal;
11650 Label heap_number;
11651 // If it's not a heap number, then return equal.
11652 __ cmp(FieldOperand(edx, HeapObject::kMapOffset),
11653 Immediate(Factory::heap_number_map()));
11654 __ j(equal, &heap_number);
11655 __ bind(&return_equal);
11656 __ Set(eax, Immediate(Smi::FromInt(EQUAL)));
11657 __ ret(0);
ager@chromium.org7c537e22008-10-16 08:43:32 +000011658
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +000011659 __ bind(&heap_number);
11660 // It is a heap number, so return non-equal if it's NaN and equal if
11661 // it's not NaN.
11662 // The representation of NaN values has all exponent bits (52..62) set,
11663 // and not all mantissa bits (0..51) clear.
11664 // We only accept QNaNs, which have bit 51 set.
11665 // Read top bits of double representation (second word of value).
11666
11667 // Value is a QNaN if value & kQuietNaNMask == kQuietNaNMask, i.e.,
11668 // all bits in the mask are set. We only need to check the word
11669 // that contains the exponent and high bit of the mantissa.
11670 ASSERT_NE(0, (kQuietNaNHighBitsMask << 1) & 0x80000000u);
11671 __ mov(edx, FieldOperand(edx, HeapNumber::kExponentOffset));
11672 __ xor_(eax, Operand(eax));
11673 // Shift value and mask so kQuietNaNHighBitsMask applies to topmost
11674 // bits.
11675 __ add(edx, Operand(edx));
11676 __ cmp(edx, kQuietNaNHighBitsMask << 1);
11677 if (cc_ == equal) {
11678 ASSERT_NE(1, EQUAL);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000011679 __ setcc(above_equal, eax);
11680 __ ret(0);
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +000011681 } else {
11682 Label nan;
11683 __ j(above_equal, &nan);
11684 __ Set(eax, Immediate(Smi::FromInt(EQUAL)));
11685 __ ret(0);
11686 __ bind(&nan);
11687 __ Set(eax, Immediate(Smi::FromInt(NegativeComparisonResult(cc_))));
11688 __ ret(0);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000011689 }
kasperl@chromium.org8ccb0be2009-04-07 07:21:39 +000011690 }
ager@chromium.org7c537e22008-10-16 08:43:32 +000011691
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +000011692 __ bind(&not_identical);
11693 }
11694
11695 if (cc_ == equal) { // Both strict and non-strict.
11696 Label slow; // Fallthrough label.
11697
kasperl@chromium.org8ccb0be2009-04-07 07:21:39 +000011698 // If we're doing a strict equality comparison, we don't have to do
11699 // type conversion, so we generate code to do fast comparison for objects
11700 // and oddballs. Non-smi numbers and strings still go through the usual
11701 // slow-case code.
11702 if (strict_) {
11703 // If either is a Smi (we know that not both are), then they can only
11704 // be equal if the other is a HeapNumber. If so, use the slow case.
11705 {
11706 Label not_smis;
11707 ASSERT_EQ(0, kSmiTag);
11708 ASSERT_EQ(0, Smi::FromInt(0));
11709 __ mov(ecx, Immediate(kSmiTagMask));
11710 __ and_(ecx, Operand(eax));
11711 __ test(ecx, Operand(edx));
11712 __ j(not_zero, &not_smis);
11713 // One operand is a smi.
ager@chromium.org7c537e22008-10-16 08:43:32 +000011714
kasperl@chromium.org8ccb0be2009-04-07 07:21:39 +000011715 // Check whether the non-smi is a heap number.
11716 ASSERT_EQ(1, kSmiTagMask);
11717 // ecx still holds eax & kSmiTag, which is either zero or one.
11718 __ sub(Operand(ecx), Immediate(0x01));
11719 __ mov(ebx, edx);
11720 __ xor_(ebx, Operand(eax));
11721 __ and_(ebx, Operand(ecx)); // ebx holds either 0 or eax ^ edx.
11722 __ xor_(ebx, Operand(eax));
11723 // if eax was smi, ebx is now edx, else eax.
ager@chromium.org7c537e22008-10-16 08:43:32 +000011724
kasperl@chromium.org8ccb0be2009-04-07 07:21:39 +000011725 // Check if the non-smi operand is a heap number.
11726 __ cmp(FieldOperand(ebx, HeapObject::kMapOffset),
11727 Immediate(Factory::heap_number_map()));
11728 // If heap number, handle it in the slow case.
11729 __ j(equal, &slow);
11730 // Return non-equal (ebx is not zero)
11731 __ mov(eax, ebx);
11732 __ ret(0);
11733
11734 __ bind(&not_smis);
11735 }
11736
11737 // If either operand is a JSObject or an oddball value, then they are not
11738 // equal since their pointers are different
11739 // There is no test for undetectability in strict equality.
11740
11741 // Get the type of the first operand.
11742 __ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset));
11743 __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
11744
11745 // If the first object is a JS object, we have done pointer comparison.
11746 ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
11747 Label first_non_object;
11748 __ cmp(ecx, FIRST_JS_OBJECT_TYPE);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +000011749 __ j(below, &first_non_object);
kasperl@chromium.org8ccb0be2009-04-07 07:21:39 +000011750
11751 // Return non-zero (eax is not zero)
11752 Label return_not_equal;
11753 ASSERT(kHeapObjectTag != 0);
11754 __ bind(&return_not_equal);
11755 __ ret(0);
11756
11757 __ bind(&first_non_object);
11758 // Check for oddballs: true, false, null, undefined.
11759 __ cmp(ecx, ODDBALL_TYPE);
11760 __ j(equal, &return_not_equal);
11761
11762 __ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset));
11763 __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
11764
11765 __ cmp(ecx, FIRST_JS_OBJECT_TYPE);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +000011766 __ j(above_equal, &return_not_equal);
kasperl@chromium.org8ccb0be2009-04-07 07:21:39 +000011767
11768 // Check for oddballs: true, false, null, undefined.
11769 __ cmp(ecx, ODDBALL_TYPE);
11770 __ j(equal, &return_not_equal);
11771
11772 // Fall through to the general case.
11773 }
ager@chromium.org7c537e22008-10-16 08:43:32 +000011774 __ bind(&slow);
11775 }
11776
ager@chromium.org5aa501c2009-06-23 07:57:28 +000011777 // Push arguments below the return address.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000011778 __ pop(ecx);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000011779 __ push(eax);
11780 __ push(edx);
11781 __ push(ecx);
11782
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +000011783 // Generate the number comparison code.
11784 if (include_number_compare_) {
11785 Label non_number_comparison;
11786 Label unordered;
11787 if (CpuFeatures::IsSupported(SSE2)) {
11788 CpuFeatures::Scope use_sse2(SSE2);
11789 CpuFeatures::Scope use_cmov(CMOV);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000011790
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +000011791 FloatingPointHelper::LoadSSE2Operands(masm, &non_number_comparison);
11792 __ comisd(xmm0, xmm1);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000011793
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +000011794 // Don't base result on EFLAGS when a NaN is involved.
11795 __ j(parity_even, &unordered, not_taken);
11796 // Return a result of -1, 0, or 1, based on EFLAGS.
11797 __ mov(eax, 0); // equal
11798 __ mov(ecx, Immediate(Smi::FromInt(1)));
11799 __ cmov(above, eax, Operand(ecx));
11800 __ mov(ecx, Immediate(Smi::FromInt(-1)));
11801 __ cmov(below, eax, Operand(ecx));
11802 __ ret(2 * kPointerSize);
11803 } else {
11804 FloatingPointHelper::CheckFloatOperands(
11805 masm, &non_number_comparison, ebx);
11806 FloatingPointHelper::LoadFloatOperands(masm, ecx);
11807 __ FCmp();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000011808
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +000011809 // Don't base result on EFLAGS when a NaN is involved.
11810 __ j(parity_even, &unordered, not_taken);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000011811
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +000011812 Label below_label, above_label;
11813 // Return a result of -1, 0, or 1, based on EFLAGS. In all cases remove
11814 // two arguments from the stack as they have been pushed in preparation
11815 // of a possible runtime call.
11816 __ j(below, &below_label, not_taken);
11817 __ j(above, &above_label, not_taken);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000011818
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +000011819 __ xor_(eax, Operand(eax));
11820 __ ret(2 * kPointerSize);
ager@chromium.org4af710e2009-09-15 12:20:11 +000011821
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +000011822 __ bind(&below_label);
11823 __ mov(eax, Immediate(Smi::FromInt(-1)));
11824 __ ret(2 * kPointerSize);
ager@chromium.org4af710e2009-09-15 12:20:11 +000011825
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +000011826 __ bind(&above_label);
11827 __ mov(eax, Immediate(Smi::FromInt(1)));
11828 __ ret(2 * kPointerSize);
11829 }
11830
11831 // If one of the numbers was NaN, then the result is always false.
11832 // The cc is never not-equal.
11833 __ bind(&unordered);
11834 ASSERT(cc_ != not_equal);
11835 if (cc_ == less || cc_ == less_equal) {
11836 __ mov(eax, Immediate(Smi::FromInt(1)));
11837 } else {
11838 __ mov(eax, Immediate(Smi::FromInt(-1)));
11839 }
ager@chromium.org4af710e2009-09-15 12:20:11 +000011840 __ ret(2 * kPointerSize); // eax, edx were pushed
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +000011841
11842 // The number comparison code did not provide a valid result.
11843 __ bind(&non_number_comparison);
ager@chromium.org4af710e2009-09-15 12:20:11 +000011844 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000011845
ager@chromium.org5aa501c2009-06-23 07:57:28 +000011846 // Fast negative check for symbol-to-symbol equality.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000011847 Label check_for_strings;
ager@chromium.org5aa501c2009-06-23 07:57:28 +000011848 if (cc_ == equal) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000011849 BranchIfNonSymbol(masm, &check_for_strings, eax, ecx);
11850 BranchIfNonSymbol(masm, &check_for_strings, edx, ecx);
ager@chromium.org5aa501c2009-06-23 07:57:28 +000011851
11852 // We've already checked for object identity, so if both operands
11853 // are symbols they aren't equal. Register eax already holds a
11854 // non-zero value, which indicates not equal, so just return.
11855 __ ret(2 * kPointerSize);
11856 }
11857
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000011858 __ bind(&check_for_strings);
11859
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000011860 __ JumpIfNotBothSequentialAsciiStrings(edx, eax, ecx, ebx, &call_builtin);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000011861
11862 // Inline comparison of ascii strings.
11863 StringCompareStub::GenerateCompareFlatAsciiStrings(masm,
11864 edx,
11865 eax,
11866 ecx,
11867 ebx,
11868 edi);
11869#ifdef DEBUG
11870 __ Abort("Unexpected fall-through from string comparison");
11871#endif
11872
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000011873 __ bind(&call_builtin);
11874 // must swap argument order
11875 __ pop(ecx);
11876 __ pop(edx);
11877 __ pop(eax);
11878 __ push(edx);
11879 __ push(eax);
11880
11881 // Figure out which native to call and setup the arguments.
11882 Builtins::JavaScript builtin;
11883 if (cc_ == equal) {
11884 builtin = strict_ ? Builtins::STRICT_EQUALS : Builtins::EQUALS;
11885 } else {
11886 builtin = Builtins::COMPARE;
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +000011887 __ push(Immediate(Smi::FromInt(NegativeComparisonResult(cc_))));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000011888 }
11889
11890 // Restore return address on the stack.
11891 __ push(ecx);
11892
11893 // Call the native; it returns -1 (less), 0 (equal), or 1 (greater)
11894 // tagged as a small integer.
11895 __ InvokeBuiltin(builtin, JUMP_FUNCTION);
11896}
11897
11898
ager@chromium.org5aa501c2009-06-23 07:57:28 +000011899void CompareStub::BranchIfNonSymbol(MacroAssembler* masm,
11900 Label* label,
11901 Register object,
11902 Register scratch) {
11903 __ test(object, Immediate(kSmiTagMask));
11904 __ j(zero, label);
11905 __ mov(scratch, FieldOperand(object, HeapObject::kMapOffset));
11906 __ movzx_b(scratch, FieldOperand(scratch, Map::kInstanceTypeOffset));
11907 __ and_(scratch, kIsSymbolMask | kIsNotStringMask);
11908 __ cmp(scratch, kSymbolTag | kStringTag);
11909 __ j(not_equal, label);
11910}
11911
11912
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000011913void StackCheckStub::Generate(MacroAssembler* masm) {
11914 // Because builtins always remove the receiver from the stack, we
11915 // have to fake one to avoid underflowing the stack. The receiver
11916 // must be inserted below the return address on the stack so we
11917 // temporarily store that in a register.
11918 __ pop(eax);
11919 __ push(Immediate(Smi::FromInt(0)));
11920 __ push(eax);
11921
11922 // Do tail-call to runtime routine.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000011923 __ TailCallRuntime(Runtime::kStackGuard, 1, 1);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000011924}
11925
11926
11927void CallFunctionStub::Generate(MacroAssembler* masm) {
11928 Label slow;
11929
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000011930 // If the receiver might be a value (string, number or boolean) check for this
11931 // and box it if it is.
11932 if (ReceiverMightBeValue()) {
11933 // Get the receiver from the stack.
11934 // +1 ~ return address
11935 Label receiver_is_value, receiver_is_js_object;
11936 __ mov(eax, Operand(esp, (argc_ + 1) * kPointerSize));
11937
11938 // Check if receiver is a smi (which is a number value).
11939 __ test(eax, Immediate(kSmiTagMask));
11940 __ j(zero, &receiver_is_value, not_taken);
11941
11942 // Check if the receiver is a valid JS object.
11943 __ CmpObjectType(eax, FIRST_JS_OBJECT_TYPE, edi);
11944 __ j(above_equal, &receiver_is_js_object);
11945
11946 // Call the runtime to box the value.
11947 __ bind(&receiver_is_value);
11948 __ EnterInternalFrame();
11949 __ push(eax);
11950 __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION);
11951 __ LeaveInternalFrame();
11952 __ mov(Operand(esp, (argc_ + 1) * kPointerSize), eax);
11953
11954 __ bind(&receiver_is_js_object);
11955 }
11956
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000011957 // Get the function to call from the stack.
11958 // +2 ~ receiver, return address
11959 __ mov(edi, Operand(esp, (argc_ + 2) * kPointerSize));
11960
11961 // Check that the function really is a JavaScript function.
11962 __ test(edi, Immediate(kSmiTagMask));
11963 __ j(zero, &slow, not_taken);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +000011964 // Goto slow case if we do not have a function.
11965 __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000011966 __ j(not_equal, &slow, not_taken);
11967
11968 // Fast-case: Just invoke the function.
11969 ParameterCount actual(argc_);
11970 __ InvokeFunction(edi, actual, JUMP_FUNCTION);
11971
11972 // Slow-case: Non-function called.
11973 __ bind(&slow);
ager@chromium.org5c838252010-02-19 08:53:10 +000011974 // CALL_NON_FUNCTION expects the non-function callee as receiver (instead
11975 // of the original receiver from the call site).
11976 __ mov(Operand(esp, (argc_ + 1) * kPointerSize), edi);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000011977 __ Set(eax, Immediate(argc_));
11978 __ Set(ebx, Immediate(0));
11979 __ GetBuiltinEntry(edx, Builtins::CALL_NON_FUNCTION);
11980 Handle<Code> adaptor(Builtins::builtin(Builtins::ArgumentsAdaptorTrampoline));
11981 __ jmp(adaptor, RelocInfo::CODE_TARGET);
11982}
11983
11984
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011985void CEntryStub::GenerateThrowTOS(MacroAssembler* masm) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +000011986 // eax holds the exception.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011987
ager@chromium.orgeadaf222009-06-16 09:43:10 +000011988 // Adjust this code if not the case.
11989 ASSERT(StackHandlerConstants::kSize == 4 * kPointerSize);
11990
11991 // Drop the sp to the top of the handler.
11992 ExternalReference handler_address(Top::k_handler_address);
11993 __ mov(esp, Operand::StaticVariable(handler_address));
11994
11995 // Restore next handler and frame pointer, discard handler state.
11996 ASSERT(StackHandlerConstants::kNextOffset == 0);
11997 __ pop(Operand::StaticVariable(handler_address));
11998 ASSERT(StackHandlerConstants::kFPOffset == 1 * kPointerSize);
11999 __ pop(ebp);
12000 __ pop(edx); // Remove state.
12001
12002 // Before returning we restore the context from the frame pointer if
12003 // not NULL. The frame pointer is NULL in the exception handler of
12004 // a JS entry frame.
12005 __ xor_(esi, Operand(esi)); // Tentatively set context pointer to NULL.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000012006 Label skip;
12007 __ cmp(ebp, 0);
12008 __ j(equal, &skip, not_taken);
12009 __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
12010 __ bind(&skip);
12011
ager@chromium.orgeadaf222009-06-16 09:43:10 +000012012 ASSERT(StackHandlerConstants::kPCOffset == 3 * kPointerSize);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000012013 __ ret(0);
12014}
12015
12016
ager@chromium.orgc4c92722009-11-18 14:12:51 +000012017// If true, a Handle<T> passed by value is passed and returned by
12018// using the location_ field directly. If false, it is passed and
12019// returned as a pointer to a handle.
lrn@chromium.org25156de2010-04-06 13:10:27 +000012020#ifdef USING_BSD_ABI
ager@chromium.orgc4c92722009-11-18 14:12:51 +000012021static const bool kPassHandlesDirectly = true;
12022#else
12023static const bool kPassHandlesDirectly = false;
12024#endif
12025
12026
12027void ApiGetterEntryStub::Generate(MacroAssembler* masm) {
12028 Label get_result;
12029 Label prologue;
12030 Label promote_scheduled_exception;
12031 __ EnterApiExitFrame(ExitFrame::MODE_NORMAL, kStackSpace, kArgc);
12032 ASSERT_EQ(kArgc, 4);
12033 if (kPassHandlesDirectly) {
12034 // When handles as passed directly we don't have to allocate extra
12035 // space for and pass an out parameter.
12036 __ mov(Operand(esp, 0 * kPointerSize), ebx); // name.
12037 __ mov(Operand(esp, 1 * kPointerSize), eax); // arguments pointer.
12038 } else {
12039 // The function expects three arguments to be passed but we allocate
12040 // four to get space for the output cell. The argument slots are filled
12041 // as follows:
12042 //
12043 // 3: output cell
12044 // 2: arguments pointer
12045 // 1: name
12046 // 0: pointer to the output cell
12047 //
12048 // Note that this is one more "argument" than the function expects
12049 // so the out cell will have to be popped explicitly after returning
12050 // from the function.
12051 __ mov(Operand(esp, 1 * kPointerSize), ebx); // name.
12052 __ mov(Operand(esp, 2 * kPointerSize), eax); // arguments pointer.
12053 __ mov(ebx, esp);
12054 __ add(Operand(ebx), Immediate(3 * kPointerSize));
12055 __ mov(Operand(esp, 0 * kPointerSize), ebx); // output
12056 __ mov(Operand(esp, 3 * kPointerSize), Immediate(0)); // out cell.
12057 }
12058 // Call the api function!
12059 __ call(fun()->address(), RelocInfo::RUNTIME_ENTRY);
12060 // Check if the function scheduled an exception.
12061 ExternalReference scheduled_exception_address =
12062 ExternalReference::scheduled_exception_address();
12063 __ cmp(Operand::StaticVariable(scheduled_exception_address),
12064 Immediate(Factory::the_hole_value()));
12065 __ j(not_equal, &promote_scheduled_exception, not_taken);
12066 if (!kPassHandlesDirectly) {
12067 // The returned value is a pointer to the handle holding the result.
12068 // Dereference this to get to the location.
12069 __ mov(eax, Operand(eax, 0));
12070 }
12071 // Check if the result handle holds 0
12072 __ test(eax, Operand(eax));
12073 __ j(not_zero, &get_result, taken);
12074 // It was zero; the result is undefined.
12075 __ mov(eax, Factory::undefined_value());
12076 __ jmp(&prologue);
12077 // It was non-zero. Dereference to get the result value.
12078 __ bind(&get_result);
12079 __ mov(eax, Operand(eax, 0));
12080 __ bind(&prologue);
12081 __ LeaveExitFrame(ExitFrame::MODE_NORMAL);
12082 __ ret(0);
12083 __ bind(&promote_scheduled_exception);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000012084 __ TailCallRuntime(Runtime::kPromoteScheduledException, 0, 1);
ager@chromium.orgc4c92722009-11-18 14:12:51 +000012085}
12086
12087
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000012088void CEntryStub::GenerateCore(MacroAssembler* masm,
12089 Label* throw_normal_exception,
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +000012090 Label* throw_termination_exception,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000012091 Label* throw_out_of_memory_exception,
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +000012092 bool do_gc,
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000012093 bool always_allocate_scope,
12094 int /* alignment_skew */) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000012095 // eax: result parameter for PerformGC, if any
12096 // ebx: pointer to C function (C callee-saved)
12097 // ebp: frame pointer (restored after C call)
12098 // esp: stack pointer (restored after C call)
mads.s.ager31e71382008-08-13 09:32:07 +000012099 // edi: number of arguments including receiver (C callee-saved)
ager@chromium.org236ad962008-09-25 09:45:57 +000012100 // esi: pointer to the first argument (C callee-saved)
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000012101
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000012102 // Result returned in eax, or eax+edx if result_size_ is 2.
12103
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000012104 // Check stack alignment.
12105 if (FLAG_debug_code) {
12106 __ CheckStackAlignment();
12107 }
12108
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000012109 if (do_gc) {
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000012110 // Pass failure code returned from last attempt as first argument to
12111 // PerformGC. No need to use PrepareCallCFunction/CallCFunction here as the
12112 // stack alignment is known to be correct. This function takes one argument
12113 // which is passed on the stack, and we know that the stack has been
12114 // prepared to pass at least one argument.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000012115 __ mov(Operand(esp, 0 * kPointerSize), eax); // Result.
ager@chromium.org236ad962008-09-25 09:45:57 +000012116 __ call(FUNCTION_ADDR(Runtime::PerformGC), RelocInfo::RUNTIME_ENTRY);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000012117 }
12118
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +000012119 ExternalReference scope_depth =
12120 ExternalReference::heap_always_allocate_scope_depth();
12121 if (always_allocate_scope) {
12122 __ inc(Operand::StaticVariable(scope_depth));
12123 }
12124
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000012125 // Call C function.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000012126 __ mov(Operand(esp, 0 * kPointerSize), edi); // argc.
ager@chromium.org236ad962008-09-25 09:45:57 +000012127 __ mov(Operand(esp, 1 * kPointerSize), esi); // argv.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000012128 __ call(Operand(ebx));
12129 // Result is in eax or edx:eax - do not destroy these registers!
12130
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +000012131 if (always_allocate_scope) {
12132 __ dec(Operand::StaticVariable(scope_depth));
12133 }
12134
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +000012135 // Make sure we're not trying to return 'the hole' from the runtime
12136 // call as this may lead to crashes in the IC code later.
12137 if (FLAG_debug_code) {
12138 Label okay;
12139 __ cmp(eax, Factory::the_hole_value());
12140 __ j(not_equal, &okay);
12141 __ int3();
12142 __ bind(&okay);
12143 }
12144
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000012145 // Check for failure result.
12146 Label failure_returned;
12147 ASSERT(((kFailureTag + 1) & kFailureTagMask) == 0);
12148 __ lea(ecx, Operand(eax, 1));
12149 // Lower 2 bits of ecx are 0 iff eax has failure tag.
12150 __ test(ecx, Immediate(kFailureTagMask));
12151 __ j(zero, &failure_returned, not_taken);
12152
ager@chromium.org236ad962008-09-25 09:45:57 +000012153 // Exit the JavaScript to C++ exit frame.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000012154 __ LeaveExitFrame(mode_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000012155 __ ret(0);
12156
ager@chromium.org236ad962008-09-25 09:45:57 +000012157 // Handling of failure.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000012158 __ bind(&failure_returned);
12159
12160 Label retry;
12161 // If the returned exception is RETRY_AFTER_GC continue at retry label
12162 ASSERT(Failure::RETRY_AFTER_GC == 0);
12163 __ test(eax, Immediate(((1 << kFailureTypeTagSize) - 1) << kFailureTagSize));
12164 __ j(zero, &retry, taken);
12165
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +000012166 // Special handling of out of memory exceptions.
12167 __ cmp(eax, reinterpret_cast<int32_t>(Failure::OutOfMemoryException()));
12168 __ j(equal, throw_out_of_memory_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000012169
12170 // Retrieve the pending exception and clear the variable.
12171 ExternalReference pending_exception_address(Top::k_pending_exception_address);
12172 __ mov(eax, Operand::StaticVariable(pending_exception_address));
12173 __ mov(edx,
12174 Operand::StaticVariable(ExternalReference::the_hole_value_location()));
12175 __ mov(Operand::StaticVariable(pending_exception_address), edx);
12176
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +000012177 // Special handling of termination exceptions which are uncatchable
12178 // by javascript code.
12179 __ cmp(eax, Factory::termination_exception());
12180 __ j(equal, throw_termination_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000012181
12182 // Handle normal exception.
12183 __ jmp(throw_normal_exception);
12184
12185 // Retry.
12186 __ bind(&retry);
12187}
12188
12189
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +000012190void CEntryStub::GenerateThrowUncatchable(MacroAssembler* masm,
12191 UncatchableExceptionType type) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +000012192 // Adjust this code if not the case.
12193 ASSERT(StackHandlerConstants::kSize == 4 * kPointerSize);
12194
12195 // Drop sp to the top stack handler.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000012196 ExternalReference handler_address(Top::k_handler_address);
ager@chromium.orgeadaf222009-06-16 09:43:10 +000012197 __ mov(esp, Operand::StaticVariable(handler_address));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000012198
12199 // Unwind the handlers until the ENTRY handler is found.
12200 Label loop, done;
12201 __ bind(&loop);
12202 // Load the type of the current stack handler.
ager@chromium.orgeadaf222009-06-16 09:43:10 +000012203 const int kStateOffset = StackHandlerConstants::kStateOffset;
12204 __ cmp(Operand(esp, kStateOffset), Immediate(StackHandler::ENTRY));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000012205 __ j(equal, &done);
12206 // Fetch the next handler in the list.
ager@chromium.orgeadaf222009-06-16 09:43:10 +000012207 const int kNextOffset = StackHandlerConstants::kNextOffset;
12208 __ mov(esp, Operand(esp, kNextOffset));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000012209 __ jmp(&loop);
12210 __ bind(&done);
12211
12212 // Set the top handler address to next handler past the current ENTRY handler.
ager@chromium.orgeadaf222009-06-16 09:43:10 +000012213 ASSERT(StackHandlerConstants::kNextOffset == 0);
12214 __ pop(Operand::StaticVariable(handler_address));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000012215
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +000012216 if (type == OUT_OF_MEMORY) {
12217 // Set external caught exception to false.
12218 ExternalReference external_caught(Top::k_external_caught_exception_address);
12219 __ mov(eax, false);
12220 __ mov(Operand::StaticVariable(external_caught), eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000012221
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +000012222 // Set pending exception and eax to out of memory exception.
12223 ExternalReference pending_exception(Top::k_pending_exception_address);
12224 __ mov(eax, reinterpret_cast<int32_t>(Failure::OutOfMemoryException()));
12225 __ mov(Operand::StaticVariable(pending_exception), eax);
12226 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000012227
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +000012228 // Clear the context pointer.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000012229 __ xor_(esi, Operand(esi));
12230
ager@chromium.orgeadaf222009-06-16 09:43:10 +000012231 // Restore fp from handler and discard handler state.
12232 ASSERT(StackHandlerConstants::kFPOffset == 1 * kPointerSize);
12233 __ pop(ebp);
12234 __ pop(edx); // State.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000012235
ager@chromium.orgeadaf222009-06-16 09:43:10 +000012236 ASSERT(StackHandlerConstants::kPCOffset == 3 * kPointerSize);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000012237 __ ret(0);
12238}
12239
12240
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000012241void CEntryStub::Generate(MacroAssembler* masm) {
mads.s.ager31e71382008-08-13 09:32:07 +000012242 // eax: number of arguments including receiver
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000012243 // ebx: pointer to C function (C callee-saved)
12244 // ebp: frame pointer (restored after C call)
12245 // esp: stack pointer (restored after C call)
12246 // esi: current context (C callee-saved)
ager@chromium.orgeadaf222009-06-16 09:43:10 +000012247 // edi: JS function of the caller (C callee-saved)
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000012248
ager@chromium.orgeadaf222009-06-16 09:43:10 +000012249 // NOTE: Invocations of builtins may return failure objects instead
12250 // of a proper result. The builtin entry handles this by performing
12251 // a garbage collection and retrying the builtin (twice).
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000012252
ager@chromium.org236ad962008-09-25 09:45:57 +000012253 // Enter the exit frame that transitions from JavaScript to C++.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000012254 __ EnterExitFrame(mode_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000012255
12256 // eax: result parameter for PerformGC, if any (setup below)
12257 // ebx: pointer to builtin function (C callee-saved)
12258 // ebp: frame pointer (restored after C call)
12259 // esp: stack pointer (restored after C call)
mads.s.ager31e71382008-08-13 09:32:07 +000012260 // edi: number of arguments including receiver (C callee-saved)
ager@chromium.org236ad962008-09-25 09:45:57 +000012261 // esi: argv pointer (C callee-saved)
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000012262
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000012263 Label throw_normal_exception;
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +000012264 Label throw_termination_exception;
12265 Label throw_out_of_memory_exception;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000012266
sgjesse@chromium.org911335c2009-08-19 12:59:44 +000012267 // Call into the runtime system.
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +000012268 GenerateCore(masm,
12269 &throw_normal_exception,
12270 &throw_termination_exception,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000012271 &throw_out_of_memory_exception,
sgjesse@chromium.org911335c2009-08-19 12:59:44 +000012272 false,
ager@chromium.org236ad962008-09-25 09:45:57 +000012273 false);
ager@chromium.org236ad962008-09-25 09:45:57 +000012274
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +000012275 // Do space-specific GC and retry runtime call.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000012276 GenerateCore(masm,
12277 &throw_normal_exception,
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +000012278 &throw_termination_exception,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000012279 &throw_out_of_memory_exception,
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +000012280 true,
12281 false);
12282
12283 // Do full GC and retry runtime call one final time.
12284 Failure* failure = Failure::InternalError();
ager@chromium.org3bf7b912008-11-17 09:09:45 +000012285 __ mov(eax, Immediate(reinterpret_cast<int32_t>(failure)));
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +000012286 GenerateCore(masm,
12287 &throw_normal_exception,
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +000012288 &throw_termination_exception,
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +000012289 &throw_out_of_memory_exception,
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +000012290 true,
ager@chromium.org236ad962008-09-25 09:45:57 +000012291 true);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000012292
12293 __ bind(&throw_out_of_memory_exception);
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +000012294 GenerateThrowUncatchable(masm, OUT_OF_MEMORY);
12295
12296 __ bind(&throw_termination_exception);
12297 GenerateThrowUncatchable(masm, TERMINATION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000012298
12299 __ bind(&throw_normal_exception);
12300 GenerateThrowTOS(masm);
12301}
12302
12303
12304void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) {
12305 Label invoke, exit;
ager@chromium.orge2902be2009-06-08 12:21:35 +000012306#ifdef ENABLE_LOGGING_AND_PROFILING
12307 Label not_outermost_js, not_outermost_js_2;
12308#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000012309
12310 // Setup frame.
12311 __ push(ebp);
12312 __ mov(ebp, Operand(esp));
12313
ager@chromium.org18ad94b2009-09-02 08:22:29 +000012314 // Push marker in two places.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000012315 int marker = is_construct ? StackFrame::ENTRY_CONSTRUCT : StackFrame::ENTRY;
ager@chromium.org18ad94b2009-09-02 08:22:29 +000012316 __ push(Immediate(Smi::FromInt(marker))); // context slot
12317 __ push(Immediate(Smi::FromInt(marker))); // function slot
12318 // Save callee-saved registers (C calling conventions).
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000012319 __ push(edi);
12320 __ push(esi);
12321 __ push(ebx);
12322
12323 // Save copies of the top frame descriptor on the stack.
12324 ExternalReference c_entry_fp(Top::k_c_entry_fp_address);
12325 __ push(Operand::StaticVariable(c_entry_fp));
12326
ager@chromium.orge2902be2009-06-08 12:21:35 +000012327#ifdef ENABLE_LOGGING_AND_PROFILING
12328 // If this is the outermost JS call, set js_entry_sp value.
12329 ExternalReference js_entry_sp(Top::k_js_entry_sp_address);
12330 __ cmp(Operand::StaticVariable(js_entry_sp), Immediate(0));
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +000012331 __ j(not_equal, &not_outermost_js);
ager@chromium.orge2902be2009-06-08 12:21:35 +000012332 __ mov(Operand::StaticVariable(js_entry_sp), ebp);
12333 __ bind(&not_outermost_js);
12334#endif
12335
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000012336 // Call a faked try-block that does the invoke.
12337 __ call(&invoke);
12338
12339 // Caught exception: Store result (exception) in the pending
12340 // exception field in the JSEnv and return a failure sentinel.
12341 ExternalReference pending_exception(Top::k_pending_exception_address);
12342 __ mov(Operand::StaticVariable(pending_exception), eax);
ager@chromium.org3bf7b912008-11-17 09:09:45 +000012343 __ mov(eax, reinterpret_cast<int32_t>(Failure::Exception()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000012344 __ jmp(&exit);
12345
12346 // Invoke: Link this frame into the handler chain.
12347 __ bind(&invoke);
12348 __ PushTryHandler(IN_JS_ENTRY, JS_ENTRY_HANDLER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000012349
12350 // Clear any pending exceptions.
12351 __ mov(edx,
12352 Operand::StaticVariable(ExternalReference::the_hole_value_location()));
12353 __ mov(Operand::StaticVariable(pending_exception), edx);
12354
12355 // Fake a receiver (NULL).
12356 __ push(Immediate(0)); // receiver
12357
12358 // Invoke the function by calling through JS entry trampoline
12359 // builtin and pop the faked function when we return. Notice that we
12360 // cannot store a reference to the trampoline code directly in this
12361 // stub, because the builtin stubs may not have been generated yet.
12362 if (is_construct) {
12363 ExternalReference construct_entry(Builtins::JSConstructEntryTrampoline);
ager@chromium.org3bf7b912008-11-17 09:09:45 +000012364 __ mov(edx, Immediate(construct_entry));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000012365 } else {
12366 ExternalReference entry(Builtins::JSEntryTrampoline);
ager@chromium.org3bf7b912008-11-17 09:09:45 +000012367 __ mov(edx, Immediate(entry));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000012368 }
12369 __ mov(edx, Operand(edx, 0)); // deref address
12370 __ lea(edx, FieldOperand(edx, Code::kHeaderSize));
12371 __ call(Operand(edx));
12372
12373 // Unlink this frame from the handler chain.
12374 __ pop(Operand::StaticVariable(ExternalReference(Top::k_handler_address)));
12375 // Pop next_sp.
12376 __ add(Operand(esp), Immediate(StackHandlerConstants::kSize - kPointerSize));
12377
ager@chromium.orge2902be2009-06-08 12:21:35 +000012378#ifdef ENABLE_LOGGING_AND_PROFILING
12379 // If current EBP value is the same as js_entry_sp value, it means that
12380 // the current function is the outermost.
12381 __ cmp(ebp, Operand::StaticVariable(js_entry_sp));
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +000012382 __ j(not_equal, &not_outermost_js_2);
ager@chromium.orge2902be2009-06-08 12:21:35 +000012383 __ mov(Operand::StaticVariable(js_entry_sp), Immediate(0));
12384 __ bind(&not_outermost_js_2);
12385#endif
12386
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000012387 // Restore the top frame descriptor from the stack.
12388 __ bind(&exit);
12389 __ pop(Operand::StaticVariable(ExternalReference(Top::k_c_entry_fp_address)));
12390
12391 // Restore callee-saved registers (C calling conventions).
12392 __ pop(ebx);
12393 __ pop(esi);
12394 __ pop(edi);
12395 __ add(Operand(esp), Immediate(2 * kPointerSize)); // remove markers
12396
12397 // Restore frame pointer and return.
12398 __ pop(ebp);
12399 __ ret(0);
12400}
12401
12402
ager@chromium.org7c537e22008-10-16 08:43:32 +000012403void InstanceofStub::Generate(MacroAssembler* masm) {
12404 // Get the object - go slow case if it's a smi.
12405 Label slow;
12406 __ mov(eax, Operand(esp, 2 * kPointerSize)); // 2 ~ return address, function
12407 __ test(eax, Immediate(kSmiTagMask));
12408 __ j(zero, &slow, not_taken);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000012409
ager@chromium.org7c537e22008-10-16 08:43:32 +000012410 // Check that the left hand is a JS object.
ager@chromium.org13441bf2009-05-12 13:10:27 +000012411 __ mov(eax, FieldOperand(eax, HeapObject::kMapOffset)); // eax - object map
ager@chromium.org7c537e22008-10-16 08:43:32 +000012412 __ movzx_b(ecx, FieldOperand(eax, Map::kInstanceTypeOffset)); // ecx - type
12413 __ cmp(ecx, FIRST_JS_OBJECT_TYPE);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +000012414 __ j(below, &slow, not_taken);
ager@chromium.org7c537e22008-10-16 08:43:32 +000012415 __ cmp(ecx, LAST_JS_OBJECT_TYPE);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +000012416 __ j(above, &slow, not_taken);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000012417
ager@chromium.org7c537e22008-10-16 08:43:32 +000012418 // Get the prototype of the function.
12419 __ mov(edx, Operand(esp, 1 * kPointerSize)); // 1 ~ return address
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +000012420 // edx is function, eax is map.
12421
12422 // Look up the function and the map in the instanceof cache.
12423 Label miss;
12424 ExternalReference roots_address = ExternalReference::roots_address();
12425 __ mov(ecx, Immediate(Heap::kInstanceofCacheFunctionRootIndex));
12426 __ cmp(edx, Operand::StaticArray(ecx, times_pointer_size, roots_address));
12427 __ j(not_equal, &miss);
12428 __ mov(ecx, Immediate(Heap::kInstanceofCacheMapRootIndex));
12429 __ cmp(eax, Operand::StaticArray(ecx, times_pointer_size, roots_address));
12430 __ j(not_equal, &miss);
12431 __ mov(ecx, Immediate(Heap::kInstanceofCacheAnswerRootIndex));
12432 __ mov(eax, Operand::StaticArray(ecx, times_pointer_size, roots_address));
12433 __ ret(2 * kPointerSize);
12434
12435 __ bind(&miss);
ager@chromium.org7c537e22008-10-16 08:43:32 +000012436 __ TryGetFunctionPrototype(edx, ebx, ecx, &slow);
12437
12438 // Check that the function prototype is a JS object.
ager@chromium.org13441bf2009-05-12 13:10:27 +000012439 __ test(ebx, Immediate(kSmiTagMask));
12440 __ j(zero, &slow, not_taken);
ager@chromium.org7c537e22008-10-16 08:43:32 +000012441 __ mov(ecx, FieldOperand(ebx, HeapObject::kMapOffset));
12442 __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
12443 __ cmp(ecx, FIRST_JS_OBJECT_TYPE);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +000012444 __ j(below, &slow, not_taken);
ager@chromium.org7c537e22008-10-16 08:43:32 +000012445 __ cmp(ecx, LAST_JS_OBJECT_TYPE);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +000012446 __ j(above, &slow, not_taken);
ager@chromium.org7c537e22008-10-16 08:43:32 +000012447
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +000012448 // Register mapping:
12449 // eax is object map.
12450 // edx is function.
12451 // ebx is function prototype.
12452 __ mov(ecx, Immediate(Heap::kInstanceofCacheMapRootIndex));
12453 __ mov(Operand::StaticArray(ecx, times_pointer_size, roots_address), eax);
12454 __ mov(ecx, Immediate(Heap::kInstanceofCacheFunctionRootIndex));
12455 __ mov(Operand::StaticArray(ecx, times_pointer_size, roots_address), edx);
12456
ager@chromium.org7c537e22008-10-16 08:43:32 +000012457 __ mov(ecx, FieldOperand(eax, Map::kPrototypeOffset));
12458
12459 // Loop through the prototype chain looking for the function prototype.
12460 Label loop, is_instance, is_not_instance;
12461 __ bind(&loop);
12462 __ cmp(ecx, Operand(ebx));
12463 __ j(equal, &is_instance);
12464 __ cmp(Operand(ecx), Immediate(Factory::null_value()));
12465 __ j(equal, &is_not_instance);
12466 __ mov(ecx, FieldOperand(ecx, HeapObject::kMapOffset));
12467 __ mov(ecx, FieldOperand(ecx, Map::kPrototypeOffset));
12468 __ jmp(&loop);
12469
12470 __ bind(&is_instance);
12471 __ Set(eax, Immediate(0));
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +000012472 __ mov(ecx, Immediate(Heap::kInstanceofCacheAnswerRootIndex));
12473 __ mov(Operand::StaticArray(ecx, times_pointer_size, roots_address), eax);
ager@chromium.org7c537e22008-10-16 08:43:32 +000012474 __ ret(2 * kPointerSize);
12475
12476 __ bind(&is_not_instance);
12477 __ Set(eax, Immediate(Smi::FromInt(1)));
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +000012478 __ mov(ecx, Immediate(Heap::kInstanceofCacheAnswerRootIndex));
12479 __ mov(Operand::StaticArray(ecx, times_pointer_size, roots_address), eax);
ager@chromium.org7c537e22008-10-16 08:43:32 +000012480 __ ret(2 * kPointerSize);
12481
12482 // Slow-case: Go through the JavaScript implementation.
12483 __ bind(&slow);
12484 __ InvokeBuiltin(Builtins::INSTANCE_OF, JUMP_FUNCTION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000012485}
12486
12487
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +000012488int CompareStub::MinorKey() {
12489 // Encode the three parameters in a unique 16 bit value. To avoid duplicate
12490 // stubs the never NaN NaN condition is only taken into account if the
12491 // condition is equals.
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +000012492 ASSERT(static_cast<unsigned>(cc_) < (1 << 13));
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +000012493 return ConditionField::encode(static_cast<unsigned>(cc_))
12494 | StrictField::encode(strict_)
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +000012495 | NeverNanNanField::encode(cc_ == equal ? never_nan_nan_ : false)
12496 | IncludeNumberCompareField::encode(include_number_compare_);
12497}
12498
12499
12500// Unfortunately you have to run without snapshots to see most of these
12501// names in the profile since most compare stubs end up in the snapshot.
12502const char* CompareStub::GetName() {
12503 if (name_ != NULL) return name_;
12504 const int kMaxNameLength = 100;
12505 name_ = Bootstrapper::AllocateAutoDeletedArray(kMaxNameLength);
12506 if (name_ == NULL) return "OOM";
12507
12508 const char* cc_name;
12509 switch (cc_) {
12510 case less: cc_name = "LT"; break;
12511 case greater: cc_name = "GT"; break;
12512 case less_equal: cc_name = "LE"; break;
12513 case greater_equal: cc_name = "GE"; break;
12514 case equal: cc_name = "EQ"; break;
12515 case not_equal: cc_name = "NE"; break;
12516 default: cc_name = "UnknownCondition"; break;
12517 }
12518
12519 const char* strict_name = "";
12520 if (strict_ && (cc_ == equal || cc_ == not_equal)) {
12521 strict_name = "_STRICT";
12522 }
12523
12524 const char* never_nan_nan_name = "";
12525 if (never_nan_nan_ && (cc_ == equal || cc_ == not_equal)) {
12526 never_nan_nan_name = "_NO_NAN";
12527 }
12528
12529 const char* include_number_compare_name = "";
12530 if (!include_number_compare_) {
12531 include_number_compare_name = "_NO_NUMBER";
12532 }
12533
12534 OS::SNPrintF(Vector<char>(name_, kMaxNameLength),
12535 "CompareStub_%s%s%s%s",
12536 cc_name,
12537 strict_name,
12538 never_nan_nan_name,
12539 include_number_compare_name);
12540 return name_;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000012541}
12542
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000012543
ricow@chromium.org30ce4112010-05-31 10:38:25 +000012544// -------------------------------------------------------------------------
12545// StringCharCodeAtGenerator
12546
12547void StringCharCodeAtGenerator::GenerateFast(MacroAssembler* masm) {
12548 Label flat_string;
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000012549 Label ascii_string;
12550 Label got_char_code;
12551
12552 // If the receiver is a smi trigger the non-string case.
12553 ASSERT(kSmiTag == 0);
ricow@chromium.org30ce4112010-05-31 10:38:25 +000012554 __ test(object_, Immediate(kSmiTagMask));
12555 __ j(zero, receiver_not_string_);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000012556
12557 // Fetch the instance type of the receiver into result register.
ricow@chromium.org30ce4112010-05-31 10:38:25 +000012558 __ mov(result_, FieldOperand(object_, HeapObject::kMapOffset));
12559 __ movzx_b(result_, FieldOperand(result_, Map::kInstanceTypeOffset));
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000012560 // If the receiver is not a string trigger the non-string case.
ricow@chromium.org30ce4112010-05-31 10:38:25 +000012561 __ test(result_, Immediate(kIsNotStringMask));
12562 __ j(not_zero, receiver_not_string_);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000012563
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000012564 // If the index is non-smi trigger the non-smi case.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000012565 ASSERT(kSmiTag == 0);
ricow@chromium.org30ce4112010-05-31 10:38:25 +000012566 __ test(index_, Immediate(kSmiTagMask));
12567 __ j(not_zero, &index_not_smi_);
12568
12569 // Put smi-tagged index into scratch register.
12570 __ mov(scratch_, index_);
12571 __ bind(&got_smi_index_);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000012572
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000012573 // Check for index out of range.
ricow@chromium.org30ce4112010-05-31 10:38:25 +000012574 __ cmp(scratch_, FieldOperand(object_, String::kLengthOffset));
12575 __ j(above_equal, index_out_of_range_);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000012576
12577 // We need special handling for non-flat strings.
12578 ASSERT(kSeqStringTag == 0);
ricow@chromium.org30ce4112010-05-31 10:38:25 +000012579 __ test(result_, Immediate(kStringRepresentationMask));
12580 __ j(zero, &flat_string);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000012581
12582 // Handle non-flat strings.
ricow@chromium.org30ce4112010-05-31 10:38:25 +000012583 __ test(result_, Immediate(kIsConsStringMask));
12584 __ j(zero, &call_runtime_);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000012585
12586 // ConsString.
12587 // Check whether the right hand side is the empty string (i.e. if
12588 // this is really a flat string in a cons string). If that is not
12589 // the case we would rather go to the runtime system now to flatten
12590 // the string.
ricow@chromium.org30ce4112010-05-31 10:38:25 +000012591 __ cmp(FieldOperand(object_, ConsString::kSecondOffset),
12592 Immediate(Factory::empty_string()));
12593 __ j(not_equal, &call_runtime_);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000012594 // Get the first of the two strings and load its instance type.
ricow@chromium.org30ce4112010-05-31 10:38:25 +000012595 __ mov(object_, FieldOperand(object_, ConsString::kFirstOffset));
12596 __ mov(result_, FieldOperand(object_, HeapObject::kMapOffset));
12597 __ movzx_b(result_, FieldOperand(result_, Map::kInstanceTypeOffset));
12598 // If the first cons component is also non-flat, then go to runtime.
12599 ASSERT(kSeqStringTag == 0);
12600 __ test(result_, Immediate(kStringRepresentationMask));
12601 __ j(not_zero, &call_runtime_);
12602
12603 // Check for 1-byte or 2-byte string.
12604 __ bind(&flat_string);
12605 ASSERT(kAsciiStringTag != 0);
12606 __ test(result_, Immediate(kStringEncodingMask));
12607 __ j(not_zero, &ascii_string);
12608
12609 // 2-byte string.
12610 // Load the 2-byte character code into the result register.
12611 ASSERT(kSmiTag == 0 && kSmiTagSize == 1);
12612 __ movzx_w(result_, FieldOperand(object_,
12613 scratch_, times_1, // Scratch is smi-tagged.
12614 SeqTwoByteString::kHeaderSize));
12615 __ jmp(&got_char_code);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000012616
12617 // ASCII string.
kmillikin@chromium.org4111b802010-05-03 10:34:42 +000012618 // Load the byte into the result register.
ricow@chromium.org30ce4112010-05-31 10:38:25 +000012619 __ bind(&ascii_string);
12620 __ SmiUntag(scratch_);
12621 __ movzx_b(result_, FieldOperand(object_,
12622 scratch_, times_1,
12623 SeqAsciiString::kHeaderSize));
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000012624 __ bind(&got_char_code);
ricow@chromium.org30ce4112010-05-31 10:38:25 +000012625 __ SmiTag(result_);
12626 __ bind(&exit_);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000012627}
12628
12629
ricow@chromium.org30ce4112010-05-31 10:38:25 +000012630void StringCharCodeAtGenerator::GenerateSlow(
12631 MacroAssembler* masm, const RuntimeCallHelper& call_helper) {
12632 __ Abort("Unexpected fallthrough to CharCodeAt slow case");
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000012633
ricow@chromium.org30ce4112010-05-31 10:38:25 +000012634 // Index is not a smi.
12635 __ bind(&index_not_smi_);
12636 // If index is a heap number, try converting it to an integer.
12637 __ CheckMap(index_, Factory::heap_number_map(), index_not_number_, true);
12638 call_helper.BeforeCall(masm);
12639 __ push(object_);
12640 __ push(index_);
ricow@chromium.org30ce4112010-05-31 10:38:25 +000012641 __ push(index_); // Consumed by runtime conversion function.
12642 if (index_flags_ == STRING_INDEX_IS_NUMBER) {
12643 __ CallRuntime(Runtime::kNumberToIntegerMapMinusZero, 1);
12644 } else {
12645 ASSERT(index_flags_ == STRING_INDEX_IS_ARRAY_INDEX);
12646 // NumberToSmi discards numbers that are not exact integers.
12647 __ CallRuntime(Runtime::kNumberToSmi, 1);
12648 }
12649 if (!scratch_.is(eax)) {
12650 // Save the conversion result before the pop instructions below
12651 // have a chance to overwrite it.
12652 __ mov(scratch_, eax);
12653 }
ricow@chromium.org30ce4112010-05-31 10:38:25 +000012654 __ pop(index_);
12655 __ pop(object_);
sgjesse@chromium.org82dbbab2010-06-02 08:57:44 +000012656 // Reload the instance type.
12657 __ mov(result_, FieldOperand(object_, HeapObject::kMapOffset));
12658 __ movzx_b(result_, FieldOperand(result_, Map::kInstanceTypeOffset));
ricow@chromium.org30ce4112010-05-31 10:38:25 +000012659 call_helper.AfterCall(masm);
12660 // If index is still not a smi, it must be out of range.
12661 ASSERT(kSmiTag == 0);
12662 __ test(scratch_, Immediate(kSmiTagMask));
12663 __ j(not_zero, index_out_of_range_);
12664 // Otherwise, return to the fast path.
12665 __ jmp(&got_smi_index_);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000012666
ricow@chromium.org30ce4112010-05-31 10:38:25 +000012667 // Call runtime. We get here when the receiver is a string and the
12668 // index is a number, but the code of getting the actual character
12669 // is too complex (e.g., when the string needs to be flattened).
12670 __ bind(&call_runtime_);
12671 call_helper.BeforeCall(masm);
12672 __ push(object_);
12673 __ push(index_);
12674 __ CallRuntime(Runtime::kStringCharCodeAt, 2);
12675 if (!result_.is(eax)) {
12676 __ mov(result_, eax);
12677 }
12678 call_helper.AfterCall(masm);
12679 __ jmp(&exit_);
12680
12681 __ Abort("Unexpected fallthrough from CharCodeAt slow case");
12682}
12683
12684
12685// -------------------------------------------------------------------------
12686// StringCharFromCodeGenerator
12687
12688void StringCharFromCodeGenerator::GenerateFast(MacroAssembler* masm) {
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000012689 // Fast case of Heap::LookupSingleCharacterStringFromCode.
12690 ASSERT(kSmiTag == 0);
12691 ASSERT(kSmiShiftSize == 0);
12692 ASSERT(IsPowerOf2(String::kMaxAsciiCharCode + 1));
ricow@chromium.org30ce4112010-05-31 10:38:25 +000012693 __ test(code_,
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000012694 Immediate(kSmiTagMask |
12695 ((~String::kMaxAsciiCharCode) << kSmiTagSize)));
ricow@chromium.org30ce4112010-05-31 10:38:25 +000012696 __ j(not_zero, &slow_case_, not_taken);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000012697
ricow@chromium.org30ce4112010-05-31 10:38:25 +000012698 __ Set(result_, Immediate(Factory::single_character_string_cache()));
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000012699 ASSERT(kSmiTag == 0);
12700 ASSERT(kSmiTagSize == 1);
12701 ASSERT(kSmiShiftSize == 0);
12702 // At this point code register contains smi tagged ascii char code.
ricow@chromium.org30ce4112010-05-31 10:38:25 +000012703 __ mov(result_, FieldOperand(result_,
12704 code_, times_half_pointer_size,
12705 FixedArray::kHeaderSize));
12706 __ cmp(result_, Factory::undefined_value());
12707 __ j(equal, &slow_case_, not_taken);
12708 __ bind(&exit_);
12709}
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000012710
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000012711
ricow@chromium.org30ce4112010-05-31 10:38:25 +000012712void StringCharFromCodeGenerator::GenerateSlow(
12713 MacroAssembler* masm, const RuntimeCallHelper& call_helper) {
12714 __ Abort("Unexpected fallthrough to CharFromCode slow case");
12715
12716 __ bind(&slow_case_);
12717 call_helper.BeforeCall(masm);
12718 __ push(code_);
12719 __ CallRuntime(Runtime::kCharFromCode, 1);
12720 if (!result_.is(eax)) {
12721 __ mov(result_, eax);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000012722 }
ricow@chromium.org30ce4112010-05-31 10:38:25 +000012723 call_helper.AfterCall(masm);
12724 __ jmp(&exit_);
12725
12726 __ Abort("Unexpected fallthrough from CharFromCode slow case");
12727}
12728
12729
12730// -------------------------------------------------------------------------
12731// StringCharAtGenerator
12732
12733void StringCharAtGenerator::GenerateFast(MacroAssembler* masm) {
12734 char_code_at_generator_.GenerateFast(masm);
12735 char_from_code_generator_.GenerateFast(masm);
12736}
12737
12738
12739void StringCharAtGenerator::GenerateSlow(
12740 MacroAssembler* masm, const RuntimeCallHelper& call_helper) {
12741 char_code_at_generator_.GenerateSlow(masm, call_helper);
12742 char_from_code_generator_.GenerateSlow(masm, call_helper);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000012743}
12744
12745
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000012746void StringAddStub::Generate(MacroAssembler* masm) {
12747 Label string_add_runtime;
12748
12749 // Load the two arguments.
12750 __ mov(eax, Operand(esp, 2 * kPointerSize)); // First argument.
12751 __ mov(edx, Operand(esp, 1 * kPointerSize)); // Second argument.
12752
12753 // Make sure that both arguments are strings if not known in advance.
12754 if (string_check_) {
12755 __ test(eax, Immediate(kSmiTagMask));
12756 __ j(zero, &string_add_runtime);
12757 __ CmpObjectType(eax, FIRST_NONSTRING_TYPE, ebx);
12758 __ j(above_equal, &string_add_runtime);
12759
12760 // First argument is a a string, test second.
12761 __ test(edx, Immediate(kSmiTagMask));
12762 __ j(zero, &string_add_runtime);
12763 __ CmpObjectType(edx, FIRST_NONSTRING_TYPE, ebx);
12764 __ j(above_equal, &string_add_runtime);
12765 }
12766
12767 // Both arguments are strings.
12768 // eax: first string
12769 // edx: second string
12770 // Check if either of the strings are empty. In that case return the other.
12771 Label second_not_zero_length, both_not_zero_length;
12772 __ mov(ecx, FieldOperand(edx, String::kLengthOffset));
ager@chromium.orgac091b72010-05-05 07:34:42 +000012773 ASSERT(kSmiTag == 0);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000012774 __ test(ecx, Operand(ecx));
12775 __ j(not_zero, &second_not_zero_length);
12776 // Second string is empty, result is first string which is already in eax.
12777 __ IncrementCounter(&Counters::string_add_native, 1);
12778 __ ret(2 * kPointerSize);
12779 __ bind(&second_not_zero_length);
12780 __ mov(ebx, FieldOperand(eax, String::kLengthOffset));
ager@chromium.orgac091b72010-05-05 07:34:42 +000012781 ASSERT(kSmiTag == 0);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000012782 __ test(ebx, Operand(ebx));
12783 __ j(not_zero, &both_not_zero_length);
12784 // First string is empty, result is second string which is in edx.
12785 __ mov(eax, edx);
12786 __ IncrementCounter(&Counters::string_add_native, 1);
12787 __ ret(2 * kPointerSize);
12788
12789 // Both strings are non-empty.
12790 // eax: first string
ager@chromium.orgac091b72010-05-05 07:34:42 +000012791 // ebx: length of first string as a smi
12792 // ecx: length of second string as a smi
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000012793 // edx: second string
12794 // Look at the length of the result of adding the two strings.
ager@chromium.org5c838252010-02-19 08:53:10 +000012795 Label string_add_flat_result, longer_than_two;
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000012796 __ bind(&both_not_zero_length);
12797 __ add(ebx, Operand(ecx));
ager@chromium.orgac091b72010-05-05 07:34:42 +000012798 ASSERT(Smi::kMaxValue == String::kMaxLength);
12799 // Handle exceptionally long strings in the runtime system.
12800 __ j(overflow, &string_add_runtime);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000012801 // Use the runtime system when adding two one character strings, as it
12802 // contains optimizations for this specific case using the symbol table.
ager@chromium.orgac091b72010-05-05 07:34:42 +000012803 __ cmp(Operand(ebx), Immediate(Smi::FromInt(2)));
ager@chromium.org5c838252010-02-19 08:53:10 +000012804 __ j(not_equal, &longer_than_two);
12805
12806 // Check that both strings are non-external ascii strings.
12807 __ JumpIfNotBothSequentialAsciiStrings(eax, edx, ebx, ecx,
12808 &string_add_runtime);
12809
12810 // Get the two characters forming the sub string.
12811 __ movzx_b(ebx, FieldOperand(eax, SeqAsciiString::kHeaderSize));
12812 __ movzx_b(ecx, FieldOperand(edx, SeqAsciiString::kHeaderSize));
12813
12814 // Try to lookup two character string in symbol table. If it is not found
12815 // just allocate a new one.
12816 Label make_two_character_string, make_flat_ascii_string;
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000012817 StringHelper::GenerateTwoCharacterSymbolTableProbe(
12818 masm, ebx, ecx, eax, edx, edi, &make_two_character_string);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000012819 __ IncrementCounter(&Counters::string_add_native, 1);
ager@chromium.org5c838252010-02-19 08:53:10 +000012820 __ ret(2 * kPointerSize);
12821
12822 __ bind(&make_two_character_string);
ager@chromium.orgac091b72010-05-05 07:34:42 +000012823 __ Set(ebx, Immediate(Smi::FromInt(2)));
ager@chromium.org5c838252010-02-19 08:53:10 +000012824 __ jmp(&make_flat_ascii_string);
12825
12826 __ bind(&longer_than_two);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000012827 // Check if resulting string will be flat.
ager@chromium.orgac091b72010-05-05 07:34:42 +000012828 __ cmp(Operand(ebx), Immediate(Smi::FromInt(String::kMinNonFlatLength)));
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000012829 __ j(below, &string_add_flat_result);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000012830
12831 // If result is not supposed to be flat allocate a cons string object. If both
12832 // strings are ascii the result is an ascii cons string.
12833 Label non_ascii, allocated;
12834 __ mov(edi, FieldOperand(eax, HeapObject::kMapOffset));
12835 __ movzx_b(ecx, FieldOperand(edi, Map::kInstanceTypeOffset));
12836 __ mov(edi, FieldOperand(edx, HeapObject::kMapOffset));
12837 __ movzx_b(edi, FieldOperand(edi, Map::kInstanceTypeOffset));
12838 __ and_(ecx, Operand(edi));
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +000012839 ASSERT(kStringEncodingMask == kAsciiStringTag);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000012840 __ test(ecx, Immediate(kAsciiStringTag));
12841 __ j(zero, &non_ascii);
12842 // Allocate an acsii cons string.
12843 __ AllocateAsciiConsString(ecx, edi, no_reg, &string_add_runtime);
12844 __ bind(&allocated);
12845 // Fill the fields of the cons string.
ager@chromium.orgac091b72010-05-05 07:34:42 +000012846 if (FLAG_debug_code) __ AbortIfNotSmi(ebx);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000012847 __ mov(FieldOperand(ecx, ConsString::kLengthOffset), ebx);
12848 __ mov(FieldOperand(ecx, ConsString::kHashFieldOffset),
12849 Immediate(String::kEmptyHashField));
12850 __ mov(FieldOperand(ecx, ConsString::kFirstOffset), eax);
12851 __ mov(FieldOperand(ecx, ConsString::kSecondOffset), edx);
12852 __ mov(eax, ecx);
12853 __ IncrementCounter(&Counters::string_add_native, 1);
12854 __ ret(2 * kPointerSize);
12855 __ bind(&non_ascii);
12856 // Allocate a two byte cons string.
12857 __ AllocateConsString(ecx, edi, no_reg, &string_add_runtime);
12858 __ jmp(&allocated);
12859
12860 // Handle creating a flat result. First check that both strings are not
12861 // external strings.
12862 // eax: first string
ager@chromium.orgac091b72010-05-05 07:34:42 +000012863 // ebx: length of resulting flat string as a smi
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000012864 // edx: second string
12865 __ bind(&string_add_flat_result);
12866 __ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset));
12867 __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
12868 __ and_(ecx, kStringRepresentationMask);
12869 __ cmp(ecx, kExternalStringTag);
12870 __ j(equal, &string_add_runtime);
12871 __ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset));
12872 __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
12873 __ and_(ecx, kStringRepresentationMask);
12874 __ cmp(ecx, kExternalStringTag);
12875 __ j(equal, &string_add_runtime);
12876 // Now check if both strings are ascii strings.
12877 // eax: first string
ager@chromium.orgac091b72010-05-05 07:34:42 +000012878 // ebx: length of resulting flat string as a smi
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000012879 // edx: second string
12880 Label non_ascii_string_add_flat_result;
12881 __ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset));
12882 __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +000012883 ASSERT(kStringEncodingMask == kAsciiStringTag);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000012884 __ test(ecx, Immediate(kAsciiStringTag));
12885 __ j(zero, &non_ascii_string_add_flat_result);
12886 __ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset));
12887 __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
12888 __ test(ecx, Immediate(kAsciiStringTag));
12889 __ j(zero, &string_add_runtime);
ager@chromium.org5c838252010-02-19 08:53:10 +000012890
12891 __ bind(&make_flat_ascii_string);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000012892 // Both strings are ascii strings. As they are short they are both flat.
ager@chromium.orgac091b72010-05-05 07:34:42 +000012893 // ebx: length of resulting flat string as a smi
12894 __ SmiUntag(ebx);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000012895 __ AllocateAsciiString(eax, ebx, ecx, edx, edi, &string_add_runtime);
12896 // eax: result string
12897 __ mov(ecx, eax);
12898 // Locate first character of result.
12899 __ add(Operand(ecx), Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag));
12900 // Load first argument and locate first character.
12901 __ mov(edx, Operand(esp, 2 * kPointerSize));
12902 __ mov(edi, FieldOperand(edx, String::kLengthOffset));
ager@chromium.orgac091b72010-05-05 07:34:42 +000012903 __ SmiUntag(edi);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000012904 __ add(Operand(edx), Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag));
12905 // eax: result string
12906 // ecx: first character of result
12907 // edx: first char of first argument
12908 // edi: length of first argument
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000012909 StringHelper::GenerateCopyCharacters(masm, ecx, edx, edi, ebx, true);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000012910 // Load second argument and locate first character.
12911 __ mov(edx, Operand(esp, 1 * kPointerSize));
12912 __ mov(edi, FieldOperand(edx, String::kLengthOffset));
ager@chromium.orgac091b72010-05-05 07:34:42 +000012913 __ SmiUntag(edi);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000012914 __ add(Operand(edx), Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag));
12915 // eax: result string
12916 // ecx: next character of result
12917 // edx: first char of second argument
12918 // edi: length of second argument
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000012919 StringHelper::GenerateCopyCharacters(masm, ecx, edx, edi, ebx, true);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000012920 __ IncrementCounter(&Counters::string_add_native, 1);
12921 __ ret(2 * kPointerSize);
12922
12923 // Handle creating a flat two byte result.
12924 // eax: first string - known to be two byte
ager@chromium.orgac091b72010-05-05 07:34:42 +000012925 // ebx: length of resulting flat string as a smi
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000012926 // edx: second string
12927 __ bind(&non_ascii_string_add_flat_result);
12928 __ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset));
12929 __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
12930 __ and_(ecx, kAsciiStringTag);
12931 __ j(not_zero, &string_add_runtime);
12932 // Both strings are two byte strings. As they are short they are both
12933 // flat.
ager@chromium.orgac091b72010-05-05 07:34:42 +000012934 __ SmiUntag(ebx);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000012935 __ AllocateTwoByteString(eax, ebx, ecx, edx, edi, &string_add_runtime);
12936 // eax: result string
12937 __ mov(ecx, eax);
12938 // Locate first character of result.
12939 __ add(Operand(ecx),
12940 Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
12941 // Load first argument and locate first character.
12942 __ mov(edx, Operand(esp, 2 * kPointerSize));
12943 __ mov(edi, FieldOperand(edx, String::kLengthOffset));
ager@chromium.orgac091b72010-05-05 07:34:42 +000012944 __ SmiUntag(edi);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000012945 __ add(Operand(edx),
12946 Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
12947 // eax: result string
12948 // ecx: first character of result
12949 // edx: first char of first argument
12950 // edi: length of first argument
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000012951 StringHelper::GenerateCopyCharacters(masm, ecx, edx, edi, ebx, false);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000012952 // Load second argument and locate first character.
12953 __ mov(edx, Operand(esp, 1 * kPointerSize));
12954 __ mov(edi, FieldOperand(edx, String::kLengthOffset));
ager@chromium.orgac091b72010-05-05 07:34:42 +000012955 __ SmiUntag(edi);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000012956 __ add(Operand(edx), Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag));
12957 // eax: result string
12958 // ecx: next character of result
12959 // edx: first char of second argument
12960 // edi: length of second argument
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000012961 StringHelper::GenerateCopyCharacters(masm, ecx, edx, edi, ebx, false);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000012962 __ IncrementCounter(&Counters::string_add_native, 1);
12963 __ ret(2 * kPointerSize);
12964
12965 // Just jump to runtime to add the two strings.
12966 __ bind(&string_add_runtime);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000012967 __ TailCallRuntime(Runtime::kStringAdd, 2, 1);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000012968}
12969
12970
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000012971void StringHelper::GenerateCopyCharacters(MacroAssembler* masm,
12972 Register dest,
12973 Register src,
12974 Register count,
12975 Register scratch,
12976 bool ascii) {
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000012977 Label loop;
12978 __ bind(&loop);
12979 // This loop just copies one character at a time, as it is only used for very
12980 // short strings.
12981 if (ascii) {
12982 __ mov_b(scratch, Operand(src, 0));
12983 __ mov_b(Operand(dest, 0), scratch);
12984 __ add(Operand(src), Immediate(1));
12985 __ add(Operand(dest), Immediate(1));
12986 } else {
12987 __ mov_w(scratch, Operand(src, 0));
12988 __ mov_w(Operand(dest, 0), scratch);
12989 __ add(Operand(src), Immediate(2));
12990 __ add(Operand(dest), Immediate(2));
12991 }
12992 __ sub(Operand(count), Immediate(1));
12993 __ j(not_zero, &loop);
12994}
12995
12996
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000012997void StringHelper::GenerateCopyCharactersREP(MacroAssembler* masm,
12998 Register dest,
12999 Register src,
13000 Register count,
13001 Register scratch,
13002 bool ascii) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000013003 // Copy characters using rep movs of doublewords. Align destination on 4 byte
13004 // boundary before starting rep movs. Copy remaining characters after running
13005 // rep movs.
13006 ASSERT(dest.is(edi)); // rep movs destination
13007 ASSERT(src.is(esi)); // rep movs source
13008 ASSERT(count.is(ecx)); // rep movs count
13009 ASSERT(!scratch.is(dest));
13010 ASSERT(!scratch.is(src));
13011 ASSERT(!scratch.is(count));
13012
13013 // Nothing to do for zero characters.
13014 Label done;
13015 __ test(count, Operand(count));
13016 __ j(zero, &done);
13017
13018 // Make count the number of bytes to copy.
13019 if (!ascii) {
13020 __ shl(count, 1);
13021 }
13022
13023 // Don't enter the rep movs if there are less than 4 bytes to copy.
13024 Label last_bytes;
13025 __ test(count, Immediate(~3));
13026 __ j(zero, &last_bytes);
13027
13028 // Copy from edi to esi using rep movs instruction.
13029 __ mov(scratch, count);
13030 __ sar(count, 2); // Number of doublewords to copy.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000013031 __ cld();
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000013032 __ rep_movs();
13033
13034 // Find number of bytes left.
13035 __ mov(count, scratch);
13036 __ and_(count, 3);
13037
13038 // Check if there are more bytes to copy.
13039 __ bind(&last_bytes);
13040 __ test(count, Operand(count));
13041 __ j(zero, &done);
13042
13043 // Copy remaining characters.
13044 Label loop;
13045 __ bind(&loop);
13046 __ mov_b(scratch, Operand(src, 0));
13047 __ mov_b(Operand(dest, 0), scratch);
13048 __ add(Operand(src), Immediate(1));
13049 __ add(Operand(dest), Immediate(1));
13050 __ sub(Operand(count), Immediate(1));
13051 __ j(not_zero, &loop);
13052
13053 __ bind(&done);
13054}
13055
13056
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000013057void StringHelper::GenerateTwoCharacterSymbolTableProbe(MacroAssembler* masm,
13058 Register c1,
13059 Register c2,
13060 Register scratch1,
13061 Register scratch2,
13062 Register scratch3,
13063 Label* not_found) {
ager@chromium.org5c838252010-02-19 08:53:10 +000013064 // Register scratch3 is the general scratch register in this function.
13065 Register scratch = scratch3;
13066
13067 // Make sure that both characters are not digits as such strings has a
13068 // different hash algorithm. Don't try to look for these in the symbol table.
13069 Label not_array_index;
13070 __ mov(scratch, c1);
13071 __ sub(Operand(scratch), Immediate(static_cast<int>('0')));
13072 __ cmp(Operand(scratch), Immediate(static_cast<int>('9' - '0')));
13073 __ j(above, &not_array_index);
13074 __ mov(scratch, c2);
13075 __ sub(Operand(scratch), Immediate(static_cast<int>('0')));
13076 __ cmp(Operand(scratch), Immediate(static_cast<int>('9' - '0')));
13077 __ j(below_equal, not_found);
13078
13079 __ bind(&not_array_index);
13080 // Calculate the two character string hash.
13081 Register hash = scratch1;
13082 GenerateHashInit(masm, hash, c1, scratch);
13083 GenerateHashAddCharacter(masm, hash, c2, scratch);
13084 GenerateHashGetHash(masm, hash, scratch);
13085
13086 // Collect the two characters in a register.
13087 Register chars = c1;
13088 __ shl(c2, kBitsPerByte);
13089 __ or_(chars, Operand(c2));
13090
13091 // chars: two character string, char 1 in byte 0 and char 2 in byte 1.
13092 // hash: hash of two character string.
13093
13094 // Load the symbol table.
13095 Register symbol_table = c2;
13096 ExternalReference roots_address = ExternalReference::roots_address();
13097 __ mov(scratch, Immediate(Heap::kSymbolTableRootIndex));
13098 __ mov(symbol_table,
13099 Operand::StaticArray(scratch, times_pointer_size, roots_address));
13100
13101 // Calculate capacity mask from the symbol table capacity.
13102 Register mask = scratch2;
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000013103 __ mov(mask, FieldOperand(symbol_table, SymbolTable::kCapacityOffset));
ager@chromium.org5c838252010-02-19 08:53:10 +000013104 __ SmiUntag(mask);
13105 __ sub(Operand(mask), Immediate(1));
13106
13107 // Registers
13108 // chars: two character string, char 1 in byte 0 and char 2 in byte 1.
13109 // hash: hash of two character string
13110 // symbol_table: symbol table
13111 // mask: capacity mask
13112 // scratch: -
13113
13114 // Perform a number of probes in the symbol table.
13115 static const int kProbes = 4;
13116 Label found_in_symbol_table;
13117 Label next_probe[kProbes], next_probe_pop_mask[kProbes];
13118 for (int i = 0; i < kProbes; i++) {
13119 // Calculate entry in symbol table.
13120 __ mov(scratch, hash);
13121 if (i > 0) {
13122 __ add(Operand(scratch), Immediate(SymbolTable::GetProbeOffset(i)));
13123 }
13124 __ and_(scratch, Operand(mask));
13125
13126 // Load the entry from the symble table.
13127 Register candidate = scratch; // Scratch register contains candidate.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000013128 ASSERT_EQ(1, SymbolTable::kEntrySize);
ager@chromium.org5c838252010-02-19 08:53:10 +000013129 __ mov(candidate,
13130 FieldOperand(symbol_table,
13131 scratch,
13132 times_pointer_size,
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000013133 SymbolTable::kElementsStartOffset));
ager@chromium.org5c838252010-02-19 08:53:10 +000013134
13135 // If entry is undefined no string with this hash can be found.
13136 __ cmp(candidate, Factory::undefined_value());
13137 __ j(equal, not_found);
13138
13139 // If length is not 2 the string is not a candidate.
ager@chromium.orgac091b72010-05-05 07:34:42 +000013140 __ cmp(FieldOperand(candidate, String::kLengthOffset),
13141 Immediate(Smi::FromInt(2)));
ager@chromium.org5c838252010-02-19 08:53:10 +000013142 __ j(not_equal, &next_probe[i]);
13143
13144 // As we are out of registers save the mask on the stack and use that
13145 // register as a temporary.
13146 __ push(mask);
13147 Register temp = mask;
13148
13149 // Check that the candidate is a non-external ascii string.
13150 __ mov(temp, FieldOperand(candidate, HeapObject::kMapOffset));
13151 __ movzx_b(temp, FieldOperand(temp, Map::kInstanceTypeOffset));
13152 __ JumpIfInstanceTypeIsNotSequentialAscii(
13153 temp, temp, &next_probe_pop_mask[i]);
13154
13155 // Check if the two characters match.
13156 __ mov(temp, FieldOperand(candidate, SeqAsciiString::kHeaderSize));
13157 __ and_(temp, 0x0000ffff);
13158 __ cmp(chars, Operand(temp));
13159 __ j(equal, &found_in_symbol_table);
13160 __ bind(&next_probe_pop_mask[i]);
13161 __ pop(mask);
13162 __ bind(&next_probe[i]);
13163 }
13164
13165 // No matching 2 character string found by probing.
13166 __ jmp(not_found);
13167
13168 // Scratch register contains result when we fall through to here.
13169 Register result = scratch;
13170 __ bind(&found_in_symbol_table);
13171 __ pop(mask); // Pop temporally saved mask from the stack.
13172 if (!result.is(eax)) {
13173 __ mov(eax, result);
13174 }
13175}
13176
13177
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000013178void StringHelper::GenerateHashInit(MacroAssembler* masm,
13179 Register hash,
13180 Register character,
13181 Register scratch) {
ager@chromium.org5c838252010-02-19 08:53:10 +000013182 // hash = character + (character << 10);
13183 __ mov(hash, character);
13184 __ shl(hash, 10);
13185 __ add(hash, Operand(character));
13186 // hash ^= hash >> 6;
13187 __ mov(scratch, hash);
13188 __ sar(scratch, 6);
13189 __ xor_(hash, Operand(scratch));
13190}
13191
13192
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000013193void StringHelper::GenerateHashAddCharacter(MacroAssembler* masm,
13194 Register hash,
13195 Register character,
13196 Register scratch) {
ager@chromium.org5c838252010-02-19 08:53:10 +000013197 // hash += character;
13198 __ add(hash, Operand(character));
13199 // hash += hash << 10;
13200 __ mov(scratch, hash);
13201 __ shl(scratch, 10);
13202 __ add(hash, Operand(scratch));
13203 // hash ^= hash >> 6;
13204 __ mov(scratch, hash);
13205 __ sar(scratch, 6);
13206 __ xor_(hash, Operand(scratch));
13207}
13208
13209
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000013210void StringHelper::GenerateHashGetHash(MacroAssembler* masm,
13211 Register hash,
13212 Register scratch) {
ager@chromium.org5c838252010-02-19 08:53:10 +000013213 // hash += hash << 3;
13214 __ mov(scratch, hash);
13215 __ shl(scratch, 3);
13216 __ add(hash, Operand(scratch));
13217 // hash ^= hash >> 11;
13218 __ mov(scratch, hash);
13219 __ sar(scratch, 11);
13220 __ xor_(hash, Operand(scratch));
13221 // hash += hash << 15;
13222 __ mov(scratch, hash);
13223 __ shl(scratch, 15);
13224 __ add(hash, Operand(scratch));
13225
13226 // if (hash == 0) hash = 27;
13227 Label hash_not_zero;
13228 __ test(hash, Operand(hash));
13229 __ j(not_zero, &hash_not_zero);
13230 __ mov(hash, Immediate(27));
13231 __ bind(&hash_not_zero);
13232}
13233
13234
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000013235void SubStringStub::Generate(MacroAssembler* masm) {
13236 Label runtime;
13237
13238 // Stack frame on entry.
13239 // esp[0]: return address
13240 // esp[4]: to
13241 // esp[8]: from
13242 // esp[12]: string
13243
13244 // Make sure first argument is a string.
13245 __ mov(eax, Operand(esp, 3 * kPointerSize));
13246 ASSERT_EQ(0, kSmiTag);
13247 __ test(eax, Immediate(kSmiTagMask));
13248 __ j(zero, &runtime);
13249 Condition is_string = masm->IsObjectStringType(eax, ebx, ebx);
13250 __ j(NegateCondition(is_string), &runtime);
13251
13252 // eax: string
13253 // ebx: instance type
13254 // Calculate length of sub string using the smi values.
ager@chromium.org5c838252010-02-19 08:53:10 +000013255 Label result_longer_than_two;
13256 __ mov(ecx, Operand(esp, 1 * kPointerSize)); // To index.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000013257 __ test(ecx, Immediate(kSmiTagMask));
13258 __ j(not_zero, &runtime);
ager@chromium.org5c838252010-02-19 08:53:10 +000013259 __ mov(edx, Operand(esp, 2 * kPointerSize)); // From index.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000013260 __ test(edx, Immediate(kSmiTagMask));
13261 __ j(not_zero, &runtime);
13262 __ sub(ecx, Operand(edx));
ager@chromium.org5c838252010-02-19 08:53:10 +000013263 // Special handling of sub-strings of length 1 and 2. One character strings
13264 // are handled in the runtime system (looked up in the single character
13265 // cache). Two character strings are looked for in the symbol cache.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000013266 __ SmiUntag(ecx); // Result length is no longer smi.
13267 __ cmp(ecx, 2);
ager@chromium.org5c838252010-02-19 08:53:10 +000013268 __ j(greater, &result_longer_than_two);
13269 __ j(less, &runtime);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000013270
ager@chromium.org5c838252010-02-19 08:53:10 +000013271 // Sub string of length 2 requested.
13272 // eax: string
13273 // ebx: instance type
13274 // ecx: sub string length (value is 2)
13275 // edx: from index (smi)
13276 __ JumpIfInstanceTypeIsNotSequentialAscii(ebx, ebx, &runtime);
13277
13278 // Get the two characters forming the sub string.
13279 __ SmiUntag(edx); // From index is no longer smi.
13280 __ movzx_b(ebx, FieldOperand(eax, edx, times_1, SeqAsciiString::kHeaderSize));
13281 __ movzx_b(ecx,
13282 FieldOperand(eax, edx, times_1, SeqAsciiString::kHeaderSize + 1));
13283
13284 // Try to lookup two character string in symbol table.
13285 Label make_two_character_string;
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000013286 StringHelper::GenerateTwoCharacterSymbolTableProbe(
13287 masm, ebx, ecx, eax, edx, edi, &make_two_character_string);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000013288 __ ret(3 * kPointerSize);
ager@chromium.org5c838252010-02-19 08:53:10 +000013289
13290 __ bind(&make_two_character_string);
13291 // Setup registers for allocating the two character string.
13292 __ mov(eax, Operand(esp, 3 * kPointerSize));
13293 __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset));
13294 __ movzx_b(ebx, FieldOperand(ebx, Map::kInstanceTypeOffset));
13295 __ Set(ecx, Immediate(2));
13296
13297 __ bind(&result_longer_than_two);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000013298 // eax: string
13299 // ebx: instance type
13300 // ecx: result string length
13301 // Check for flat ascii string
13302 Label non_ascii_flat;
ager@chromium.org5c838252010-02-19 08:53:10 +000013303 __ JumpIfInstanceTypeIsNotSequentialAscii(ebx, ebx, &non_ascii_flat);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000013304
13305 // Allocate the result.
13306 __ AllocateAsciiString(eax, ecx, ebx, edx, edi, &runtime);
13307
13308 // eax: result string
13309 // ecx: result string length
13310 __ mov(edx, esi); // esi used by following code.
13311 // Locate first character of result.
13312 __ mov(edi, eax);
13313 __ add(Operand(edi), Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag));
13314 // Load string argument and locate character of sub string start.
13315 __ mov(esi, Operand(esp, 3 * kPointerSize));
13316 __ add(Operand(esi), Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag));
13317 __ mov(ebx, Operand(esp, 2 * kPointerSize)); // from
13318 __ SmiUntag(ebx);
13319 __ add(esi, Operand(ebx));
13320
13321 // eax: result string
13322 // ecx: result length
13323 // edx: original value of esi
13324 // edi: first character of result
13325 // esi: character of sub string start
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000013326 StringHelper::GenerateCopyCharactersREP(masm, edi, esi, ecx, ebx, true);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000013327 __ mov(esi, edx); // Restore esi.
13328 __ IncrementCounter(&Counters::sub_string_native, 1);
13329 __ ret(3 * kPointerSize);
13330
13331 __ bind(&non_ascii_flat);
13332 // eax: string
13333 // ebx: instance type & kStringRepresentationMask | kStringEncodingMask
13334 // ecx: result string length
13335 // Check for flat two byte string
13336 __ cmp(ebx, kSeqStringTag | kTwoByteStringTag);
13337 __ j(not_equal, &runtime);
13338
13339 // Allocate the result.
13340 __ AllocateTwoByteString(eax, ecx, ebx, edx, edi, &runtime);
13341
13342 // eax: result string
13343 // ecx: result string length
13344 __ mov(edx, esi); // esi used by following code.
13345 // Locate first character of result.
13346 __ mov(edi, eax);
13347 __ add(Operand(edi),
13348 Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
13349 // Load string argument and locate character of sub string start.
13350 __ mov(esi, Operand(esp, 3 * kPointerSize));
ager@chromium.org5c838252010-02-19 08:53:10 +000013351 __ add(Operand(esi),
13352 Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000013353 __ mov(ebx, Operand(esp, 2 * kPointerSize)); // from
13354 // As from is a smi it is 2 times the value which matches the size of a two
13355 // byte character.
13356 ASSERT_EQ(0, kSmiTag);
13357 ASSERT_EQ(1, kSmiTagSize + kSmiShiftSize);
13358 __ add(esi, Operand(ebx));
13359
13360 // eax: result string
13361 // ecx: result length
13362 // edx: original value of esi
13363 // edi: first character of result
13364 // esi: character of sub string start
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000013365 StringHelper::GenerateCopyCharactersREP(masm, edi, esi, ecx, ebx, false);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000013366 __ mov(esi, edx); // Restore esi.
13367 __ IncrementCounter(&Counters::sub_string_native, 1);
13368 __ ret(3 * kPointerSize);
13369
13370 // Just jump to runtime to create the sub string.
13371 __ bind(&runtime);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000013372 __ TailCallRuntime(Runtime::kSubString, 3, 1);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000013373}
13374
13375
13376void StringCompareStub::GenerateCompareFlatAsciiStrings(MacroAssembler* masm,
13377 Register left,
13378 Register right,
13379 Register scratch1,
13380 Register scratch2,
13381 Register scratch3) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000013382 Label result_not_equal;
13383 Label result_greater;
13384 Label compare_lengths;
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +000013385
13386 __ IncrementCounter(&Counters::string_compare_native, 1);
13387
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000013388 // Find minimum length.
13389 Label left_shorter;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000013390 __ mov(scratch1, FieldOperand(left, String::kLengthOffset));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000013391 __ mov(scratch3, scratch1);
13392 __ sub(scratch3, FieldOperand(right, String::kLengthOffset));
13393
13394 Register length_delta = scratch3;
13395
13396 __ j(less_equal, &left_shorter);
13397 // Right string is shorter. Change scratch1 to be length of right string.
13398 __ sub(scratch1, Operand(length_delta));
13399 __ bind(&left_shorter);
13400
13401 Register min_length = scratch1;
13402
13403 // If either length is zero, just compare lengths.
13404 __ test(min_length, Operand(min_length));
13405 __ j(zero, &compare_lengths);
13406
13407 // Change index to run from -min_length to -1 by adding min_length
13408 // to string start. This means that loop ends when index reaches zero,
13409 // which doesn't need an additional compare.
ager@chromium.orgac091b72010-05-05 07:34:42 +000013410 __ SmiUntag(min_length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000013411 __ lea(left,
13412 FieldOperand(left,
13413 min_length, times_1,
13414 SeqAsciiString::kHeaderSize));
13415 __ lea(right,
13416 FieldOperand(right,
13417 min_length, times_1,
13418 SeqAsciiString::kHeaderSize));
13419 __ neg(min_length);
13420
13421 Register index = min_length; // index = -min_length;
13422
13423 {
13424 // Compare loop.
13425 Label loop;
13426 __ bind(&loop);
13427 // Compare characters.
13428 __ mov_b(scratch2, Operand(left, index, times_1, 0));
13429 __ cmpb(scratch2, Operand(right, index, times_1, 0));
13430 __ j(not_equal, &result_not_equal);
13431 __ add(Operand(index), Immediate(1));
13432 __ j(not_zero, &loop);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000013433 }
13434
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000013435 // Compare lengths - strings up to min-length are equal.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000013436 __ bind(&compare_lengths);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000013437 __ test(length_delta, Operand(length_delta));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000013438 __ j(not_zero, &result_not_equal);
13439
13440 // Result is EQUAL.
13441 ASSERT_EQ(0, EQUAL);
13442 ASSERT_EQ(0, kSmiTag);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000013443 __ Set(eax, Immediate(Smi::FromInt(EQUAL)));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000013444 __ ret(2 * kPointerSize);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000013445
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000013446 __ bind(&result_not_equal);
13447 __ j(greater, &result_greater);
13448
13449 // Result is LESS.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000013450 __ Set(eax, Immediate(Smi::FromInt(LESS)));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000013451 __ ret(2 * kPointerSize);
13452
13453 // Result is GREATER.
13454 __ bind(&result_greater);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000013455 __ Set(eax, Immediate(Smi::FromInt(GREATER)));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000013456 __ ret(2 * kPointerSize);
13457}
13458
13459
13460void StringCompareStub::Generate(MacroAssembler* masm) {
13461 Label runtime;
13462
13463 // Stack frame on entry.
13464 // esp[0]: return address
13465 // esp[4]: right string
13466 // esp[8]: left string
13467
13468 __ mov(edx, Operand(esp, 2 * kPointerSize)); // left
13469 __ mov(eax, Operand(esp, 1 * kPointerSize)); // right
13470
13471 Label not_same;
13472 __ cmp(edx, Operand(eax));
13473 __ j(not_equal, &not_same);
13474 ASSERT_EQ(0, EQUAL);
13475 ASSERT_EQ(0, kSmiTag);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000013476 __ Set(eax, Immediate(Smi::FromInt(EQUAL)));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000013477 __ IncrementCounter(&Counters::string_compare_native, 1);
13478 __ ret(2 * kPointerSize);
13479
13480 __ bind(&not_same);
13481
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000013482 // Check that both objects are sequential ascii strings.
13483 __ JumpIfNotBothSequentialAsciiStrings(edx, eax, ecx, ebx, &runtime);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000013484
13485 // Compare flat ascii strings.
13486 GenerateCompareFlatAsciiStrings(masm, edx, eax, ecx, ebx, edi);
13487
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000013488 // Call the runtime; it returns -1 (less), 0 (equal), or 1 (greater)
13489 // tagged as a small integer.
13490 __ bind(&runtime);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000013491 __ TailCallRuntime(Runtime::kStringCompare, 2, 1);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000013492}
13493
ager@chromium.org7c537e22008-10-16 08:43:32 +000013494#undef __
13495
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000013496} } // namespace v8::internal
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +000013497
13498#endif // V8_TARGET_ARCH_IA32