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