1// Copyright (c) 1994-2006 Sun Microsystems Inc.
2// All Rights Reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are
6// met:
7//
8// - Redistributions of source code must retain the above copyright notice,
9// this list of conditions and the following disclaimer.
10//
11// - Redistribution in binary form must reproduce the above copyright
12// notice, this list of conditions and the following disclaimer in the
13// documentation and/or other materials provided with the distribution.
14//
15// - Neither the name of Sun Microsystems or the names of contributors may
16// be used to endorse or promote products derived from this software without
17// specific prior written permission.
18//
19// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
20// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
23// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31// The original source code covered by the above license above has been
32// modified significantly by Google Inc.
33// Copyright 2012 the V8 project authors. All rights reserved.
34
35// A light-weight IA32 Assembler.
36
37#ifndef V8_IA32_ASSEMBLER_IA32_INL_H_
38#define V8_IA32_ASSEMBLER_IA32_INL_H_
39
40#include "src/ia32/assembler-ia32.h"
41
42#include "src/assembler.h"
43#include "src/debug/debug.h"
44#include "src/objects-inl.h"
45
46namespace v8 {
47namespace internal {
48
49bool CpuFeatures::SupportsCrankshaft() { return true; }
50
51bool CpuFeatures::SupportsSimd128() { return false; }
52
53static const byte kCallOpcode = 0xE8;
54static const int kNoCodeAgeSequenceLength = 5;
55
56
57// The modes possibly affected by apply must be in kApplyMask.
58void RelocInfo::apply(intptr_t delta) {
59  if (IsRuntimeEntry(rmode_) || IsCodeTarget(rmode_)) {
60    int32_t* p = reinterpret_cast<int32_t*>(pc_);
61    *p -= delta;  // Relocate entry.
62  } else if (IsCodeAgeSequence(rmode_)) {
63    if (*pc_ == kCallOpcode) {
64      int32_t* p = reinterpret_cast<int32_t*>(pc_ + 1);
65      *p -= delta;  // Relocate entry.
66    }
67  } else if (IsDebugBreakSlot(rmode_) && IsPatchedDebugBreakSlotSequence()) {
68    // Special handling of a debug break slot when a break point is set (call
69    // instruction has been inserted).
70    int32_t* p = reinterpret_cast<int32_t*>(
71        pc_ + Assembler::kPatchDebugBreakSlotAddressOffset);
72    *p -= delta;  // Relocate entry.
73  } else if (IsInternalReference(rmode_)) {
74    // absolute code pointer inside code object moves with the code object.
75    int32_t* p = reinterpret_cast<int32_t*>(pc_);
76    *p += delta;  // Relocate entry.
77  }
78}
79
80
81Address RelocInfo::target_address() {
82  DCHECK(IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_));
83  return Assembler::target_address_at(pc_, host_);
84}
85
86Address RelocInfo::target_address_address() {
87  DCHECK(IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_)
88                              || rmode_ == EMBEDDED_OBJECT
89                              || rmode_ == EXTERNAL_REFERENCE);
90  return reinterpret_cast<Address>(pc_);
91}
92
93
94Address RelocInfo::constant_pool_entry_address() {
95  UNREACHABLE();
96  return NULL;
97}
98
99
100int RelocInfo::target_address_size() {
101  return Assembler::kSpecialTargetSize;
102}
103
104
105Object* RelocInfo::target_object() {
106  DCHECK(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT);
107  return Memory::Object_at(pc_);
108}
109
110
111Handle<Object> RelocInfo::target_object_handle(Assembler* origin) {
112  DCHECK(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT);
113  return Memory::Object_Handle_at(pc_);
114}
115
116
117void RelocInfo::set_target_object(Object* target,
118                                  WriteBarrierMode write_barrier_mode,
119                                  ICacheFlushMode icache_flush_mode) {
120  DCHECK(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT);
121  Memory::Object_at(pc_) = target;
122  if (icache_flush_mode != SKIP_ICACHE_FLUSH) {
123    Assembler::FlushICache(isolate_, pc_, sizeof(Address));
124  }
125  if (write_barrier_mode == UPDATE_WRITE_BARRIER &&
126      host() != NULL &&
127      target->IsHeapObject()) {
128    host()->GetHeap()->RecordWriteIntoCode(host(), this, target);
129    host()->GetHeap()->incremental_marking()->RecordWriteIntoCode(
130        host(), this, HeapObject::cast(target));
131  }
132}
133
134
135Address RelocInfo::target_external_reference() {
136  DCHECK(rmode_ == RelocInfo::EXTERNAL_REFERENCE);
137  return Memory::Address_at(pc_);
138}
139
140
141Address RelocInfo::target_internal_reference() {
142  DCHECK(rmode_ == INTERNAL_REFERENCE);
143  return Memory::Address_at(pc_);
144}
145
146
147Address RelocInfo::target_internal_reference_address() {
148  DCHECK(rmode_ == INTERNAL_REFERENCE);
149  return reinterpret_cast<Address>(pc_);
150}
151
152
153Address RelocInfo::target_runtime_entry(Assembler* origin) {
154  DCHECK(IsRuntimeEntry(rmode_));
155  return reinterpret_cast<Address>(*reinterpret_cast<int32_t*>(pc_));
156}
157
158
159void RelocInfo::set_target_runtime_entry(Address target,
160                                         WriteBarrierMode write_barrier_mode,
161                                         ICacheFlushMode icache_flush_mode) {
162  DCHECK(IsRuntimeEntry(rmode_));
163  if (target_address() != target) {
164    set_target_address(target, write_barrier_mode, icache_flush_mode);
165  }
166}
167
168
169Handle<Cell> RelocInfo::target_cell_handle() {
170  DCHECK(rmode_ == RelocInfo::CELL);
171  Address address = Memory::Address_at(pc_);
172  return Handle<Cell>(reinterpret_cast<Cell**>(address));
173}
174
175
176Cell* RelocInfo::target_cell() {
177  DCHECK(rmode_ == RelocInfo::CELL);
178  return Cell::FromValueAddress(Memory::Address_at(pc_));
179}
180
181
182void RelocInfo::set_target_cell(Cell* cell,
183                                WriteBarrierMode write_barrier_mode,
184                                ICacheFlushMode icache_flush_mode) {
185  DCHECK(cell->IsCell());
186  DCHECK(rmode_ == RelocInfo::CELL);
187  Address address = cell->address() + Cell::kValueOffset;
188  Memory::Address_at(pc_) = address;
189  if (icache_flush_mode != SKIP_ICACHE_FLUSH) {
190    Assembler::FlushICache(isolate_, pc_, sizeof(Address));
191  }
192  if (write_barrier_mode == UPDATE_WRITE_BARRIER && host() != NULL) {
193    host()->GetHeap()->incremental_marking()->RecordWriteIntoCode(host(), this,
194                                                                  cell);
195  }
196}
197
198
199Handle<Object> RelocInfo::code_age_stub_handle(Assembler* origin) {
200  DCHECK(rmode_ == RelocInfo::CODE_AGE_SEQUENCE);
201  DCHECK(*pc_ == kCallOpcode);
202  return Memory::Object_Handle_at(pc_ + 1);
203}
204
205
206Code* RelocInfo::code_age_stub() {
207  DCHECK(rmode_ == RelocInfo::CODE_AGE_SEQUENCE);
208  DCHECK(*pc_ == kCallOpcode);
209  return Code::GetCodeFromTargetAddress(
210      Assembler::target_address_at(pc_ + 1, host_));
211}
212
213
214void RelocInfo::set_code_age_stub(Code* stub,
215                                  ICacheFlushMode icache_flush_mode) {
216  DCHECK(*pc_ == kCallOpcode);
217  DCHECK(rmode_ == RelocInfo::CODE_AGE_SEQUENCE);
218  Assembler::set_target_address_at(
219      isolate_, pc_ + 1, host_, stub->instruction_start(), icache_flush_mode);
220}
221
222
223Address RelocInfo::debug_call_address() {
224  DCHECK(IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence());
225  Address location = pc_ + Assembler::kPatchDebugBreakSlotAddressOffset;
226  return Assembler::target_address_at(location, host_);
227}
228
229
230void RelocInfo::set_debug_call_address(Address target) {
231  DCHECK(IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence());
232  Address location = pc_ + Assembler::kPatchDebugBreakSlotAddressOffset;
233  Assembler::set_target_address_at(isolate_, location, host_, target);
234  if (host() != NULL) {
235    Object* target_code = Code::GetCodeFromTargetAddress(target);
236    host()->GetHeap()->incremental_marking()->RecordWriteIntoCode(
237        host(), this, HeapObject::cast(target_code));
238  }
239}
240
241
242void RelocInfo::WipeOut() {
243  if (IsEmbeddedObject(rmode_) || IsExternalReference(rmode_) ||
244      IsInternalReference(rmode_)) {
245    Memory::Address_at(pc_) = NULL;
246  } else if (IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_)) {
247    // Effectively write zero into the relocation.
248    Assembler::set_target_address_at(isolate_, pc_, host_,
249                                     pc_ + sizeof(int32_t));
250  } else {
251    UNREACHABLE();
252  }
253}
254
255template <typename ObjectVisitor>
256void RelocInfo::Visit(Isolate* isolate, ObjectVisitor* visitor) {
257  RelocInfo::Mode mode = rmode();
258  if (mode == RelocInfo::EMBEDDED_OBJECT) {
259    visitor->VisitEmbeddedPointer(this);
260    Assembler::FlushICache(isolate, pc_, sizeof(Address));
261  } else if (RelocInfo::IsCodeTarget(mode)) {
262    visitor->VisitCodeTarget(this);
263  } else if (mode == RelocInfo::CELL) {
264    visitor->VisitCell(this);
265  } else if (mode == RelocInfo::EXTERNAL_REFERENCE) {
266    visitor->VisitExternalReference(this);
267  } else if (mode == RelocInfo::INTERNAL_REFERENCE) {
268    visitor->VisitInternalReference(this);
269  } else if (RelocInfo::IsCodeAgeSequence(mode)) {
270    visitor->VisitCodeAgeSequence(this);
271  } else if (RelocInfo::IsDebugBreakSlot(mode) &&
272             IsPatchedDebugBreakSlotSequence()) {
273    visitor->VisitDebugTarget(this);
274  } else if (IsRuntimeEntry(mode)) {
275    visitor->VisitRuntimeEntry(this);
276  }
277}
278
279
280template<typename StaticVisitor>
281void RelocInfo::Visit(Heap* heap) {
282  RelocInfo::Mode mode = rmode();
283  if (mode == RelocInfo::EMBEDDED_OBJECT) {
284    StaticVisitor::VisitEmbeddedPointer(heap, this);
285    Assembler::FlushICache(heap->isolate(), pc_, sizeof(Address));
286  } else if (RelocInfo::IsCodeTarget(mode)) {
287    StaticVisitor::VisitCodeTarget(heap, this);
288  } else if (mode == RelocInfo::CELL) {
289    StaticVisitor::VisitCell(heap, this);
290  } else if (mode == RelocInfo::EXTERNAL_REFERENCE) {
291    StaticVisitor::VisitExternalReference(this);
292  } else if (mode == RelocInfo::INTERNAL_REFERENCE) {
293    StaticVisitor::VisitInternalReference(this);
294  } else if (RelocInfo::IsCodeAgeSequence(mode)) {
295    StaticVisitor::VisitCodeAgeSequence(heap, this);
296  } else if (RelocInfo::IsDebugBreakSlot(mode) &&
297             IsPatchedDebugBreakSlotSequence()) {
298    StaticVisitor::VisitDebugTarget(heap, this);
299  } else if (IsRuntimeEntry(mode)) {
300    StaticVisitor::VisitRuntimeEntry(this);
301  }
302}
303
304
305
306Immediate::Immediate(int x)  {
307  x_ = x;
308  rmode_ = RelocInfo::NONE32;
309}
310
311Immediate::Immediate(Address x, RelocInfo::Mode rmode) {
312  x_ = reinterpret_cast<int32_t>(x);
313  rmode_ = rmode;
314}
315
316Immediate::Immediate(const ExternalReference& ext) {
317  x_ = reinterpret_cast<int32_t>(ext.address());
318  rmode_ = RelocInfo::EXTERNAL_REFERENCE;
319}
320
321
322Immediate::Immediate(Label* internal_offset) {
323  x_ = reinterpret_cast<int32_t>(internal_offset);
324  rmode_ = RelocInfo::INTERNAL_REFERENCE;
325}
326
327
328Immediate::Immediate(Handle<Object> handle) {
329  AllowDeferredHandleDereference using_raw_address;
330  // Verify all Objects referred by code are NOT in new space.
331  Object* obj = *handle;
332  if (obj->IsHeapObject()) {
333    x_ = reinterpret_cast<intptr_t>(handle.location());
334    rmode_ = RelocInfo::EMBEDDED_OBJECT;
335  } else {
336    // no relocation needed
337    x_ =  reinterpret_cast<intptr_t>(obj);
338    rmode_ = RelocInfo::NONE32;
339  }
340}
341
342
343Immediate::Immediate(Smi* value) {
344  x_ = reinterpret_cast<intptr_t>(value);
345  rmode_ = RelocInfo::NONE32;
346}
347
348
349Immediate::Immediate(Address addr) {
350  x_ = reinterpret_cast<int32_t>(addr);
351  rmode_ = RelocInfo::NONE32;
352}
353
354
355void Assembler::emit(uint32_t x) {
356  *reinterpret_cast<uint32_t*>(pc_) = x;
357  pc_ += sizeof(uint32_t);
358}
359
360
361void Assembler::emit_q(uint64_t x) {
362  *reinterpret_cast<uint64_t*>(pc_) = x;
363  pc_ += sizeof(uint64_t);
364}
365
366
367void Assembler::emit(Handle<Object> handle) {
368  AllowDeferredHandleDereference heap_object_check;
369  // Verify all Objects referred by code are NOT in new space.
370  Object* obj = *handle;
371  if (obj->IsHeapObject()) {
372    emit(reinterpret_cast<intptr_t>(handle.location()),
373         RelocInfo::EMBEDDED_OBJECT);
374  } else {
375    // no relocation needed
376    emit(reinterpret_cast<intptr_t>(obj));
377  }
378}
379
380
381void Assembler::emit(uint32_t x, RelocInfo::Mode rmode, TypeFeedbackId id) {
382  if (rmode == RelocInfo::CODE_TARGET && !id.IsNone()) {
383    RecordRelocInfo(RelocInfo::CODE_TARGET_WITH_ID, id.ToInt());
384  } else if (!RelocInfo::IsNone(rmode)
385      && rmode != RelocInfo::CODE_AGE_SEQUENCE) {
386    RecordRelocInfo(rmode);
387  }
388  emit(x);
389}
390
391
392void Assembler::emit(Handle<Code> code,
393                     RelocInfo::Mode rmode,
394                     TypeFeedbackId id) {
395  AllowDeferredHandleDereference embedding_raw_address;
396  emit(reinterpret_cast<intptr_t>(code.location()), rmode, id);
397}
398
399
400void Assembler::emit(const Immediate& x) {
401  if (x.rmode_ == RelocInfo::INTERNAL_REFERENCE) {
402    Label* label = reinterpret_cast<Label*>(x.x_);
403    emit_code_relative_offset(label);
404    return;
405  }
406  if (!RelocInfo::IsNone(x.rmode_)) RecordRelocInfo(x.rmode_);
407  emit(x.x_);
408}
409
410
411void Assembler::emit_code_relative_offset(Label* label) {
412  if (label->is_bound()) {
413    int32_t pos;
414    pos = label->pos() + Code::kHeaderSize - kHeapObjectTag;
415    emit(pos);
416  } else {
417    emit_disp(label, Displacement::CODE_RELATIVE);
418  }
419}
420
421void Assembler::emit_b(Immediate x) {
422  DCHECK(x.is_int8() || x.is_uint8());
423  uint8_t value = static_cast<uint8_t>(x.x_);
424  *pc_++ = value;
425}
426
427void Assembler::emit_w(const Immediate& x) {
428  DCHECK(RelocInfo::IsNone(x.rmode_));
429  uint16_t value = static_cast<uint16_t>(x.x_);
430  reinterpret_cast<uint16_t*>(pc_)[0] = value;
431  pc_ += sizeof(uint16_t);
432}
433
434
435Address Assembler::target_address_at(Address pc, Address constant_pool) {
436  return pc + sizeof(int32_t) + *reinterpret_cast<int32_t*>(pc);
437}
438
439
440void Assembler::set_target_address_at(Isolate* isolate, Address pc,
441                                      Address constant_pool, Address target,
442                                      ICacheFlushMode icache_flush_mode) {
443  int32_t* p = reinterpret_cast<int32_t*>(pc);
444  *p = target - (pc + sizeof(int32_t));
445  if (icache_flush_mode != SKIP_ICACHE_FLUSH) {
446    Assembler::FlushICache(isolate, p, sizeof(int32_t));
447  }
448}
449
450Address Assembler::target_address_at(Address pc, Code* code) {
451  Address constant_pool = code ? code->constant_pool() : NULL;
452  return target_address_at(pc, constant_pool);
453}
454
455void Assembler::set_target_address_at(Isolate* isolate, Address pc, Code* code,
456                                      Address target,
457                                      ICacheFlushMode icache_flush_mode) {
458  Address constant_pool = code ? code->constant_pool() : NULL;
459  set_target_address_at(isolate, pc, constant_pool, target);
460}
461
462Address Assembler::target_address_from_return_address(Address pc) {
463  return pc - kCallTargetAddressOffset;
464}
465
466
467Displacement Assembler::disp_at(Label* L) {
468  return Displacement(long_at(L->pos()));
469}
470
471
472void Assembler::disp_at_put(Label* L, Displacement disp) {
473  long_at_put(L->pos(), disp.data());
474}
475
476
477void Assembler::emit_disp(Label* L, Displacement::Type type) {
478  Displacement disp(L, type);
479  L->link_to(pc_offset());
480  emit(static_cast<int>(disp.data()));
481}
482
483
484void Assembler::emit_near_disp(Label* L) {
485  byte disp = 0x00;
486  if (L->is_near_linked()) {
487    int offset = L->near_link_pos() - pc_offset();
488    DCHECK(is_int8(offset));
489    disp = static_cast<byte>(offset & 0xFF);
490  }
491  L->link_to(pc_offset(), Label::kNear);
492  *pc_++ = disp;
493}
494
495
496void Assembler::deserialization_set_target_internal_reference_at(
497    Isolate* isolate, Address pc, Address target, RelocInfo::Mode mode) {
498  Memory::Address_at(pc) = target;
499}
500
501
502void Operand::set_modrm(int mod, Register rm) {
503  DCHECK((mod & -4) == 0);
504  buf_[0] = mod << 6 | rm.code();
505  len_ = 1;
506}
507
508
509void Operand::set_sib(ScaleFactor scale, Register index, Register base) {
510  DCHECK(len_ == 1);
511  DCHECK((scale & -4) == 0);
512  // Use SIB with no index register only for base esp.
513  DCHECK(!index.is(esp) || base.is(esp));
514  buf_[1] = scale << 6 | index.code() << 3 | base.code();
515  len_ = 2;
516}
517
518
519void Operand::set_disp8(int8_t disp) {
520  DCHECK(len_ == 1 || len_ == 2);
521  *reinterpret_cast<int8_t*>(&buf_[len_++]) = disp;
522}
523
524
525void Operand::set_dispr(int32_t disp, RelocInfo::Mode rmode) {
526  DCHECK(len_ == 1 || len_ == 2);
527  int32_t* p = reinterpret_cast<int32_t*>(&buf_[len_]);
528  *p = disp;
529  len_ += sizeof(int32_t);
530  rmode_ = rmode;
531}
532
533Operand::Operand(Register reg) {
534  // reg
535  set_modrm(3, reg);
536}
537
538
539Operand::Operand(XMMRegister xmm_reg) {
540  Register reg = { xmm_reg.code() };
541  set_modrm(3, reg);
542}
543
544
545Operand::Operand(int32_t disp, RelocInfo::Mode rmode) {
546  // [disp/r]
547  set_modrm(0, ebp);
548  set_dispr(disp, rmode);
549}
550
551
552Operand::Operand(Immediate imm) {
553  // [disp/r]
554  set_modrm(0, ebp);
555  set_dispr(imm.x_, imm.rmode_);
556}
557}  // namespace internal
558}  // namespace v8
559
560#endif  // V8_IA32_ASSEMBLER_IA32_INL_H_
561