1cfe34b241fc1ab804032d2babe2126afec9eab2
[platform/framework/web/crosswalk.git] / src / v8 / src / arm / assembler-arm-inl.h
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
6 // are 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
14 // distribution.
15 //
16 // - Neither the name of Sun Microsystems or the names of contributors may
17 // be used to endorse or promote products derived from this software without
18 // specific prior written permission.
19 //
20 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 // COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25 // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26 // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
29 // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
31 // OF THE POSSIBILITY OF SUCH DAMAGE.
32
33 // The original source code covered by the above license above has been modified
34 // significantly by Google Inc.
35 // Copyright 2012 the V8 project authors. All rights reserved.
36
37 #ifndef V8_ARM_ASSEMBLER_ARM_INL_H_
38 #define V8_ARM_ASSEMBLER_ARM_INL_H_
39
40 #include "src/arm/assembler-arm.h"
41
42 #include "src/assembler.h"
43 #include "src/debug.h"
44
45
46 namespace v8 {
47 namespace internal {
48
49
50 bool CpuFeatures::SupportsCrankshaft() { return IsSupported(VFP3); }
51
52
53 int Register::NumAllocatableRegisters() {
54   return kMaxNumAllocatableRegisters;
55 }
56
57
58 int DwVfpRegister::NumRegisters() {
59   return CpuFeatures::IsSupported(VFP32DREGS) ? 32 : 16;
60 }
61
62
63 int DwVfpRegister::NumReservedRegisters() {
64   return kNumReservedRegisters;
65 }
66
67
68 int DwVfpRegister::NumAllocatableRegisters() {
69   return NumRegisters() - kNumReservedRegisters;
70 }
71
72
73 int DwVfpRegister::ToAllocationIndex(DwVfpRegister reg) {
74   DCHECK(!reg.is(kDoubleRegZero));
75   DCHECK(!reg.is(kScratchDoubleReg));
76   if (reg.code() > kDoubleRegZero.code()) {
77     return reg.code() - kNumReservedRegisters;
78   }
79   return reg.code();
80 }
81
82
83 DwVfpRegister DwVfpRegister::FromAllocationIndex(int index) {
84   DCHECK(index >= 0 && index < NumAllocatableRegisters());
85   DCHECK(kScratchDoubleReg.code() - kDoubleRegZero.code() ==
86          kNumReservedRegisters - 1);
87   if (index >= kDoubleRegZero.code()) {
88     return from_code(index + kNumReservedRegisters);
89   }
90   return from_code(index);
91 }
92
93
94 void RelocInfo::apply(intptr_t delta, ICacheFlushMode icache_flush_mode) {
95   if (RelocInfo::IsInternalReference(rmode_)) {
96     // absolute code pointer inside code object moves with the code object.
97     int32_t* p = reinterpret_cast<int32_t*>(pc_);
98     *p += delta;  // relocate entry
99   }
100   // We do not use pc relative addressing on ARM, so there is
101   // nothing else to do.
102 }
103
104
105 Address RelocInfo::target_address() {
106   DCHECK(IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_));
107   return Assembler::target_address_at(pc_, host_);
108 }
109
110
111 Address RelocInfo::target_address_address() {
112   DCHECK(IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_)
113                               || rmode_ == EMBEDDED_OBJECT
114                               || rmode_ == EXTERNAL_REFERENCE);
115   if (FLAG_enable_ool_constant_pool ||
116       Assembler::IsMovW(Memory::int32_at(pc_))) {
117     // We return the PC for ool constant pool since this function is used by the
118     // serializerer and expects the address to reside within the code object.
119     return reinterpret_cast<Address>(pc_);
120   } else {
121     DCHECK(Assembler::IsLdrPcImmediateOffset(Memory::int32_at(pc_)));
122     return constant_pool_entry_address();
123   }
124 }
125
126
127 Address RelocInfo::constant_pool_entry_address() {
128   DCHECK(IsInConstantPool());
129   return Assembler::constant_pool_entry_address(pc_, host_->constant_pool());
130 }
131
132
133 int RelocInfo::target_address_size() {
134   return kPointerSize;
135 }
136
137
138 void RelocInfo::set_target_address(Address target,
139                                    WriteBarrierMode write_barrier_mode,
140                                    ICacheFlushMode icache_flush_mode) {
141   DCHECK(IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_));
142   Assembler::set_target_address_at(pc_, host_, target, icache_flush_mode);
143   if (write_barrier_mode == UPDATE_WRITE_BARRIER &&
144       host() != NULL && IsCodeTarget(rmode_)) {
145     Object* target_code = Code::GetCodeFromTargetAddress(target);
146     host()->GetHeap()->incremental_marking()->RecordWriteIntoCode(
147         host(), this, HeapObject::cast(target_code));
148   }
149 }
150
151
152 Object* RelocInfo::target_object() {
153   DCHECK(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT);
154   return reinterpret_cast<Object*>(Assembler::target_address_at(pc_, host_));
155 }
156
157
158 Handle<Object> RelocInfo::target_object_handle(Assembler* origin) {
159   DCHECK(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT);
160   return Handle<Object>(reinterpret_cast<Object**>(
161       Assembler::target_address_at(pc_, host_)));
162 }
163
164
165 void RelocInfo::set_target_object(Object* target,
166                                   WriteBarrierMode write_barrier_mode,
167                                   ICacheFlushMode icache_flush_mode) {
168   DCHECK(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT);
169   Assembler::set_target_address_at(pc_, host_,
170                                    reinterpret_cast<Address>(target),
171                                    icache_flush_mode);
172   if (write_barrier_mode == UPDATE_WRITE_BARRIER &&
173       host() != NULL &&
174       target->IsHeapObject()) {
175     host()->GetHeap()->incremental_marking()->RecordWrite(
176         host(), &Memory::Object_at(pc_), HeapObject::cast(target));
177   }
178 }
179
180
181 Address RelocInfo::target_reference() {
182   DCHECK(rmode_ == EXTERNAL_REFERENCE);
183   return Assembler::target_address_at(pc_, host_);
184 }
185
186
187 Address RelocInfo::target_runtime_entry(Assembler* origin) {
188   DCHECK(IsRuntimeEntry(rmode_));
189   return target_address();
190 }
191
192
193 void RelocInfo::set_target_runtime_entry(Address target,
194                                          WriteBarrierMode write_barrier_mode,
195                                          ICacheFlushMode icache_flush_mode) {
196   DCHECK(IsRuntimeEntry(rmode_));
197   if (target_address() != target)
198     set_target_address(target, write_barrier_mode, icache_flush_mode);
199 }
200
201
202 Handle<Cell> RelocInfo::target_cell_handle() {
203   DCHECK(rmode_ == RelocInfo::CELL);
204   Address address = Memory::Address_at(pc_);
205   return Handle<Cell>(reinterpret_cast<Cell**>(address));
206 }
207
208
209 Cell* RelocInfo::target_cell() {
210   DCHECK(rmode_ == RelocInfo::CELL);
211   return Cell::FromValueAddress(Memory::Address_at(pc_));
212 }
213
214
215 void RelocInfo::set_target_cell(Cell* cell,
216                                 WriteBarrierMode write_barrier_mode,
217                                 ICacheFlushMode icache_flush_mode) {
218   DCHECK(rmode_ == RelocInfo::CELL);
219   Address address = cell->address() + Cell::kValueOffset;
220   Memory::Address_at(pc_) = address;
221   if (write_barrier_mode == UPDATE_WRITE_BARRIER && host() != NULL) {
222     // TODO(1550) We are passing NULL as a slot because cell can never be on
223     // evacuation candidate.
224     host()->GetHeap()->incremental_marking()->RecordWrite(
225         host(), NULL, cell);
226   }
227 }
228
229
230 static const int kNoCodeAgeSequenceLength = 3 * Assembler::kInstrSize;
231
232
233 Handle<Object> RelocInfo::code_age_stub_handle(Assembler* origin) {
234   UNREACHABLE();  // This should never be reached on Arm.
235   return Handle<Object>();
236 }
237
238
239 Code* RelocInfo::code_age_stub() {
240   DCHECK(rmode_ == RelocInfo::CODE_AGE_SEQUENCE);
241   return Code::GetCodeFromTargetAddress(
242       Memory::Address_at(pc_ +
243                          (kNoCodeAgeSequenceLength - Assembler::kInstrSize)));
244 }
245
246
247 void RelocInfo::set_code_age_stub(Code* stub,
248                                   ICacheFlushMode icache_flush_mode) {
249   DCHECK(rmode_ == RelocInfo::CODE_AGE_SEQUENCE);
250   Memory::Address_at(pc_ +
251                      (kNoCodeAgeSequenceLength - Assembler::kInstrSize)) =
252       stub->instruction_start();
253 }
254
255
256 Address RelocInfo::call_address() {
257   // The 2 instructions offset assumes patched debug break slot or return
258   // sequence.
259   DCHECK((IsJSReturn(rmode()) && IsPatchedReturnSequence()) ||
260          (IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence()));
261   return Memory::Address_at(pc_ + 2 * Assembler::kInstrSize);
262 }
263
264
265 void RelocInfo::set_call_address(Address target) {
266   DCHECK((IsJSReturn(rmode()) && IsPatchedReturnSequence()) ||
267          (IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence()));
268   Memory::Address_at(pc_ + 2 * Assembler::kInstrSize) = target;
269   if (host() != NULL) {
270     Object* target_code = Code::GetCodeFromTargetAddress(target);
271     host()->GetHeap()->incremental_marking()->RecordWriteIntoCode(
272         host(), this, HeapObject::cast(target_code));
273   }
274 }
275
276
277 Object* RelocInfo::call_object() {
278   return *call_object_address();
279 }
280
281
282 void RelocInfo::set_call_object(Object* target) {
283   *call_object_address() = target;
284 }
285
286
287 Object** RelocInfo::call_object_address() {
288   DCHECK((IsJSReturn(rmode()) && IsPatchedReturnSequence()) ||
289          (IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence()));
290   return reinterpret_cast<Object**>(pc_ + 2 * Assembler::kInstrSize);
291 }
292
293
294 void RelocInfo::WipeOut() {
295   DCHECK(IsEmbeddedObject(rmode_) ||
296          IsCodeTarget(rmode_) ||
297          IsRuntimeEntry(rmode_) ||
298          IsExternalReference(rmode_));
299   Assembler::set_target_address_at(pc_, host_, NULL);
300 }
301
302
303 bool RelocInfo::IsPatchedReturnSequence() {
304   Instr current_instr = Assembler::instr_at(pc_);
305   Instr next_instr = Assembler::instr_at(pc_ + Assembler::kInstrSize);
306   // A patched return sequence is:
307   //  ldr ip, [pc, #0]
308   //  blx ip
309   return Assembler::IsLdrPcImmediateOffset(current_instr) &&
310          Assembler::IsBlxReg(next_instr);
311 }
312
313
314 bool RelocInfo::IsPatchedDebugBreakSlotSequence() {
315   Instr current_instr = Assembler::instr_at(pc_);
316   return !Assembler::IsNop(current_instr, Assembler::DEBUG_BREAK_NOP);
317 }
318
319
320 void RelocInfo::Visit(Isolate* isolate, ObjectVisitor* visitor) {
321   RelocInfo::Mode mode = rmode();
322   if (mode == RelocInfo::EMBEDDED_OBJECT) {
323     visitor->VisitEmbeddedPointer(this);
324   } else if (RelocInfo::IsCodeTarget(mode)) {
325     visitor->VisitCodeTarget(this);
326   } else if (mode == RelocInfo::CELL) {
327     visitor->VisitCell(this);
328   } else if (mode == RelocInfo::EXTERNAL_REFERENCE) {
329     visitor->VisitExternalReference(this);
330   } else if (RelocInfo::IsCodeAgeSequence(mode)) {
331     visitor->VisitCodeAgeSequence(this);
332   } else if (((RelocInfo::IsJSReturn(mode) &&
333               IsPatchedReturnSequence()) ||
334              (RelocInfo::IsDebugBreakSlot(mode) &&
335               IsPatchedDebugBreakSlotSequence())) &&
336              isolate->debug()->has_break_points()) {
337     visitor->VisitDebugTarget(this);
338   } else if (RelocInfo::IsRuntimeEntry(mode)) {
339     visitor->VisitRuntimeEntry(this);
340   }
341 }
342
343
344 template<typename StaticVisitor>
345 void RelocInfo::Visit(Heap* heap) {
346   RelocInfo::Mode mode = rmode();
347   if (mode == RelocInfo::EMBEDDED_OBJECT) {
348     StaticVisitor::VisitEmbeddedPointer(heap, this);
349   } else if (RelocInfo::IsCodeTarget(mode)) {
350     StaticVisitor::VisitCodeTarget(heap, this);
351   } else if (mode == RelocInfo::CELL) {
352     StaticVisitor::VisitCell(heap, this);
353   } else if (mode == RelocInfo::EXTERNAL_REFERENCE) {
354     StaticVisitor::VisitExternalReference(this);
355   } else if (RelocInfo::IsCodeAgeSequence(mode)) {
356     StaticVisitor::VisitCodeAgeSequence(heap, this);
357   } else if (heap->isolate()->debug()->has_break_points() &&
358              ((RelocInfo::IsJSReturn(mode) &&
359               IsPatchedReturnSequence()) ||
360              (RelocInfo::IsDebugBreakSlot(mode) &&
361               IsPatchedDebugBreakSlotSequence()))) {
362     StaticVisitor::VisitDebugTarget(heap, this);
363   } else if (RelocInfo::IsRuntimeEntry(mode)) {
364     StaticVisitor::VisitRuntimeEntry(this);
365   }
366 }
367
368
369 Operand::Operand(int32_t immediate, RelocInfo::Mode rmode)  {
370   rm_ = no_reg;
371   imm32_ = immediate;
372   rmode_ = rmode;
373 }
374
375
376 Operand::Operand(const ExternalReference& f)  {
377   rm_ = no_reg;
378   imm32_ = reinterpret_cast<int32_t>(f.address());
379   rmode_ = RelocInfo::EXTERNAL_REFERENCE;
380 }
381
382
383 Operand::Operand(Smi* value) {
384   rm_ = no_reg;
385   imm32_ =  reinterpret_cast<intptr_t>(value);
386   rmode_ = RelocInfo::NONE32;
387 }
388
389
390 Operand::Operand(Register rm) {
391   rm_ = rm;
392   rs_ = no_reg;
393   shift_op_ = LSL;
394   shift_imm_ = 0;
395 }
396
397
398 bool Operand::is_reg() const {
399   return rm_.is_valid() &&
400          rs_.is(no_reg) &&
401          shift_op_ == LSL &&
402          shift_imm_ == 0;
403 }
404
405
406 void Assembler::CheckBuffer() {
407   if (buffer_space() <= kGap) {
408     GrowBuffer();
409   }
410   if (pc_offset() >= next_buffer_check_) {
411     CheckConstPool(false, true);
412   }
413 }
414
415
416 void Assembler::emit(Instr x) {
417   CheckBuffer();
418   *reinterpret_cast<Instr*>(pc_) = x;
419   pc_ += kInstrSize;
420 }
421
422
423 Address Assembler::target_address_from_return_address(Address pc) {
424   // Returns the address of the call target from the return address that will
425   // be returned to after a call.
426   // Call sequence on V7 or later is :
427   //  movw  ip, #... @ call address low 16
428   //  movt  ip, #... @ call address high 16
429   //  blx   ip
430   //                      @ return address
431   // Or pre-V7 or cases that need frequent patching, the address is in the
432   // constant pool.  It could be a small constant pool load:
433   //  ldr   ip, [pc / pp, #...] @ call address
434   //  blx   ip
435   //                      @ return address
436   // Or an extended constant pool load:
437   //  movw  ip, #...
438   //  movt  ip, #...
439   //  ldr   ip, [pc, ip]  @ call address
440   //  blx   ip
441   //                      @ return address
442   Address candidate = pc - 2 * Assembler::kInstrSize;
443   Instr candidate_instr(Memory::int32_at(candidate));
444   if (IsLdrPcImmediateOffset(candidate_instr) |
445       IsLdrPpImmediateOffset(candidate_instr)) {
446     return candidate;
447   } else if (IsLdrPpRegOffset(candidate_instr)) {
448     candidate = pc - 4 * Assembler::kInstrSize;
449     DCHECK(IsMovW(Memory::int32_at(candidate)) &&
450            IsMovT(Memory::int32_at(candidate + Assembler::kInstrSize)));
451     return candidate;
452   } else {
453     candidate = pc - 3 * Assembler::kInstrSize;
454     DCHECK(IsMovW(Memory::int32_at(candidate)) &&
455            IsMovT(Memory::int32_at(candidate + kInstrSize)));
456     return candidate;
457   }
458 }
459
460
461 Address Assembler::break_address_from_return_address(Address pc) {
462   return pc - Assembler::kPatchDebugBreakSlotReturnOffset;
463 }
464
465
466 Address Assembler::return_address_from_call_start(Address pc) {
467   if (IsLdrPcImmediateOffset(Memory::int32_at(pc)) |
468       IsLdrPpImmediateOffset(Memory::int32_at(pc))) {
469     // Load from constant pool, small section.
470     return pc + kInstrSize * 2;
471   } else {
472     DCHECK(IsMovW(Memory::int32_at(pc)));
473     DCHECK(IsMovT(Memory::int32_at(pc + kInstrSize)));
474     if (IsLdrPpRegOffset(Memory::int32_at(pc + kInstrSize))) {
475       // Load from constant pool, extended section.
476       return pc + kInstrSize * 4;
477     } else {
478       // A movw / movt load immediate.
479       return pc + kInstrSize * 3;
480     }
481   }
482 }
483
484
485 void Assembler::deserialization_set_special_target_at(
486     Address constant_pool_entry, Code* code, Address target) {
487   if (FLAG_enable_ool_constant_pool) {
488     set_target_address_at(constant_pool_entry, code, target);
489   } else {
490     Memory::Address_at(constant_pool_entry) = target;
491   }
492 }
493
494
495 bool Assembler::is_constant_pool_load(Address pc) {
496   return !Assembler::IsMovW(Memory::int32_at(pc)) ||
497          (FLAG_enable_ool_constant_pool &&
498           Assembler::IsLdrPpRegOffset(
499               Memory::int32_at(pc + 2 * Assembler::kInstrSize)));
500 }
501
502
503 Address Assembler::constant_pool_entry_address(
504     Address pc, ConstantPoolArray* constant_pool) {
505   if (FLAG_enable_ool_constant_pool) {
506     DCHECK(constant_pool != NULL);
507     int cp_offset;
508     if (IsMovW(Memory::int32_at(pc))) {
509       DCHECK(IsMovT(Memory::int32_at(pc + kInstrSize)) &&
510              IsLdrPpRegOffset(Memory::int32_at(pc + 2 * kInstrSize)));
511       // This is an extended constant pool lookup.
512       Instruction* movw_instr = Instruction::At(pc);
513       Instruction* movt_instr = Instruction::At(pc + kInstrSize);
514       cp_offset = (movt_instr->ImmedMovwMovtValue() << 16) |
515                   movw_instr->ImmedMovwMovtValue();
516     } else {
517       // This is a small constant pool lookup.
518       DCHECK(Assembler::IsLdrPpImmediateOffset(Memory::int32_at(pc)));
519       cp_offset = GetLdrRegisterImmediateOffset(Memory::int32_at(pc));
520     }
521     return reinterpret_cast<Address>(constant_pool) + cp_offset;
522   } else {
523     DCHECK(Assembler::IsLdrPcImmediateOffset(Memory::int32_at(pc)));
524     Instr instr = Memory::int32_at(pc);
525     return pc + GetLdrRegisterImmediateOffset(instr) + kPcLoadDelta;
526   }
527 }
528
529
530 Address Assembler::target_address_at(Address pc,
531                                      ConstantPoolArray* constant_pool) {
532   if (is_constant_pool_load(pc)) {
533     // This is a constant pool lookup. Return the value in the constant pool.
534     return Memory::Address_at(constant_pool_entry_address(pc, constant_pool));
535   } else {
536     // This is an movw_movt immediate load. Return the immediate.
537     DCHECK(IsMovW(Memory::int32_at(pc)) &&
538            IsMovT(Memory::int32_at(pc + kInstrSize)));
539     Instruction* movw_instr = Instruction::At(pc);
540     Instruction* movt_instr = Instruction::At(pc + kInstrSize);
541     return reinterpret_cast<Address>(
542         (movt_instr->ImmedMovwMovtValue() << 16) |
543          movw_instr->ImmedMovwMovtValue());
544   }
545 }
546
547
548 void Assembler::set_target_address_at(Address pc,
549                                       ConstantPoolArray* constant_pool,
550                                       Address target,
551                                       ICacheFlushMode icache_flush_mode) {
552   if (is_constant_pool_load(pc)) {
553     // This is a constant pool lookup. Update the entry in the constant pool.
554     Memory::Address_at(constant_pool_entry_address(pc, constant_pool)) = target;
555     // Intuitively, we would think it is necessary to always flush the
556     // instruction cache after patching a target address in the code as follows:
557     //   CpuFeatures::FlushICache(pc, sizeof(target));
558     // However, on ARM, no instruction is actually patched in the case
559     // of embedded constants of the form:
560     // ldr   ip, [pp, #...]
561     // since the instruction accessing this address in the constant pool remains
562     // unchanged.
563   } else {
564     // This is an movw_movt immediate load. Patch the immediate embedded in the
565     // instructions.
566     DCHECK(IsMovW(Memory::int32_at(pc)));
567     DCHECK(IsMovT(Memory::int32_at(pc + kInstrSize)));
568     uint32_t* instr_ptr = reinterpret_cast<uint32_t*>(pc);
569     uint32_t immediate = reinterpret_cast<uint32_t>(target);
570     instr_ptr[0] = PatchMovwImmediate(instr_ptr[0], immediate & 0xFFFF);
571     instr_ptr[1] = PatchMovwImmediate(instr_ptr[1], immediate >> 16);
572     DCHECK(IsMovW(Memory::int32_at(pc)));
573     DCHECK(IsMovT(Memory::int32_at(pc + kInstrSize)));
574     if (icache_flush_mode != SKIP_ICACHE_FLUSH) {
575       CpuFeatures::FlushICache(pc, 2 * kInstrSize);
576     }
577   }
578 }
579
580
581 } }  // namespace v8::internal
582
583 #endif  // V8_ARM_ASSEMBLER_ARM_INL_H_