blob: d6d5800fe6b87c3020971e31213fe5a6a49137f5 [file] [log] [blame]
ager@chromium.org9258b6b2008-09-11 09:11:10 +00001// Copyright 2006-2008 the V8 project authors. All rights reserved.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28#include "v8.h"
29
30#include "bootstrapper.h"
31#include "codegen-inl.h"
32#include "debug.h"
33#include "runtime.h"
34#include "serialize.h"
35
36namespace v8 { namespace internal {
37
kasperl@chromium.org7be3c992009-03-12 07:19:55 +000038// -------------------------------------------------------------------------
39// MacroAssembler implementation.
40
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000041MacroAssembler::MacroAssembler(void* buffer, int size)
42 : Assembler(buffer, size),
43 unresolved_(0),
kasper.lund7276f142008-07-30 08:49:36 +000044 generating_stub_(false),
kasperl@chromium.org061ef742009-02-27 12:16:20 +000045 allow_stub_calls_(true),
46 code_object_(Heap::undefined_value()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000047}
48
49
50static void RecordWriteHelper(MacroAssembler* masm,
51 Register object,
52 Register addr,
53 Register scratch) {
54 Label fast;
55
56 // Compute the page address from the heap object pointer, leave it
57 // in 'object'.
58 masm->and_(object, ~Page::kPageAlignmentMask);
59
60 // Compute the bit addr in the remembered set, leave it in "addr".
61 masm->sub(addr, Operand(object));
62 masm->shr(addr, kObjectAlignmentBits);
63
64 // If the bit offset lies beyond the normal remembered set range, it is in
65 // the extra remembered set area of a large object.
66 masm->cmp(addr, Page::kPageSize / kPointerSize);
67 masm->j(less, &fast);
68
69 // Adjust 'addr' to be relative to the start of the extra remembered set
70 // and the page address in 'object' to be the address of the extra
71 // remembered set.
72 masm->sub(Operand(addr), Immediate(Page::kPageSize / kPointerSize));
73 // Load the array length into 'scratch' and multiply by four to get the
74 // size in bytes of the elements.
75 masm->mov(scratch, Operand(object, Page::kObjectStartOffset
76 + FixedArray::kLengthOffset));
77 masm->shl(scratch, kObjectAlignmentBits);
78 // Add the page header, array header, and array body size to the page
79 // address.
80 masm->add(Operand(object), Immediate(Page::kObjectStartOffset
81 + Array::kHeaderSize));
82 masm->add(object, Operand(scratch));
83
84
85 // NOTE: For now, we use the bit-test-and-set (bts) x86 instruction
86 // to limit code size. We should probably evaluate this decision by
87 // measuring the performance of an equivalent implementation using
88 // "simpler" instructions
89 masm->bind(&fast);
90 masm->bts(Operand(object, 0), addr);
91}
92
93
94class RecordWriteStub : public CodeStub {
95 public:
96 RecordWriteStub(Register object, Register addr, Register scratch)
97 : object_(object), addr_(addr), scratch_(scratch) { }
98
99 void Generate(MacroAssembler* masm);
100
101 private:
102 Register object_;
103 Register addr_;
104 Register scratch_;
105
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000106#ifdef DEBUG
107 void Print() {
108 PrintF("RecordWriteStub (object reg %d), (addr reg %d), (scratch reg %d)\n",
109 object_.code(), addr_.code(), scratch_.code());
110 }
111#endif
112
113 // Minor key encoding in 12 bits of three registers (object, address and
114 // scratch) OOOOAAAASSSS.
115 class ScratchBits: public BitField<uint32_t, 0, 4> {};
116 class AddressBits: public BitField<uint32_t, 4, 4> {};
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000117 class ObjectBits: public BitField<uint32_t, 8, 4> {};
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000118
119 Major MajorKey() { return RecordWrite; }
120
121 int MinorKey() {
122 // Encode the registers.
123 return ObjectBits::encode(object_.code()) |
124 AddressBits::encode(addr_.code()) |
125 ScratchBits::encode(scratch_.code());
126 }
127};
128
129
130void RecordWriteStub::Generate(MacroAssembler* masm) {
131 RecordWriteHelper(masm, object_, addr_, scratch_);
132 masm->ret(0);
133}
134
135
136// Set the remembered set bit for [object+offset].
137// object is the object being stored into, value is the object being stored.
138// If offset is zero, then the scratch register contains the array index into
139// the elements array represented as a Smi.
140// All registers are clobbered by the operation.
141void MacroAssembler::RecordWrite(Register object, int offset,
142 Register value, Register scratch) {
143 // First, check if a remembered set write is even needed. The tests below
144 // catch stores of Smis and stores into young gen (which does not have space
145 // for the remembered set bits.
146 Label done;
147
148 // This optimization cannot survive serialization and deserialization,
149 // so we disable as long as serialization can take place.
150 int32_t new_space_start =
151 reinterpret_cast<int32_t>(ExternalReference::new_space_start().address());
152 if (Serializer::enabled() || new_space_start < 0) {
153 // Cannot do smart bit-twiddling. Need to do two consecutive checks.
154 // Check for Smi first.
155 test(value, Immediate(kSmiTagMask));
156 j(zero, &done);
157 // Test that the object address is not in the new space. We cannot
158 // set remembered set bits in the new space.
159 mov(value, Operand(object));
160 and_(value, Heap::NewSpaceMask());
161 cmp(Operand(value), Immediate(ExternalReference::new_space_start()));
162 j(equal, &done);
163 } else {
164 // move the value SmiTag into the sign bit
165 shl(value, 31);
166 // combine the object with value SmiTag
167 or_(value, Operand(object));
168 // remove the uninteresing bits inside the page
169 and_(value, Heap::NewSpaceMask() | (1 << 31));
170 // xor has two effects:
171 // - if the value was a smi, then the result will be negative
172 // - if the object is pointing into new space area the page bits will
173 // all be zero
174 xor_(value, new_space_start | (1 << 31));
175 // Check for both conditions in one branch
176 j(less_equal, &done);
177 }
178
179 if ((offset > 0) && (offset < Page::kMaxHeapObjectSize)) {
180 // Compute the bit offset in the remembered set, leave it in 'value'.
181 mov(value, Operand(object));
182 and_(value, Page::kPageAlignmentMask);
183 add(Operand(value), Immediate(offset));
184 shr(value, kObjectAlignmentBits);
185
186 // Compute the page address from the heap object pointer, leave it in
187 // 'object'.
188 and_(object, ~Page::kPageAlignmentMask);
189
190 // NOTE: For now, we use the bit-test-and-set (bts) x86 instruction
191 // to limit code size. We should probably evaluate this decision by
192 // measuring the performance of an equivalent implementation using
193 // "simpler" instructions
194 bts(Operand(object, 0), value);
195 } else {
196 Register dst = scratch;
197 if (offset != 0) {
198 lea(dst, Operand(object, offset));
199 } else {
200 // array access: calculate the destination address in the same manner as
201 // KeyedStoreIC::GenerateGeneric
202 lea(dst,
203 Operand(object, dst, times_2, Array::kHeaderSize - kHeapObjectTag));
204 }
205 // If we are already generating a shared stub, not inlining the
206 // record write code isn't going to save us any memory.
207 if (generating_stub()) {
208 RecordWriteHelper(this, object, dst, value);
209 } else {
210 RecordWriteStub stub(object, dst, value);
211 CallStub(&stub);
212 }
213 }
214
215 bind(&done);
216}
217
218
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000219#ifdef ENABLE_DEBUGGER_SUPPORT
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000220void MacroAssembler::SaveRegistersToMemory(RegList regs) {
221 ASSERT((regs & ~kJSCallerSaved) == 0);
222 // Copy the content of registers to memory location.
223 for (int i = 0; i < kNumJSCallerSaved; i++) {
224 int r = JSCallerSavedCode(i);
225 if ((regs & (1 << r)) != 0) {
226 Register reg = { r };
227 ExternalReference reg_addr =
228 ExternalReference(Debug_Address::Register(i));
229 mov(Operand::StaticVariable(reg_addr), reg);
230 }
231 }
232}
233
234
235void MacroAssembler::RestoreRegistersFromMemory(RegList regs) {
236 ASSERT((regs & ~kJSCallerSaved) == 0);
237 // Copy the content of memory location to registers.
238 for (int i = kNumJSCallerSaved; --i >= 0;) {
239 int r = JSCallerSavedCode(i);
240 if ((regs & (1 << r)) != 0) {
241 Register reg = { r };
242 ExternalReference reg_addr =
243 ExternalReference(Debug_Address::Register(i));
244 mov(reg, Operand::StaticVariable(reg_addr));
245 }
246 }
247}
248
249
250void MacroAssembler::PushRegistersFromMemory(RegList regs) {
251 ASSERT((regs & ~kJSCallerSaved) == 0);
252 // Push the content of the memory location to the stack.
253 for (int i = 0; i < kNumJSCallerSaved; i++) {
254 int r = JSCallerSavedCode(i);
255 if ((regs & (1 << r)) != 0) {
256 ExternalReference reg_addr =
257 ExternalReference(Debug_Address::Register(i));
258 push(Operand::StaticVariable(reg_addr));
259 }
260 }
261}
262
263
264void MacroAssembler::PopRegistersToMemory(RegList regs) {
265 ASSERT((regs & ~kJSCallerSaved) == 0);
266 // Pop the content from the stack to the memory location.
267 for (int i = kNumJSCallerSaved; --i >= 0;) {
268 int r = JSCallerSavedCode(i);
269 if ((regs & (1 << r)) != 0) {
270 ExternalReference reg_addr =
271 ExternalReference(Debug_Address::Register(i));
272 pop(Operand::StaticVariable(reg_addr));
273 }
274 }
275}
276
277
278void MacroAssembler::CopyRegistersFromStackToMemory(Register base,
279 Register scratch,
280 RegList regs) {
281 ASSERT((regs & ~kJSCallerSaved) == 0);
282 // Copy the content of the stack to the memory location and adjust base.
283 for (int i = kNumJSCallerSaved; --i >= 0;) {
284 int r = JSCallerSavedCode(i);
285 if ((regs & (1 << r)) != 0) {
286 mov(scratch, Operand(base, 0));
287 ExternalReference reg_addr =
288 ExternalReference(Debug_Address::Register(i));
289 mov(Operand::StaticVariable(reg_addr), scratch);
290 lea(base, Operand(base, kPointerSize));
291 }
292 }
293}
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000294#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000295
296void MacroAssembler::Set(Register dst, const Immediate& x) {
297 if (x.is_zero()) {
298 xor_(dst, Operand(dst)); // shorter than mov
299 } else {
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000300 mov(dst, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000301 }
302}
303
304
305void MacroAssembler::Set(const Operand& dst, const Immediate& x) {
306 mov(dst, x);
307}
308
309
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000310void MacroAssembler::CmpObjectType(Register heap_object,
311 InstanceType type,
312 Register map) {
313 mov(map, FieldOperand(heap_object, HeapObject::kMapOffset));
314 CmpInstanceType(map, type);
315}
316
317
318void MacroAssembler::CmpInstanceType(Register map, InstanceType type) {
319 cmpb(FieldOperand(map, Map::kInstanceTypeOffset),
320 static_cast<int8_t>(type));
321}
322
323
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000324void MacroAssembler::FCmp() {
325 fcompp();
326 push(eax);
327 fnstsw_ax();
328 sahf();
329 pop(eax);
330}
331
332
ager@chromium.org7c537e22008-10-16 08:43:32 +0000333void MacroAssembler::EnterFrame(StackFrame::Type type) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000334 push(ebp);
335 mov(ebp, Operand(esp));
336 push(esi);
337 push(Immediate(Smi::FromInt(type)));
kasperl@chromium.org061ef742009-02-27 12:16:20 +0000338 push(Immediate(CodeObject()));
339 if (FLAG_debug_code) {
340 cmp(Operand(esp, 0), Immediate(Factory::undefined_value()));
341 Check(not_equal, "code object not properly patched");
342 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000343}
344
345
ager@chromium.org7c537e22008-10-16 08:43:32 +0000346void MacroAssembler::LeaveFrame(StackFrame::Type type) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000347 if (FLAG_debug_code) {
348 cmp(Operand(ebp, StandardFrameConstants::kMarkerOffset),
349 Immediate(Smi::FromInt(type)));
350 Check(equal, "stack frame types must match");
351 }
352 leave();
353}
354
355
ager@chromium.org236ad962008-09-25 09:45:57 +0000356void MacroAssembler::EnterExitFrame(StackFrame::Type type) {
357 ASSERT(type == StackFrame::EXIT || type == StackFrame::EXIT_DEBUG);
358
359 // Setup the frame structure on the stack.
360 ASSERT(ExitFrameConstants::kPPDisplacement == +2 * kPointerSize);
361 ASSERT(ExitFrameConstants::kCallerPCOffset == +1 * kPointerSize);
362 ASSERT(ExitFrameConstants::kCallerFPOffset == 0 * kPointerSize);
363 push(ebp);
364 mov(ebp, Operand(esp));
365
366 // Reserve room for entry stack pointer and push the debug marker.
367 ASSERT(ExitFrameConstants::kSPOffset == -1 * kPointerSize);
368 push(Immediate(0)); // saved entry sp, patched before call
369 push(Immediate(type == StackFrame::EXIT_DEBUG ? 1 : 0));
370
371 // Save the frame pointer and the context in top.
372 ExternalReference c_entry_fp_address(Top::k_c_entry_fp_address);
373 ExternalReference context_address(Top::k_context_address);
374 mov(Operand::StaticVariable(c_entry_fp_address), ebp);
375 mov(Operand::StaticVariable(context_address), esi);
376
377 // Setup argc and argv in callee-saved registers.
378 int offset = StandardFrameConstants::kCallerSPOffset - kPointerSize;
379 mov(edi, Operand(eax));
380 lea(esi, Operand(ebp, eax, times_4, offset));
381
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000382#ifdef ENABLE_DEBUGGER_SUPPORT
ager@chromium.org236ad962008-09-25 09:45:57 +0000383 // Save the state of all registers to the stack from the memory
384 // location. This is needed to allow nested break points.
385 if (type == StackFrame::EXIT_DEBUG) {
386 // TODO(1243899): This should be symmetric to
387 // CopyRegistersFromStackToMemory() but it isn't! esp is assumed
388 // correct here, but computed for the other call. Very error
389 // prone! FIX THIS. Actually there are deeper problems with
390 // register saving than this asymmetry (see the bug report
391 // associated with this issue).
392 PushRegistersFromMemory(kJSCallerSaved);
393 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000394#endif
ager@chromium.org236ad962008-09-25 09:45:57 +0000395
396 // Reserve space for two arguments: argc and argv.
397 sub(Operand(esp), Immediate(2 * kPointerSize));
398
399 // Get the required frame alignment for the OS.
400 static const int kFrameAlignment = OS::ActivationFrameAlignment();
401 if (kFrameAlignment > 0) {
402 ASSERT(IsPowerOf2(kFrameAlignment));
403 and_(esp, -kFrameAlignment);
404 }
405
406 // Patch the saved entry sp.
407 mov(Operand(ebp, ExitFrameConstants::kSPOffset), esp);
408}
409
410
411void MacroAssembler::LeaveExitFrame(StackFrame::Type type) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000412#ifdef ENABLE_DEBUGGER_SUPPORT
ager@chromium.org236ad962008-09-25 09:45:57 +0000413 // Restore the memory copy of the registers by digging them out from
414 // the stack. This is needed to allow nested break points.
415 if (type == StackFrame::EXIT_DEBUG) {
416 // It's okay to clobber register ebx below because we don't need
417 // the function pointer after this.
418 const int kCallerSavedSize = kNumJSCallerSaved * kPointerSize;
419 int kOffset = ExitFrameConstants::kDebugMarkOffset - kCallerSavedSize;
420 lea(ebx, Operand(ebp, kOffset));
421 CopyRegistersFromStackToMemory(ebx, ecx, kJSCallerSaved);
422 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000423#endif
ager@chromium.org236ad962008-09-25 09:45:57 +0000424
425 // Get the return address from the stack and restore the frame pointer.
426 mov(ecx, Operand(ebp, 1 * kPointerSize));
427 mov(ebp, Operand(ebp, 0 * kPointerSize));
428
429 // Pop the arguments and the receiver from the caller stack.
430 lea(esp, Operand(esi, 1 * kPointerSize));
431
432 // Restore current context from top and clear it in debug mode.
433 ExternalReference context_address(Top::k_context_address);
434 mov(esi, Operand::StaticVariable(context_address));
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000435#ifdef DEBUG
436 mov(Operand::StaticVariable(context_address), Immediate(0));
437#endif
ager@chromium.org236ad962008-09-25 09:45:57 +0000438
439 // Push the return address to get ready to return.
440 push(ecx);
441
442 // Clear the top frame.
443 ExternalReference c_entry_fp_address(Top::k_c_entry_fp_address);
444 mov(Operand::StaticVariable(c_entry_fp_address), Immediate(0));
445}
446
447
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000448void MacroAssembler::PushTryHandler(CodeLocation try_location,
449 HandlerType type) {
450 ASSERT(StackHandlerConstants::kSize == 6 * kPointerSize); // adjust this code
451 // The pc (return address) is already on TOS.
452 if (try_location == IN_JAVASCRIPT) {
453 if (type == TRY_CATCH_HANDLER) {
454 push(Immediate(StackHandler::TRY_CATCH));
455 } else {
456 push(Immediate(StackHandler::TRY_FINALLY));
457 }
458 push(Immediate(Smi::FromInt(StackHandler::kCodeNotPresent)));
459 push(ebp);
460 push(edi);
461 } else {
462 ASSERT(try_location == IN_JS_ENTRY);
463 // The parameter pointer is meaningless here and ebp does not
464 // point to a JS frame. So we save NULL for both pp and ebp. We
465 // expect the code throwing an exception to check ebp before
466 // dereferencing it to restore the context.
467 push(Immediate(StackHandler::ENTRY));
468 push(Immediate(Smi::FromInt(StackHandler::kCodeNotPresent)));
469 push(Immediate(0)); // NULL frame pointer
470 push(Immediate(0)); // NULL parameter pointer
471 }
472 // Cached TOS.
473 mov(eax, Operand::StaticVariable(ExternalReference(Top::k_handler_address)));
474 // Link this handler.
475 mov(Operand::StaticVariable(ExternalReference(Top::k_handler_address)), esp);
476}
477
478
479Register MacroAssembler::CheckMaps(JSObject* object, Register object_reg,
480 JSObject* holder, Register holder_reg,
481 Register scratch,
482 Label* miss) {
483 // Make sure there's no overlap between scratch and the other
484 // registers.
485 ASSERT(!scratch.is(object_reg) && !scratch.is(holder_reg));
486
487 // Keep track of the current object in register reg.
488 Register reg = object_reg;
489 int depth = 1;
490
491 // Check the maps in the prototype chain.
492 // Traverse the prototype chain from the object and do map checks.
493 while (object != holder) {
494 depth++;
495
496 // Only global objects and objects that do not require access
497 // checks are allowed in stubs.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000498 ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000499
500 JSObject* prototype = JSObject::cast(object->GetPrototype());
501 if (Heap::InNewSpace(prototype)) {
502 // Get the map of the current object.
503 mov(scratch, FieldOperand(reg, HeapObject::kMapOffset));
504 cmp(Operand(scratch), Immediate(Handle<Map>(object->map())));
505 // Branch on the result of the map check.
506 j(not_equal, miss, not_taken);
507 // Check access rights to the global object. This has to happen
508 // after the map check so that we know that the object is
509 // actually a global object.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000510 if (object->IsJSGlobalProxy()) {
511 CheckAccessGlobalProxy(reg, scratch, miss);
512
513 // Restore scratch register to be the map of the object.
514 // We load the prototype from the map in the scratch register.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000515 mov(scratch, FieldOperand(reg, HeapObject::kMapOffset));
516 }
517 // The prototype is in new space; we cannot store a reference
518 // to it in the code. Load it from the map.
519 reg = holder_reg; // from now the object is in holder_reg
520 mov(reg, FieldOperand(scratch, Map::kPrototypeOffset));
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000521
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000522 } else {
523 // Check the map of the current object.
524 cmp(FieldOperand(reg, HeapObject::kMapOffset),
525 Immediate(Handle<Map>(object->map())));
526 // Branch on the result of the map check.
527 j(not_equal, miss, not_taken);
528 // Check access rights to the global object. This has to happen
529 // after the map check so that we know that the object is
530 // actually a global object.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000531 if (object->IsJSGlobalProxy()) {
532 CheckAccessGlobalProxy(reg, scratch, miss);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000533 }
534 // The prototype is in old space; load it directly.
535 reg = holder_reg; // from now the object is in holder_reg
536 mov(reg, Handle<JSObject>(prototype));
537 }
538
539 // Go to the next object in the prototype chain.
540 object = prototype;
541 }
542
543 // Check the holder map.
544 cmp(FieldOperand(reg, HeapObject::kMapOffset),
545 Immediate(Handle<Map>(holder->map())));
546 j(not_equal, miss, not_taken);
547
548 // Log the check depth.
549 LOG(IntEvent("check-maps-depth", depth));
550
551 // Perform security check for access to the global object and return
552 // the holder register.
553 ASSERT(object == holder);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000554 ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded());
555 if (object->IsJSGlobalProxy()) {
556 CheckAccessGlobalProxy(reg, scratch, miss);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000557 }
558 return reg;
559}
560
561
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000562void MacroAssembler::CheckAccessGlobalProxy(Register holder_reg,
563 Register scratch,
564 Label* miss) {
565 Label same_contexts;
566
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000567 ASSERT(!holder_reg.is(scratch));
568
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000569 // Load current lexical context from the stack frame.
570 mov(scratch, Operand(ebp, StandardFrameConstants::kContextOffset));
571
572 // When generating debug code, make sure the lexical context is set.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000573 if (FLAG_debug_code) {
574 cmp(Operand(scratch), Immediate(0));
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000575 Check(not_equal, "we should not have an empty lexical context");
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000576 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000577 // Load the global context of the current context.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000578 int offset = Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize;
579 mov(scratch, FieldOperand(scratch, offset));
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000580 mov(scratch, FieldOperand(scratch, GlobalObject::kGlobalContextOffset));
581
582 // Check the context is a global context.
583 if (FLAG_debug_code) {
584 push(scratch);
585 // Read the first word and compare to global_context_map.
586 mov(scratch, FieldOperand(scratch, HeapObject::kMapOffset));
587 cmp(scratch, Factory::global_context_map());
588 Check(equal, "JSGlobalObject::global_context should be a global context.");
589 pop(scratch);
590 }
591
592 // Check if both contexts are the same.
593 cmp(scratch, FieldOperand(holder_reg, JSGlobalProxy::kContextOffset));
594 j(equal, &same_contexts, taken);
595
596 // Compare security tokens, save holder_reg on the stack so we can use it
597 // as a temporary register.
598 //
599 // TODO(119): avoid push(holder_reg)/pop(holder_reg)
600 push(holder_reg);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000601 // Check that the security token in the calling global object is
602 // compatible with the security token in the receiving global
603 // object.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000604 mov(holder_reg, FieldOperand(holder_reg, JSGlobalProxy::kContextOffset));
605
606 // Check the context is a global context.
607 if (FLAG_debug_code) {
608 cmp(holder_reg, Factory::null_value());
609 Check(not_equal, "JSGlobalProxy::context() should not be null.");
610
611 push(holder_reg);
612 // Read the first word and compare to global_context_map(),
613 mov(holder_reg, FieldOperand(holder_reg, HeapObject::kMapOffset));
614 cmp(holder_reg, Factory::global_context_map());
615 Check(equal, "JSGlobalObject::global_context should be a global context.");
616 pop(holder_reg);
617 }
618
619 int token_offset = Context::kHeaderSize +
620 Context::SECURITY_TOKEN_INDEX * kPointerSize;
621 mov(scratch, FieldOperand(scratch, token_offset));
622 cmp(scratch, FieldOperand(holder_reg, token_offset));
623 pop(holder_reg);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000624 j(not_equal, miss, not_taken);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000625
626 bind(&same_contexts);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000627}
628
629
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000630void MacroAssembler::NegativeZeroTest(CodeGenerator* cgen,
631 Register result,
632 Register op,
633 JumpTarget* then_target) {
634 JumpTarget ok(cgen);
635 test(result, Operand(result));
636 ok.Branch(not_zero, taken);
637 test(op, Operand(op));
638 then_target->Branch(sign, not_taken);
639 ok.Bind();
640}
641
642
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000643void MacroAssembler::NegativeZeroTest(Register result,
644 Register op,
645 Label* then_label) {
646 Label ok;
647 test(result, Operand(result));
648 j(not_zero, &ok, taken);
649 test(op, Operand(op));
650 j(sign, then_label, not_taken);
651 bind(&ok);
652}
653
654
655void MacroAssembler::NegativeZeroTest(Register result,
656 Register op1,
657 Register op2,
658 Register scratch,
659 Label* then_label) {
660 Label ok;
661 test(result, Operand(result));
662 j(not_zero, &ok, taken);
663 mov(scratch, Operand(op1));
664 or_(scratch, Operand(op2));
665 j(sign, then_label, not_taken);
666 bind(&ok);
667}
668
669
ager@chromium.org7c537e22008-10-16 08:43:32 +0000670void MacroAssembler::TryGetFunctionPrototype(Register function,
671 Register result,
672 Register scratch,
673 Label* miss) {
674 // Check that the receiver isn't a smi.
675 test(function, Immediate(kSmiTagMask));
676 j(zero, miss, not_taken);
677
678 // Check that the function really is a function.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000679 CmpObjectType(function, JS_FUNCTION_TYPE, result);
ager@chromium.org7c537e22008-10-16 08:43:32 +0000680 j(not_equal, miss, not_taken);
681
682 // Make sure that the function has an instance prototype.
683 Label non_instance;
684 movzx_b(scratch, FieldOperand(result, Map::kBitFieldOffset));
685 test(scratch, Immediate(1 << Map::kHasNonInstancePrototype));
686 j(not_zero, &non_instance, not_taken);
687
688 // Get the prototype or initial map from the function.
689 mov(result,
690 FieldOperand(function, JSFunction::kPrototypeOrInitialMapOffset));
691
692 // If the prototype or initial map is the hole, don't return it and
693 // simply miss the cache instead. This will allow us to allocate a
694 // prototype object on-demand in the runtime system.
695 cmp(Operand(result), Immediate(Factory::the_hole_value()));
696 j(equal, miss, not_taken);
697
698 // If the function does not have an initial map, we're done.
699 Label done;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000700 CmpObjectType(result, MAP_TYPE, scratch);
ager@chromium.org7c537e22008-10-16 08:43:32 +0000701 j(not_equal, &done);
702
703 // Get the prototype from the initial map.
704 mov(result, FieldOperand(result, Map::kPrototypeOffset));
705 jmp(&done);
706
707 // Non-instance prototype: Fetch prototype from constructor field
708 // in initial map.
709 bind(&non_instance);
710 mov(result, FieldOperand(result, Map::kConstructorOffset));
711
712 // All done.
713 bind(&done);
714}
715
716
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000717void MacroAssembler::CallStub(CodeStub* stub) {
kasper.lund7276f142008-07-30 08:49:36 +0000718 ASSERT(allow_stub_calls()); // calls are not allowed in some stubs
ager@chromium.org236ad962008-09-25 09:45:57 +0000719 call(stub->GetCode(), RelocInfo::CODE_TARGET);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000720}
721
722
723void MacroAssembler::StubReturn(int argc) {
724 ASSERT(argc >= 1 && generating_stub());
725 ret((argc - 1) * kPointerSize);
726}
727
728
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000729void MacroAssembler::IllegalOperation(int num_arguments) {
730 if (num_arguments > 0) {
731 add(Operand(esp), Immediate(num_arguments * kPointerSize));
732 }
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000733 mov(eax, Immediate(Factory::undefined_value()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000734}
735
736
737void MacroAssembler::CallRuntime(Runtime::FunctionId id, int num_arguments) {
738 CallRuntime(Runtime::FunctionForId(id), num_arguments);
739}
740
741
742void MacroAssembler::CallRuntime(Runtime::Function* f, int num_arguments) {
mads.s.ager31e71382008-08-13 09:32:07 +0000743 // If the expected number of arguments of the runtime function is
744 // constant, we check that the actual number of arguments match the
745 // expectation.
746 if (f->nargs >= 0 && f->nargs != num_arguments) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000747 IllegalOperation(num_arguments);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000748 return;
749 }
750
mads.s.ager31e71382008-08-13 09:32:07 +0000751 Runtime::FunctionId function_id =
752 static_cast<Runtime::FunctionId>(f->stub_id);
753 RuntimeStub stub(function_id, num_arguments);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000754 CallStub(&stub);
755}
756
757
mads.s.ager31e71382008-08-13 09:32:07 +0000758void MacroAssembler::TailCallRuntime(const ExternalReference& ext,
759 int num_arguments) {
760 // TODO(1236192): Most runtime routines don't need the number of
761 // arguments passed in because it is constant. At some point we
762 // should remove this need and make the runtime routine entry code
763 // smarter.
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000764 Set(eax, Immediate(num_arguments));
mads.s.ager31e71382008-08-13 09:32:07 +0000765 JumpToBuiltin(ext);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000766}
767
768
769void MacroAssembler::JumpToBuiltin(const ExternalReference& ext) {
770 // Set the entry point and jump to the C entry runtime stub.
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000771 mov(ebx, Immediate(ext));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000772 CEntryStub ces;
ager@chromium.org236ad962008-09-25 09:45:57 +0000773 jmp(ces.GetCode(), RelocInfo::CODE_TARGET);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000774}
775
776
777void MacroAssembler::InvokePrologue(const ParameterCount& expected,
778 const ParameterCount& actual,
779 Handle<Code> code_constant,
780 const Operand& code_operand,
781 Label* done,
782 InvokeFlag flag) {
783 bool definitely_matches = false;
784 Label invoke;
785 if (expected.is_immediate()) {
786 ASSERT(actual.is_immediate());
787 if (expected.immediate() == actual.immediate()) {
788 definitely_matches = true;
789 } else {
790 mov(eax, actual.immediate());
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000791 const int sentinel = SharedFunctionInfo::kDontAdaptArgumentsSentinel;
792 if (expected.immediate() == sentinel) {
793 // Don't worry about adapting arguments for builtins that
794 // don't want that done. Skip adaption code by making it look
795 // like we have a match between expected and actual number of
796 // arguments.
797 definitely_matches = true;
798 } else {
799 mov(ebx, expected.immediate());
800 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000801 }
802 } else {
803 if (actual.is_immediate()) {
804 // Expected is in register, actual is immediate. This is the
805 // case when we invoke function values without going through the
806 // IC mechanism.
807 cmp(expected.reg(), actual.immediate());
808 j(equal, &invoke);
809 ASSERT(expected.reg().is(ebx));
810 mov(eax, actual.immediate());
811 } else if (!expected.reg().is(actual.reg())) {
812 // Both expected and actual are in (different) registers. This
813 // is the case when we invoke functions using call and apply.
814 cmp(expected.reg(), Operand(actual.reg()));
815 j(equal, &invoke);
816 ASSERT(actual.reg().is(eax));
817 ASSERT(expected.reg().is(ebx));
818 }
819 }
820
821 if (!definitely_matches) {
822 Handle<Code> adaptor =
823 Handle<Code>(Builtins::builtin(Builtins::ArgumentsAdaptorTrampoline));
824 if (!code_constant.is_null()) {
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000825 mov(edx, Immediate(code_constant));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000826 add(Operand(edx), Immediate(Code::kHeaderSize - kHeapObjectTag));
827 } else if (!code_operand.is_reg(edx)) {
828 mov(edx, code_operand);
829 }
830
831 if (flag == CALL_FUNCTION) {
ager@chromium.org236ad962008-09-25 09:45:57 +0000832 call(adaptor, RelocInfo::CODE_TARGET);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000833 jmp(done);
834 } else {
ager@chromium.org236ad962008-09-25 09:45:57 +0000835 jmp(adaptor, RelocInfo::CODE_TARGET);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000836 }
837 bind(&invoke);
838 }
839}
840
841
842void MacroAssembler::InvokeCode(const Operand& code,
843 const ParameterCount& expected,
844 const ParameterCount& actual,
845 InvokeFlag flag) {
846 Label done;
847 InvokePrologue(expected, actual, Handle<Code>::null(), code, &done, flag);
848 if (flag == CALL_FUNCTION) {
849 call(code);
850 } else {
851 ASSERT(flag == JUMP_FUNCTION);
852 jmp(code);
853 }
854 bind(&done);
855}
856
857
858void MacroAssembler::InvokeCode(Handle<Code> code,
859 const ParameterCount& expected,
860 const ParameterCount& actual,
ager@chromium.org236ad962008-09-25 09:45:57 +0000861 RelocInfo::Mode rmode,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000862 InvokeFlag flag) {
863 Label done;
864 Operand dummy(eax);
865 InvokePrologue(expected, actual, code, dummy, &done, flag);
866 if (flag == CALL_FUNCTION) {
867 call(code, rmode);
868 } else {
869 ASSERT(flag == JUMP_FUNCTION);
870 jmp(code, rmode);
871 }
872 bind(&done);
873}
874
875
876void MacroAssembler::InvokeFunction(Register fun,
877 const ParameterCount& actual,
878 InvokeFlag flag) {
879 ASSERT(fun.is(edi));
880 mov(edx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
881 mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
882 mov(ebx, FieldOperand(edx, SharedFunctionInfo::kFormalParameterCountOffset));
883 mov(edx, FieldOperand(edx, SharedFunctionInfo::kCodeOffset));
884 lea(edx, FieldOperand(edx, Code::kHeaderSize));
885
886 ParameterCount expected(ebx);
887 InvokeCode(Operand(edx), expected, actual, flag);
888}
889
890
891void MacroAssembler::InvokeBuiltin(Builtins::JavaScript id, InvokeFlag flag) {
892 bool resolved;
893 Handle<Code> code = ResolveBuiltin(id, &resolved);
894
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000895 // Calls are not allowed in some stubs.
kasper.lund7276f142008-07-30 08:49:36 +0000896 ASSERT(flag == JUMP_FUNCTION || allow_stub_calls());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000897
898 // Rely on the assertion to check that the number of provided
899 // arguments match the expected number of arguments. Fake a
900 // parameter count to avoid emitting code to do the check.
901 ParameterCount expected(0);
ager@chromium.org236ad962008-09-25 09:45:57 +0000902 InvokeCode(Handle<Code>(code), expected, expected,
903 RelocInfo::CODE_TARGET, flag);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000904
905 const char* name = Builtins::GetName(id);
906 int argc = Builtins::GetArgumentsCount(id);
907
908 if (!resolved) {
909 uint32_t flags =
910 Bootstrapper::FixupFlagsArgumentsCount::encode(argc) |
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000911 Bootstrapper::FixupFlagsIsPCRelative::encode(true) |
912 Bootstrapper::FixupFlagsUseCodeObject::encode(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000913 Unresolved entry = { pc_offset() - sizeof(int32_t), flags, name };
914 unresolved_.Add(entry);
915 }
916}
917
918
919void MacroAssembler::GetBuiltinEntry(Register target, Builtins::JavaScript id) {
920 bool resolved;
921 Handle<Code> code = ResolveBuiltin(id, &resolved);
922
923 const char* name = Builtins::GetName(id);
924 int argc = Builtins::GetArgumentsCount(id);
925
926 mov(Operand(target), Immediate(code));
927 if (!resolved) {
928 uint32_t flags =
929 Bootstrapper::FixupFlagsArgumentsCount::encode(argc) |
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000930 Bootstrapper::FixupFlagsIsPCRelative::encode(false) |
931 Bootstrapper::FixupFlagsUseCodeObject::encode(true);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000932 Unresolved entry = { pc_offset() - sizeof(int32_t), flags, name };
933 unresolved_.Add(entry);
934 }
935 add(Operand(target), Immediate(Code::kHeaderSize - kHeapObjectTag));
936}
937
938
939Handle<Code> MacroAssembler::ResolveBuiltin(Builtins::JavaScript id,
940 bool* resolved) {
941 // Move the builtin function into the temporary function slot by
942 // reading it from the builtins object. NOTE: We should be able to
943 // reduce this to two instructions by putting the function table in
944 // the global object instead of the "builtins" object and by using a
945 // real register for the function.
946 mov(edx, Operand(esi, Context::SlotOffset(Context::GLOBAL_INDEX)));
947 mov(edx, FieldOperand(edx, GlobalObject::kBuiltinsOffset));
948 int builtins_offset =
949 JSBuiltinsObject::kJSBuiltinsOffset + (id * kPointerSize);
950 mov(edi, FieldOperand(edx, builtins_offset));
951
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000952
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000953 return Builtins::GetCode(id, resolved);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000954}
955
956
957void MacroAssembler::Ret() {
958 ret(0);
959}
960
961
962void MacroAssembler::SetCounter(StatsCounter* counter, int value) {
963 if (FLAG_native_code_counters && counter->Enabled()) {
964 mov(Operand::StaticVariable(ExternalReference(counter)), Immediate(value));
965 }
966}
967
968
969void MacroAssembler::IncrementCounter(StatsCounter* counter, int value) {
970 ASSERT(value > 0);
971 if (FLAG_native_code_counters && counter->Enabled()) {
972 Operand operand = Operand::StaticVariable(ExternalReference(counter));
973 if (value == 1) {
974 inc(operand);
975 } else {
976 add(operand, Immediate(value));
977 }
978 }
979}
980
981
982void MacroAssembler::DecrementCounter(StatsCounter* counter, int value) {
983 ASSERT(value > 0);
984 if (FLAG_native_code_counters && counter->Enabled()) {
985 Operand operand = Operand::StaticVariable(ExternalReference(counter));
986 if (value == 1) {
987 dec(operand);
988 } else {
989 sub(operand, Immediate(value));
990 }
991 }
992}
993
994
995void MacroAssembler::Assert(Condition cc, const char* msg) {
996 if (FLAG_debug_code) Check(cc, msg);
997}
998
999
1000void MacroAssembler::Check(Condition cc, const char* msg) {
1001 Label L;
1002 j(cc, &L, taken);
1003 Abort(msg);
1004 // will not return here
1005 bind(&L);
1006}
1007
1008
1009void MacroAssembler::Abort(const char* msg) {
1010 // We want to pass the msg string like a smi to avoid GC
1011 // problems, however msg is not guaranteed to be aligned
1012 // properly. Instead, we pass an aligned pointer that is
ager@chromium.org32912102009-01-16 10:38:43 +00001013 // a proper v8 smi, but also pass the alignment difference
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001014 // from the real pointer as a smi.
1015 intptr_t p1 = reinterpret_cast<intptr_t>(msg);
1016 intptr_t p0 = (p1 & ~kSmiTagMask) + kSmiTag;
1017 ASSERT(reinterpret_cast<Object*>(p0)->IsSmi());
1018#ifdef DEBUG
1019 if (msg != NULL) {
1020 RecordComment("Abort message: ");
1021 RecordComment(msg);
1022 }
1023#endif
1024 push(eax);
1025 push(Immediate(p0));
1026 push(Immediate(reinterpret_cast<intptr_t>(Smi::FromInt(p1 - p0))));
1027 CallRuntime(Runtime::kAbort, 2);
1028 // will not return here
1029}
1030
1031
1032CodePatcher::CodePatcher(byte* address, int size)
1033 : address_(address), size_(size), masm_(address, size + Assembler::kGap) {
ager@chromium.org32912102009-01-16 10:38:43 +00001034 // Create a new macro assembler pointing to the address of the code to patch.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001035 // The size is adjusted with kGap on order for the assembler to generate size
1036 // bytes of instructions without failing with buffer size constraints.
1037 ASSERT(masm_.reloc_info_writer.pos() == address_ + size_ + Assembler::kGap);
1038}
1039
1040
1041CodePatcher::~CodePatcher() {
1042 // Indicate that code has changed.
1043 CPU::FlushICache(address_, size_);
1044
1045 // Check that the code was patched as expected.
1046 ASSERT(masm_.pc_ == address_ + size_);
1047 ASSERT(masm_.reloc_info_writer.pos() == address_ + size_ + Assembler::kGap);
1048}
1049
1050
1051} } // namespace v8::internal