blob: b59735a01cc8ba2e8f5f6555701154d28bbf9580 [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
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000038MacroAssembler::MacroAssembler(void* buffer, int size)
39 : Assembler(buffer, size),
40 unresolved_(0),
kasper.lund7276f142008-07-30 08:49:36 +000041 generating_stub_(false),
42 allow_stub_calls_(true) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000043}
44
45
46static void RecordWriteHelper(MacroAssembler* masm,
47 Register object,
48 Register addr,
49 Register scratch) {
50 Label fast;
51
52 // Compute the page address from the heap object pointer, leave it
53 // in 'object'.
54 masm->and_(object, ~Page::kPageAlignmentMask);
55
56 // Compute the bit addr in the remembered set, leave it in "addr".
57 masm->sub(addr, Operand(object));
58 masm->shr(addr, kObjectAlignmentBits);
59
60 // If the bit offset lies beyond the normal remembered set range, it is in
61 // the extra remembered set area of a large object.
62 masm->cmp(addr, Page::kPageSize / kPointerSize);
63 masm->j(less, &fast);
64
65 // Adjust 'addr' to be relative to the start of the extra remembered set
66 // and the page address in 'object' to be the address of the extra
67 // remembered set.
68 masm->sub(Operand(addr), Immediate(Page::kPageSize / kPointerSize));
69 // Load the array length into 'scratch' and multiply by four to get the
70 // size in bytes of the elements.
71 masm->mov(scratch, Operand(object, Page::kObjectStartOffset
72 + FixedArray::kLengthOffset));
73 masm->shl(scratch, kObjectAlignmentBits);
74 // Add the page header, array header, and array body size to the page
75 // address.
76 masm->add(Operand(object), Immediate(Page::kObjectStartOffset
77 + Array::kHeaderSize));
78 masm->add(object, Operand(scratch));
79
80
81 // NOTE: For now, we use the bit-test-and-set (bts) x86 instruction
82 // to limit code size. We should probably evaluate this decision by
83 // measuring the performance of an equivalent implementation using
84 // "simpler" instructions
85 masm->bind(&fast);
86 masm->bts(Operand(object, 0), addr);
87}
88
89
90class RecordWriteStub : public CodeStub {
91 public:
92 RecordWriteStub(Register object, Register addr, Register scratch)
93 : object_(object), addr_(addr), scratch_(scratch) { }
94
95 void Generate(MacroAssembler* masm);
96
97 private:
98 Register object_;
99 Register addr_;
100 Register scratch_;
101
102 const char* GetName() { return "RecordWriteStub"; }
103
104#ifdef DEBUG
105 void Print() {
106 PrintF("RecordWriteStub (object reg %d), (addr reg %d), (scratch reg %d)\n",
107 object_.code(), addr_.code(), scratch_.code());
108 }
109#endif
110
111 // Minor key encoding in 12 bits of three registers (object, address and
112 // scratch) OOOOAAAASSSS.
113 class ScratchBits: public BitField<uint32_t, 0, 4> {};
114 class AddressBits: public BitField<uint32_t, 4, 4> {};
115 class ObjectBits: public BitField<uint32_t, 8, 4> {
116};
117
118 Major MajorKey() { return RecordWrite; }
119
120 int MinorKey() {
121 // Encode the registers.
122 return ObjectBits::encode(object_.code()) |
123 AddressBits::encode(addr_.code()) |
124 ScratchBits::encode(scratch_.code());
125 }
126};
127
128
129void RecordWriteStub::Generate(MacroAssembler* masm) {
130 RecordWriteHelper(masm, object_, addr_, scratch_);
131 masm->ret(0);
132}
133
134
135// Set the remembered set bit for [object+offset].
136// object is the object being stored into, value is the object being stored.
137// If offset is zero, then the scratch register contains the array index into
138// the elements array represented as a Smi.
139// All registers are clobbered by the operation.
140void MacroAssembler::RecordWrite(Register object, int offset,
141 Register value, Register scratch) {
142 // First, check if a remembered set write is even needed. The tests below
143 // catch stores of Smis and stores into young gen (which does not have space
144 // for the remembered set bits.
145 Label done;
146
147 // This optimization cannot survive serialization and deserialization,
148 // so we disable as long as serialization can take place.
149 int32_t new_space_start =
150 reinterpret_cast<int32_t>(ExternalReference::new_space_start().address());
151 if (Serializer::enabled() || new_space_start < 0) {
152 // Cannot do smart bit-twiddling. Need to do two consecutive checks.
153 // Check for Smi first.
154 test(value, Immediate(kSmiTagMask));
155 j(zero, &done);
156 // Test that the object address is not in the new space. We cannot
157 // set remembered set bits in the new space.
158 mov(value, Operand(object));
159 and_(value, Heap::NewSpaceMask());
160 cmp(Operand(value), Immediate(ExternalReference::new_space_start()));
161 j(equal, &done);
162 } else {
163 // move the value SmiTag into the sign bit
164 shl(value, 31);
165 // combine the object with value SmiTag
166 or_(value, Operand(object));
167 // remove the uninteresing bits inside the page
168 and_(value, Heap::NewSpaceMask() | (1 << 31));
169 // xor has two effects:
170 // - if the value was a smi, then the result will be negative
171 // - if the object is pointing into new space area the page bits will
172 // all be zero
173 xor_(value, new_space_start | (1 << 31));
174 // Check for both conditions in one branch
175 j(less_equal, &done);
176 }
177
178 if ((offset > 0) && (offset < Page::kMaxHeapObjectSize)) {
179 // Compute the bit offset in the remembered set, leave it in 'value'.
180 mov(value, Operand(object));
181 and_(value, Page::kPageAlignmentMask);
182 add(Operand(value), Immediate(offset));
183 shr(value, kObjectAlignmentBits);
184
185 // Compute the page address from the heap object pointer, leave it in
186 // 'object'.
187 and_(object, ~Page::kPageAlignmentMask);
188
189 // NOTE: For now, we use the bit-test-and-set (bts) x86 instruction
190 // to limit code size. We should probably evaluate this decision by
191 // measuring the performance of an equivalent implementation using
192 // "simpler" instructions
193 bts(Operand(object, 0), value);
194 } else {
195 Register dst = scratch;
196 if (offset != 0) {
197 lea(dst, Operand(object, offset));
198 } else {
199 // array access: calculate the destination address in the same manner as
200 // KeyedStoreIC::GenerateGeneric
201 lea(dst,
202 Operand(object, dst, times_2, Array::kHeaderSize - kHeapObjectTag));
203 }
204 // If we are already generating a shared stub, not inlining the
205 // record write code isn't going to save us any memory.
206 if (generating_stub()) {
207 RecordWriteHelper(this, object, dst, value);
208 } else {
209 RecordWriteStub stub(object, dst, value);
210 CallStub(&stub);
211 }
212 }
213
214 bind(&done);
215}
216
217
218void MacroAssembler::SaveRegistersToMemory(RegList regs) {
219 ASSERT((regs & ~kJSCallerSaved) == 0);
220 // Copy the content of registers to memory location.
221 for (int i = 0; i < kNumJSCallerSaved; i++) {
222 int r = JSCallerSavedCode(i);
223 if ((regs & (1 << r)) != 0) {
224 Register reg = { r };
225 ExternalReference reg_addr =
226 ExternalReference(Debug_Address::Register(i));
227 mov(Operand::StaticVariable(reg_addr), reg);
228 }
229 }
230}
231
232
233void MacroAssembler::RestoreRegistersFromMemory(RegList regs) {
234 ASSERT((regs & ~kJSCallerSaved) == 0);
235 // Copy the content of memory location to registers.
236 for (int i = kNumJSCallerSaved; --i >= 0;) {
237 int r = JSCallerSavedCode(i);
238 if ((regs & (1 << r)) != 0) {
239 Register reg = { r };
240 ExternalReference reg_addr =
241 ExternalReference(Debug_Address::Register(i));
242 mov(reg, Operand::StaticVariable(reg_addr));
243 }
244 }
245}
246
247
248void MacroAssembler::PushRegistersFromMemory(RegList regs) {
249 ASSERT((regs & ~kJSCallerSaved) == 0);
250 // Push the content of the memory location to the stack.
251 for (int i = 0; i < kNumJSCallerSaved; i++) {
252 int r = JSCallerSavedCode(i);
253 if ((regs & (1 << r)) != 0) {
254 ExternalReference reg_addr =
255 ExternalReference(Debug_Address::Register(i));
256 push(Operand::StaticVariable(reg_addr));
257 }
258 }
259}
260
261
262void MacroAssembler::PopRegistersToMemory(RegList regs) {
263 ASSERT((regs & ~kJSCallerSaved) == 0);
264 // Pop the content from the stack to the memory location.
265 for (int i = kNumJSCallerSaved; --i >= 0;) {
266 int r = JSCallerSavedCode(i);
267 if ((regs & (1 << r)) != 0) {
268 ExternalReference reg_addr =
269 ExternalReference(Debug_Address::Register(i));
270 pop(Operand::StaticVariable(reg_addr));
271 }
272 }
273}
274
275
276void MacroAssembler::CopyRegistersFromStackToMemory(Register base,
277 Register scratch,
278 RegList regs) {
279 ASSERT((regs & ~kJSCallerSaved) == 0);
280 // Copy the content of the stack to the memory location and adjust base.
281 for (int i = kNumJSCallerSaved; --i >= 0;) {
282 int r = JSCallerSavedCode(i);
283 if ((regs & (1 << r)) != 0) {
284 mov(scratch, Operand(base, 0));
285 ExternalReference reg_addr =
286 ExternalReference(Debug_Address::Register(i));
287 mov(Operand::StaticVariable(reg_addr), scratch);
288 lea(base, Operand(base, kPointerSize));
289 }
290 }
291}
292
293
294void MacroAssembler::Set(Register dst, const Immediate& x) {
295 if (x.is_zero()) {
296 xor_(dst, Operand(dst)); // shorter than mov
297 } else {
298 mov(Operand(dst), x);
299 }
300}
301
302
303void MacroAssembler::Set(const Operand& dst, const Immediate& x) {
304 mov(dst, x);
305}
306
307
308void MacroAssembler::FCmp() {
309 fcompp();
310 push(eax);
311 fnstsw_ax();
312 sahf();
313 pop(eax);
314}
315
316
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000317void MacroAssembler::EnterInternalFrame() {
318 int type = StackFrame::INTERNAL;
319
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000320 push(ebp);
321 mov(ebp, Operand(esp));
322 push(esi);
323 push(Immediate(Smi::FromInt(type)));
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000324 push(Immediate(0)); // Push an empty code cache slot.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000325}
326
327
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000328void MacroAssembler::ExitInternalFrame() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000329 if (FLAG_debug_code) {
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000330 StackFrame::Type type = StackFrame::INTERNAL;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000331 cmp(Operand(ebp, StandardFrameConstants::kMarkerOffset),
332 Immediate(Smi::FromInt(type)));
333 Check(equal, "stack frame types must match");
334 }
335 leave();
336}
337
338
339void MacroAssembler::PushTryHandler(CodeLocation try_location,
340 HandlerType type) {
341 ASSERT(StackHandlerConstants::kSize == 6 * kPointerSize); // adjust this code
342 // The pc (return address) is already on TOS.
343 if (try_location == IN_JAVASCRIPT) {
344 if (type == TRY_CATCH_HANDLER) {
345 push(Immediate(StackHandler::TRY_CATCH));
346 } else {
347 push(Immediate(StackHandler::TRY_FINALLY));
348 }
349 push(Immediate(Smi::FromInt(StackHandler::kCodeNotPresent)));
350 push(ebp);
351 push(edi);
352 } else {
353 ASSERT(try_location == IN_JS_ENTRY);
354 // The parameter pointer is meaningless here and ebp does not
355 // point to a JS frame. So we save NULL for both pp and ebp. We
356 // expect the code throwing an exception to check ebp before
357 // dereferencing it to restore the context.
358 push(Immediate(StackHandler::ENTRY));
359 push(Immediate(Smi::FromInt(StackHandler::kCodeNotPresent)));
360 push(Immediate(0)); // NULL frame pointer
361 push(Immediate(0)); // NULL parameter pointer
362 }
363 // Cached TOS.
364 mov(eax, Operand::StaticVariable(ExternalReference(Top::k_handler_address)));
365 // Link this handler.
366 mov(Operand::StaticVariable(ExternalReference(Top::k_handler_address)), esp);
367}
368
369
370Register MacroAssembler::CheckMaps(JSObject* object, Register object_reg,
371 JSObject* holder, Register holder_reg,
372 Register scratch,
373 Label* miss) {
374 // Make sure there's no overlap between scratch and the other
375 // registers.
376 ASSERT(!scratch.is(object_reg) && !scratch.is(holder_reg));
377
378 // Keep track of the current object in register reg.
379 Register reg = object_reg;
380 int depth = 1;
381
382 // Check the maps in the prototype chain.
383 // Traverse the prototype chain from the object and do map checks.
384 while (object != holder) {
385 depth++;
386
387 // Only global objects and objects that do not require access
388 // checks are allowed in stubs.
389 ASSERT(object->IsJSGlobalObject() || !object->IsAccessCheckNeeded());
390
391 JSObject* prototype = JSObject::cast(object->GetPrototype());
392 if (Heap::InNewSpace(prototype)) {
393 // Get the map of the current object.
394 mov(scratch, FieldOperand(reg, HeapObject::kMapOffset));
395 cmp(Operand(scratch), Immediate(Handle<Map>(object->map())));
396 // Branch on the result of the map check.
397 j(not_equal, miss, not_taken);
398 // Check access rights to the global object. This has to happen
399 // after the map check so that we know that the object is
400 // actually a global object.
401 if (object->IsJSGlobalObject()) {
402 CheckAccessGlobal(reg, scratch, miss);
403 // Restore scratch register to be the map of the object. We
404 // load the prototype from the map in the scratch register.
405 mov(scratch, FieldOperand(reg, HeapObject::kMapOffset));
406 }
407 // The prototype is in new space; we cannot store a reference
408 // to it in the code. Load it from the map.
409 reg = holder_reg; // from now the object is in holder_reg
410 mov(reg, FieldOperand(scratch, Map::kPrototypeOffset));
411 } else {
412 // Check the map of the current object.
413 cmp(FieldOperand(reg, HeapObject::kMapOffset),
414 Immediate(Handle<Map>(object->map())));
415 // Branch on the result of the map check.
416 j(not_equal, miss, not_taken);
417 // Check access rights to the global object. This has to happen
418 // after the map check so that we know that the object is
419 // actually a global object.
420 if (object->IsJSGlobalObject()) {
421 CheckAccessGlobal(reg, scratch, miss);
422 }
423 // The prototype is in old space; load it directly.
424 reg = holder_reg; // from now the object is in holder_reg
425 mov(reg, Handle<JSObject>(prototype));
426 }
427
428 // Go to the next object in the prototype chain.
429 object = prototype;
430 }
431
432 // Check the holder map.
433 cmp(FieldOperand(reg, HeapObject::kMapOffset),
434 Immediate(Handle<Map>(holder->map())));
435 j(not_equal, miss, not_taken);
436
437 // Log the check depth.
438 LOG(IntEvent("check-maps-depth", depth));
439
440 // Perform security check for access to the global object and return
441 // the holder register.
442 ASSERT(object == holder);
443 ASSERT(object->IsJSGlobalObject() || !object->IsAccessCheckNeeded());
444 if (object->IsJSGlobalObject()) {
445 CheckAccessGlobal(reg, scratch, miss);
446 }
447 return reg;
448}
449
450
451void MacroAssembler::CheckAccessGlobal(Register holder_reg,
452 Register scratch,
453 Label* miss) {
454 ASSERT(!holder_reg.is(scratch));
455
456 // Load the security context.
457 ExternalReference security_context =
458 ExternalReference(Top::k_security_context_address);
459 mov(scratch, Operand::StaticVariable(security_context));
460 // When generating debug code, make sure the security context is set.
461 if (FLAG_debug_code) {
462 cmp(Operand(scratch), Immediate(0));
463 Check(not_equal, "we should not have an empty security context");
464 }
465 // Load the global object of the security context.
466 int offset = Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize;
467 mov(scratch, FieldOperand(scratch, offset));
468 // Check that the security token in the calling global object is
469 // compatible with the security token in the receiving global
470 // object.
471 mov(scratch, FieldOperand(scratch, JSGlobalObject::kSecurityTokenOffset));
472 cmp(scratch, FieldOperand(holder_reg, JSGlobalObject::kSecurityTokenOffset));
473 j(not_equal, miss, not_taken);
474}
475
476
477void MacroAssembler::NegativeZeroTest(Register result,
478 Register op,
479 Label* then_label) {
480 Label ok;
481 test(result, Operand(result));
482 j(not_zero, &ok, taken);
483 test(op, Operand(op));
484 j(sign, then_label, not_taken);
485 bind(&ok);
486}
487
488
489void MacroAssembler::NegativeZeroTest(Register result,
490 Register op1,
491 Register op2,
492 Register scratch,
493 Label* then_label) {
494 Label ok;
495 test(result, Operand(result));
496 j(not_zero, &ok, taken);
497 mov(scratch, Operand(op1));
498 or_(scratch, Operand(op2));
499 j(sign, then_label, not_taken);
500 bind(&ok);
501}
502
503
504void MacroAssembler::CallStub(CodeStub* stub) {
kasper.lund7276f142008-07-30 08:49:36 +0000505 ASSERT(allow_stub_calls()); // calls are not allowed in some stubs
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000506 call(stub->GetCode(), code_target);
507}
508
509
510void MacroAssembler::StubReturn(int argc) {
511 ASSERT(argc >= 1 && generating_stub());
512 ret((argc - 1) * kPointerSize);
513}
514
515
516void MacroAssembler::IllegalOperation() {
517 push(Immediate(Factory::undefined_value()));
518}
519
520
521void MacroAssembler::CallRuntime(Runtime::FunctionId id, int num_arguments) {
522 CallRuntime(Runtime::FunctionForId(id), num_arguments);
523}
524
525
526void MacroAssembler::CallRuntime(Runtime::Function* f, int num_arguments) {
mads.s.ager31e71382008-08-13 09:32:07 +0000527 // If the expected number of arguments of the runtime function is
528 // constant, we check that the actual number of arguments match the
529 // expectation.
530 if (f->nargs >= 0 && f->nargs != num_arguments) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000531 IllegalOperation();
532 return;
533 }
534
mads.s.ager31e71382008-08-13 09:32:07 +0000535 Runtime::FunctionId function_id =
536 static_cast<Runtime::FunctionId>(f->stub_id);
537 RuntimeStub stub(function_id, num_arguments);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000538 CallStub(&stub);
539}
540
541
mads.s.ager31e71382008-08-13 09:32:07 +0000542void MacroAssembler::TailCallRuntime(const ExternalReference& ext,
543 int num_arguments) {
544 // TODO(1236192): Most runtime routines don't need the number of
545 // arguments passed in because it is constant. At some point we
546 // should remove this need and make the runtime routine entry code
547 // smarter.
548 mov(Operand(eax), Immediate(num_arguments));
549 JumpToBuiltin(ext);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000550}
551
552
553void MacroAssembler::JumpToBuiltin(const ExternalReference& ext) {
554 // Set the entry point and jump to the C entry runtime stub.
555 mov(Operand(ebx), Immediate(ext));
556 CEntryStub ces;
557 jmp(ces.GetCode(), code_target);
558}
559
560
561void MacroAssembler::InvokePrologue(const ParameterCount& expected,
562 const ParameterCount& actual,
563 Handle<Code> code_constant,
564 const Operand& code_operand,
565 Label* done,
566 InvokeFlag flag) {
567 bool definitely_matches = false;
568 Label invoke;
569 if (expected.is_immediate()) {
570 ASSERT(actual.is_immediate());
571 if (expected.immediate() == actual.immediate()) {
572 definitely_matches = true;
573 } else {
574 mov(eax, actual.immediate());
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000575 const int sentinel = SharedFunctionInfo::kDontAdaptArgumentsSentinel;
576 if (expected.immediate() == sentinel) {
577 // Don't worry about adapting arguments for builtins that
578 // don't want that done. Skip adaption code by making it look
579 // like we have a match between expected and actual number of
580 // arguments.
581 definitely_matches = true;
582 } else {
583 mov(ebx, expected.immediate());
584 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000585 }
586 } else {
587 if (actual.is_immediate()) {
588 // Expected is in register, actual is immediate. This is the
589 // case when we invoke function values without going through the
590 // IC mechanism.
591 cmp(expected.reg(), actual.immediate());
592 j(equal, &invoke);
593 ASSERT(expected.reg().is(ebx));
594 mov(eax, actual.immediate());
595 } else if (!expected.reg().is(actual.reg())) {
596 // Both expected and actual are in (different) registers. This
597 // is the case when we invoke functions using call and apply.
598 cmp(expected.reg(), Operand(actual.reg()));
599 j(equal, &invoke);
600 ASSERT(actual.reg().is(eax));
601 ASSERT(expected.reg().is(ebx));
602 }
603 }
604
605 if (!definitely_matches) {
606 Handle<Code> adaptor =
607 Handle<Code>(Builtins::builtin(Builtins::ArgumentsAdaptorTrampoline));
608 if (!code_constant.is_null()) {
609 mov(Operand(edx), Immediate(code_constant));
610 add(Operand(edx), Immediate(Code::kHeaderSize - kHeapObjectTag));
611 } else if (!code_operand.is_reg(edx)) {
612 mov(edx, code_operand);
613 }
614
615 if (flag == CALL_FUNCTION) {
616 call(adaptor, code_target);
617 jmp(done);
618 } else {
619 jmp(adaptor, code_target);
620 }
621 bind(&invoke);
622 }
623}
624
625
626void MacroAssembler::InvokeCode(const Operand& code,
627 const ParameterCount& expected,
628 const ParameterCount& actual,
629 InvokeFlag flag) {
630 Label done;
631 InvokePrologue(expected, actual, Handle<Code>::null(), code, &done, flag);
632 if (flag == CALL_FUNCTION) {
633 call(code);
634 } else {
635 ASSERT(flag == JUMP_FUNCTION);
636 jmp(code);
637 }
638 bind(&done);
639}
640
641
642void MacroAssembler::InvokeCode(Handle<Code> code,
643 const ParameterCount& expected,
644 const ParameterCount& actual,
645 RelocMode rmode,
646 InvokeFlag flag) {
647 Label done;
648 Operand dummy(eax);
649 InvokePrologue(expected, actual, code, dummy, &done, flag);
650 if (flag == CALL_FUNCTION) {
651 call(code, rmode);
652 } else {
653 ASSERT(flag == JUMP_FUNCTION);
654 jmp(code, rmode);
655 }
656 bind(&done);
657}
658
659
660void MacroAssembler::InvokeFunction(Register fun,
661 const ParameterCount& actual,
662 InvokeFlag flag) {
663 ASSERT(fun.is(edi));
664 mov(edx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
665 mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
666 mov(ebx, FieldOperand(edx, SharedFunctionInfo::kFormalParameterCountOffset));
667 mov(edx, FieldOperand(edx, SharedFunctionInfo::kCodeOffset));
668 lea(edx, FieldOperand(edx, Code::kHeaderSize));
669
670 ParameterCount expected(ebx);
671 InvokeCode(Operand(edx), expected, actual, flag);
672}
673
674
675void MacroAssembler::InvokeBuiltin(Builtins::JavaScript id, InvokeFlag flag) {
676 bool resolved;
677 Handle<Code> code = ResolveBuiltin(id, &resolved);
678
kasper.lund7276f142008-07-30 08:49:36 +0000679 // Calls are not allowed in some stubs.
680 ASSERT(flag == JUMP_FUNCTION || allow_stub_calls());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000681
682 // Rely on the assertion to check that the number of provided
683 // arguments match the expected number of arguments. Fake a
684 // parameter count to avoid emitting code to do the check.
685 ParameterCount expected(0);
686 InvokeCode(Handle<Code>(code), expected, expected, code_target, flag);
687
688 const char* name = Builtins::GetName(id);
689 int argc = Builtins::GetArgumentsCount(id);
690
691 if (!resolved) {
692 uint32_t flags =
693 Bootstrapper::FixupFlagsArgumentsCount::encode(argc) |
694 Bootstrapper::FixupFlagsIsPCRelative::encode(true);
695 Unresolved entry = { pc_offset() - sizeof(int32_t), flags, name };
696 unresolved_.Add(entry);
697 }
698}
699
700
701void MacroAssembler::GetBuiltinEntry(Register target, Builtins::JavaScript id) {
702 bool resolved;
703 Handle<Code> code = ResolveBuiltin(id, &resolved);
704
705 const char* name = Builtins::GetName(id);
706 int argc = Builtins::GetArgumentsCount(id);
707
708 mov(Operand(target), Immediate(code));
709 if (!resolved) {
710 uint32_t flags =
711 Bootstrapper::FixupFlagsArgumentsCount::encode(argc) |
712 Bootstrapper::FixupFlagsIsPCRelative::encode(false);
713 Unresolved entry = { pc_offset() - sizeof(int32_t), flags, name };
714 unresolved_.Add(entry);
715 }
716 add(Operand(target), Immediate(Code::kHeaderSize - kHeapObjectTag));
717}
718
719
720Handle<Code> MacroAssembler::ResolveBuiltin(Builtins::JavaScript id,
721 bool* resolved) {
722 // Move the builtin function into the temporary function slot by
723 // reading it from the builtins object. NOTE: We should be able to
724 // reduce this to two instructions by putting the function table in
725 // the global object instead of the "builtins" object and by using a
726 // real register for the function.
727 mov(edx, Operand(esi, Context::SlotOffset(Context::GLOBAL_INDEX)));
728 mov(edx, FieldOperand(edx, GlobalObject::kBuiltinsOffset));
729 int builtins_offset =
730 JSBuiltinsObject::kJSBuiltinsOffset + (id * kPointerSize);
731 mov(edi, FieldOperand(edx, builtins_offset));
732
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000733
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000734 return Builtins::GetCode(id, resolved);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000735}
736
737
738void MacroAssembler::Ret() {
739 ret(0);
740}
741
742
743void MacroAssembler::SetCounter(StatsCounter* counter, int value) {
744 if (FLAG_native_code_counters && counter->Enabled()) {
745 mov(Operand::StaticVariable(ExternalReference(counter)), Immediate(value));
746 }
747}
748
749
750void MacroAssembler::IncrementCounter(StatsCounter* counter, int value) {
751 ASSERT(value > 0);
752 if (FLAG_native_code_counters && counter->Enabled()) {
753 Operand operand = Operand::StaticVariable(ExternalReference(counter));
754 if (value == 1) {
755 inc(operand);
756 } else {
757 add(operand, Immediate(value));
758 }
759 }
760}
761
762
763void MacroAssembler::DecrementCounter(StatsCounter* counter, int value) {
764 ASSERT(value > 0);
765 if (FLAG_native_code_counters && counter->Enabled()) {
766 Operand operand = Operand::StaticVariable(ExternalReference(counter));
767 if (value == 1) {
768 dec(operand);
769 } else {
770 sub(operand, Immediate(value));
771 }
772 }
773}
774
775
776void MacroAssembler::Assert(Condition cc, const char* msg) {
777 if (FLAG_debug_code) Check(cc, msg);
778}
779
780
781void MacroAssembler::Check(Condition cc, const char* msg) {
782 Label L;
783 j(cc, &L, taken);
784 Abort(msg);
785 // will not return here
786 bind(&L);
787}
788
789
790void MacroAssembler::Abort(const char* msg) {
791 // We want to pass the msg string like a smi to avoid GC
792 // problems, however msg is not guaranteed to be aligned
793 // properly. Instead, we pass an aligned pointer that is
794 // a proper v8 smi, but also pass the aligment difference
795 // from the real pointer as a smi.
796 intptr_t p1 = reinterpret_cast<intptr_t>(msg);
797 intptr_t p0 = (p1 & ~kSmiTagMask) + kSmiTag;
798 ASSERT(reinterpret_cast<Object*>(p0)->IsSmi());
799#ifdef DEBUG
800 if (msg != NULL) {
801 RecordComment("Abort message: ");
802 RecordComment(msg);
803 }
804#endif
805 push(eax);
806 push(Immediate(p0));
807 push(Immediate(reinterpret_cast<intptr_t>(Smi::FromInt(p1 - p0))));
808 CallRuntime(Runtime::kAbort, 2);
809 // will not return here
810}
811
812
813CodePatcher::CodePatcher(byte* address, int size)
814 : address_(address), size_(size), masm_(address, size + Assembler::kGap) {
815 // Create a new macro assembler pointing to the assress of the code to patch.
816 // The size is adjusted with kGap on order for the assembler to generate size
817 // bytes of instructions without failing with buffer size constraints.
818 ASSERT(masm_.reloc_info_writer.pos() == address_ + size_ + Assembler::kGap);
819}
820
821
822CodePatcher::~CodePatcher() {
823 // Indicate that code has changed.
824 CPU::FlushICache(address_, size_);
825
826 // Check that the code was patched as expected.
827 ASSERT(masm_.pc_ == address_ + size_);
828 ASSERT(masm_.reloc_info_writer.pos() == address_ + size_ + Assembler::kGap);
829}
830
831
832} } // namespace v8::internal