• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/debug.h"
44 #include "src/objects-inl.h"
45 
46 namespace v8 {
47 namespace internal {
48 
SupportsOptimizer()49 bool CpuFeatures::SupportsOptimizer() { return true; }
50 
SupportsWasmSimd128()51 bool CpuFeatures::SupportsWasmSimd128() { return IsSupported(NEON); }
52 
NumRegisters()53 int DoubleRegister::NumRegisters() {
54   return CpuFeatures::IsSupported(VFP32DREGS) ? 32 : 16;
55 }
56 
57 
apply(intptr_t delta)58 void RelocInfo::apply(intptr_t delta) {
59   if (RelocInfo::IsInternalReference(rmode_)) {
60     // absolute code pointer inside code object moves with the code object.
61     int32_t* p = reinterpret_cast<int32_t*>(pc_);
62     *p += delta;  // relocate entry
63   } else if (RelocInfo::IsRelativeCodeTarget(rmode_)) {
64     Instruction* branch = Instruction::At(pc_);
65     int32_t branch_offset = branch->GetBranchOffset() + delta;
66     branch->SetBranchOffset(branch_offset);
67   }
68 }
69 
70 
target_address()71 Address RelocInfo::target_address() {
72   DCHECK(IsCodeTargetMode(rmode_) || IsRuntimeEntry(rmode_) ||
73          IsWasmCall(rmode_));
74   return Assembler::target_address_at(pc_, constant_pool_);
75 }
76 
target_address_address()77 Address RelocInfo::target_address_address() {
78   DCHECK(IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_) || IsWasmCall(rmode_) ||
79          IsEmbeddedObject(rmode_) || IsExternalReference(rmode_) ||
80          IsOffHeapTarget(rmode_));
81   if (Assembler::IsMovW(Memory<int32_t>(pc_))) {
82     return pc_;
83   } else {
84     DCHECK(Assembler::IsLdrPcImmediateOffset(Memory<int32_t>(pc_)));
85     return constant_pool_entry_address();
86   }
87 }
88 
89 
constant_pool_entry_address()90 Address RelocInfo::constant_pool_entry_address() {
91   DCHECK(IsInConstantPool());
92   return Assembler::constant_pool_entry_address(pc_, constant_pool_);
93 }
94 
95 
target_address_size()96 int RelocInfo::target_address_size() {
97   return kPointerSize;
98 }
99 
target_object()100 HeapObject* RelocInfo::target_object() {
101   DCHECK(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT);
102   return HeapObject::cast(reinterpret_cast<Object*>(
103       Assembler::target_address_at(pc_, constant_pool_)));
104 }
105 
target_object_handle(Assembler * origin)106 Handle<HeapObject> RelocInfo::target_object_handle(Assembler* origin) {
107   if (IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT) {
108     return Handle<HeapObject>(reinterpret_cast<HeapObject**>(
109         Assembler::target_address_at(pc_, constant_pool_)));
110   }
111   DCHECK(IsRelativeCodeTarget(rmode_));
112   return origin->relative_code_target_object_handle_at(pc_);
113 }
114 
set_target_object(Heap * heap,HeapObject * target,WriteBarrierMode write_barrier_mode,ICacheFlushMode icache_flush_mode)115 void RelocInfo::set_target_object(Heap* heap, HeapObject* target,
116                                   WriteBarrierMode write_barrier_mode,
117                                   ICacheFlushMode icache_flush_mode) {
118   DCHECK(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT);
119   Assembler::set_target_address_at(pc_, constant_pool_,
120                                    reinterpret_cast<Address>(target),
121                                    icache_flush_mode);
122   if (write_barrier_mode == UPDATE_WRITE_BARRIER && host() != nullptr) {
123     WriteBarrierForCode(host(), this, target);
124   }
125 }
126 
127 
target_external_reference()128 Address RelocInfo::target_external_reference() {
129   DCHECK(rmode_ == EXTERNAL_REFERENCE);
130   return Assembler::target_address_at(pc_, constant_pool_);
131 }
132 
set_target_external_reference(Address target,ICacheFlushMode icache_flush_mode)133 void RelocInfo::set_target_external_reference(
134     Address target, ICacheFlushMode icache_flush_mode) {
135   DCHECK(rmode_ == RelocInfo::EXTERNAL_REFERENCE);
136   Assembler::set_target_address_at(pc_, constant_pool_, target,
137                                    icache_flush_mode);
138 }
139 
target_internal_reference()140 Address RelocInfo::target_internal_reference() {
141   DCHECK(rmode_ == INTERNAL_REFERENCE);
142   return Memory<Address>(pc_);
143 }
144 
145 
target_internal_reference_address()146 Address RelocInfo::target_internal_reference_address() {
147   DCHECK(rmode_ == INTERNAL_REFERENCE);
148   return pc_;
149 }
150 
target_runtime_entry(Assembler * origin)151 Address RelocInfo::target_runtime_entry(Assembler* origin) {
152   DCHECK(IsRuntimeEntry(rmode_));
153   return target_address();
154 }
155 
set_target_runtime_entry(Address target,WriteBarrierMode write_barrier_mode,ICacheFlushMode icache_flush_mode)156 void RelocInfo::set_target_runtime_entry(Address target,
157                                          WriteBarrierMode write_barrier_mode,
158                                          ICacheFlushMode icache_flush_mode) {
159   DCHECK(IsRuntimeEntry(rmode_));
160   if (target_address() != target)
161     set_target_address(target, write_barrier_mode, icache_flush_mode);
162 }
163 
target_off_heap_target()164 Address RelocInfo::target_off_heap_target() {
165   DCHECK(IsOffHeapTarget(rmode_));
166   return Assembler::target_address_at(pc_, constant_pool_);
167 }
168 
WipeOut()169 void RelocInfo::WipeOut() {
170   DCHECK(IsEmbeddedObject(rmode_) || IsCodeTarget(rmode_) ||
171          IsRuntimeEntry(rmode_) || IsExternalReference(rmode_) ||
172          IsInternalReference(rmode_) || IsOffHeapTarget(rmode_));
173   if (IsInternalReference(rmode_)) {
174     Memory<Address>(pc_) = kNullAddress;
175   } else {
176     Assembler::set_target_address_at(pc_, constant_pool_, kNullAddress);
177   }
178 }
179 
relative_code_target_object_handle_at(Address pc)180 Handle<Code> Assembler::relative_code_target_object_handle_at(
181     Address pc) const {
182   Instruction* branch = Instruction::At(pc);
183   int code_target_index = branch->GetBranchOffset() / kInstrSize;
184   return GetCodeTarget(code_target_index);
185 }
186 
187 template <typename ObjectVisitor>
Visit(ObjectVisitor * visitor)188 void RelocInfo::Visit(ObjectVisitor* visitor) {
189   RelocInfo::Mode mode = rmode();
190   if (mode == RelocInfo::EMBEDDED_OBJECT) {
191     visitor->VisitEmbeddedPointer(host(), this);
192   } else if (RelocInfo::IsCodeTargetMode(mode)) {
193     visitor->VisitCodeTarget(host(), this);
194   } else if (mode == RelocInfo::EXTERNAL_REFERENCE) {
195     visitor->VisitExternalReference(host(), this);
196   } else if (mode == RelocInfo::INTERNAL_REFERENCE) {
197     visitor->VisitInternalReference(host(), this);
198   } else if (RelocInfo::IsRuntimeEntry(mode)) {
199     visitor->VisitRuntimeEntry(host(), this);
200   } else if (RelocInfo::IsOffHeapTarget(mode)) {
201     visitor->VisitOffHeapTarget(host(), this);
202   }
203 }
204 
Operand(int32_t immediate,RelocInfo::Mode rmode)205 Operand::Operand(int32_t immediate, RelocInfo::Mode rmode) : rmode_(rmode) {
206   value_.immediate = immediate;
207 }
208 
Zero()209 Operand Operand::Zero() { return Operand(static_cast<int32_t>(0)); }
210 
Operand(const ExternalReference & f)211 Operand::Operand(const ExternalReference& f)
212     : rmode_(RelocInfo::EXTERNAL_REFERENCE) {
213   value_.immediate = static_cast<int32_t>(f.address());
214 }
215 
Operand(Smi * value)216 Operand::Operand(Smi* value) : rmode_(RelocInfo::NONE) {
217   value_.immediate = reinterpret_cast<intptr_t>(value);
218 }
219 
Operand(Register rm)220 Operand::Operand(Register rm) : rm_(rm), shift_op_(LSL), shift_imm_(0) {}
221 
CheckBuffer()222 void Assembler::CheckBuffer() {
223   if (buffer_space() <= kGap) {
224     GrowBuffer();
225   }
226   MaybeCheckConstPool();
227 }
228 
229 
emit(Instr x)230 void Assembler::emit(Instr x) {
231   CheckBuffer();
232   *reinterpret_cast<Instr*>(pc_) = x;
233   pc_ += kInstrSize;
234 }
235 
236 
target_address_from_return_address(Address pc)237 Address Assembler::target_address_from_return_address(Address pc) {
238   // Returns the address of the call target from the return address that will
239   // be returned to after a call.
240   // Call sequence on V7 or later is:
241   //  movw  ip, #... @ call address low 16
242   //  movt  ip, #... @ call address high 16
243   //  blx   ip
244   //                      @ return address
245   // For V6 when the constant pool is unavailable, it is:
246   //  mov  ip, #...     @ call address low 8
247   //  orr  ip, ip, #... @ call address 2nd 8
248   //  orr  ip, ip, #... @ call address 3rd 8
249   //  orr  ip, ip, #... @ call address high 8
250   //  blx   ip
251   //                      @ return address
252   // In cases that need frequent patching, the address is in the
253   // constant pool.  It could be a small constant pool load:
254   //  ldr   ip, [pc, #...] @ call address
255   //  blx   ip
256   //                      @ return address
257   Address candidate = pc - 2 * kInstrSize;
258   Instr candidate_instr(Memory<int32_t>(candidate));
259   if (IsLdrPcImmediateOffset(candidate_instr)) {
260     return candidate;
261   } else {
262     if (CpuFeatures::IsSupported(ARMv7)) {
263       candidate -= 1 * kInstrSize;
264       DCHECK(IsMovW(Memory<int32_t>(candidate)) &&
265              IsMovT(Memory<int32_t>(candidate + kInstrSize)));
266     } else {
267       candidate -= 3 * kInstrSize;
268       DCHECK(IsMovImmed(Memory<int32_t>(candidate)) &&
269              IsOrrImmed(Memory<int32_t>(candidate + kInstrSize)) &&
270              IsOrrImmed(Memory<int32_t>(candidate + 2 * kInstrSize)) &&
271              IsOrrImmed(Memory<int32_t>(candidate + 3 * kInstrSize)));
272     }
273     return candidate;
274   }
275 }
276 
277 
return_address_from_call_start(Address pc)278 Address Assembler::return_address_from_call_start(Address pc) {
279   if (IsLdrPcImmediateOffset(Memory<int32_t>(pc))) {
280     // Load from constant pool, small section.
281     return pc + kInstrSize * 2;
282   } else {
283     if (CpuFeatures::IsSupported(ARMv7)) {
284       DCHECK(IsMovW(Memory<int32_t>(pc)));
285       DCHECK(IsMovT(Memory<int32_t>(pc + kInstrSize)));
286       // A movw / movt load immediate.
287       return pc + kInstrSize * 3;
288     } else {
289       DCHECK(IsMovImmed(Memory<int32_t>(pc)));
290       DCHECK(IsOrrImmed(Memory<int32_t>(pc + kInstrSize)));
291       DCHECK(IsOrrImmed(Memory<int32_t>(pc + 2 * kInstrSize)));
292       DCHECK(IsOrrImmed(Memory<int32_t>(pc + 3 * kInstrSize)));
293       // A mov / orr load immediate.
294       return pc + kInstrSize * 5;
295     }
296   }
297 }
298 
deserialization_set_special_target_at(Address constant_pool_entry,Code * code,Address target)299 void Assembler::deserialization_set_special_target_at(
300     Address constant_pool_entry, Code* code, Address target) {
301   Memory<Address>(constant_pool_entry) = target;
302 }
303 
deserialization_special_target_size(Address location)304 int Assembler::deserialization_special_target_size(Address location) {
305   return kSpecialTargetSize;
306 }
307 
deserialization_set_target_internal_reference_at(Address pc,Address target,RelocInfo::Mode mode)308 void Assembler::deserialization_set_target_internal_reference_at(
309     Address pc, Address target, RelocInfo::Mode mode) {
310   Memory<Address>(pc) = target;
311 }
312 
313 
is_constant_pool_load(Address pc)314 bool Assembler::is_constant_pool_load(Address pc) {
315   return IsLdrPcImmediateOffset(Memory<int32_t>(pc));
316 }
317 
318 
constant_pool_entry_address(Address pc,Address constant_pool)319 Address Assembler::constant_pool_entry_address(Address pc,
320                                                Address constant_pool) {
321   DCHECK(Assembler::IsLdrPcImmediateOffset(Memory<int32_t>(pc)));
322   Instr instr = Memory<int32_t>(pc);
323   return pc + GetLdrRegisterImmediateOffset(instr) + Instruction::kPcLoadDelta;
324 }
325 
326 
target_address_at(Address pc,Address constant_pool)327 Address Assembler::target_address_at(Address pc, Address constant_pool) {
328   if (is_constant_pool_load(pc)) {
329     // This is a constant pool lookup. Return the value in the constant pool.
330     return Memory<Address>(constant_pool_entry_address(pc, constant_pool));
331   } else if (CpuFeatures::IsSupported(ARMv7) && IsMovW(Memory<int32_t>(pc))) {
332     // This is an movw / movt immediate load. Return the immediate.
333     DCHECK(IsMovW(Memory<int32_t>(pc)) &&
334            IsMovT(Memory<int32_t>(pc + kInstrSize)));
335     Instruction* movw_instr = Instruction::At(pc);
336     Instruction* movt_instr = Instruction::At(pc + kInstrSize);
337     return static_cast<Address>((movt_instr->ImmedMovwMovtValue() << 16) |
338                                 movw_instr->ImmedMovwMovtValue());
339   } else if (IsMovImmed(Memory<int32_t>(pc))) {
340     // This is an mov / orr immediate load. Return the immediate.
341     DCHECK(IsMovImmed(Memory<int32_t>(pc)) &&
342            IsOrrImmed(Memory<int32_t>(pc + kInstrSize)) &&
343            IsOrrImmed(Memory<int32_t>(pc + 2 * kInstrSize)) &&
344            IsOrrImmed(Memory<int32_t>(pc + 3 * kInstrSize)));
345     Instr mov_instr = instr_at(pc);
346     Instr orr_instr_1 = instr_at(pc + kInstrSize);
347     Instr orr_instr_2 = instr_at(pc + 2 * kInstrSize);
348     Instr orr_instr_3 = instr_at(pc + 3 * kInstrSize);
349     Address ret = static_cast<Address>(
350         DecodeShiftImm(mov_instr) | DecodeShiftImm(orr_instr_1) |
351         DecodeShiftImm(orr_instr_2) | DecodeShiftImm(orr_instr_3));
352     return ret;
353   } else {
354     Instruction* branch = Instruction::At(pc);
355     int32_t delta = branch->GetBranchOffset();
356     return pc + delta + Instruction::kPcLoadDelta;
357   }
358 }
359 
set_target_address_at(Address pc,Address constant_pool,Address target,ICacheFlushMode icache_flush_mode)360 void Assembler::set_target_address_at(Address pc, Address constant_pool,
361                                       Address target,
362                                       ICacheFlushMode icache_flush_mode) {
363   if (is_constant_pool_load(pc)) {
364     // This is a constant pool lookup. Update the entry in the constant pool.
365     Memory<Address>(constant_pool_entry_address(pc, constant_pool)) = target;
366     // Intuitively, we would think it is necessary to always flush the
367     // instruction cache after patching a target address in the code as follows:
368     //   Assembler::FlushICache(pc, sizeof(target));
369     // However, on ARM, no instruction is actually patched in the case
370     // of embedded constants of the form:
371     // ldr   ip, [pp, #...]
372     // since the instruction accessing this address in the constant pool remains
373     // unchanged.
374   } else if (CpuFeatures::IsSupported(ARMv7) && IsMovW(Memory<int32_t>(pc))) {
375     // This is an movw / movt immediate load. Patch the immediate embedded in
376     // the instructions.
377     DCHECK(IsMovW(Memory<int32_t>(pc)));
378     DCHECK(IsMovT(Memory<int32_t>(pc + kInstrSize)));
379     uint32_t* instr_ptr = reinterpret_cast<uint32_t*>(pc);
380     uint32_t immediate = static_cast<uint32_t>(target);
381     instr_ptr[0] = PatchMovwImmediate(instr_ptr[0], immediate & 0xFFFF);
382     instr_ptr[1] = PatchMovwImmediate(instr_ptr[1], immediate >> 16);
383     DCHECK(IsMovW(Memory<int32_t>(pc)));
384     DCHECK(IsMovT(Memory<int32_t>(pc + kInstrSize)));
385     if (icache_flush_mode != SKIP_ICACHE_FLUSH) {
386       Assembler::FlushICache(pc, 2 * kInstrSize);
387     }
388   } else if (IsMovImmed(Memory<int32_t>(pc))) {
389     // This is an mov / orr immediate load. Patch the immediate embedded in
390     // the instructions.
391     DCHECK(IsMovImmed(Memory<int32_t>(pc)) &&
392            IsOrrImmed(Memory<int32_t>(pc + kInstrSize)) &&
393            IsOrrImmed(Memory<int32_t>(pc + 2 * kInstrSize)) &&
394            IsOrrImmed(Memory<int32_t>(pc + 3 * kInstrSize)));
395     uint32_t* instr_ptr = reinterpret_cast<uint32_t*>(pc);
396     uint32_t immediate = static_cast<uint32_t>(target);
397     instr_ptr[0] = PatchShiftImm(instr_ptr[0], immediate & kImm8Mask);
398     instr_ptr[1] = PatchShiftImm(instr_ptr[1], immediate & (kImm8Mask << 8));
399     instr_ptr[2] = PatchShiftImm(instr_ptr[2], immediate & (kImm8Mask << 16));
400     instr_ptr[3] = PatchShiftImm(instr_ptr[3], immediate & (kImm8Mask << 24));
401     DCHECK(IsMovImmed(Memory<int32_t>(pc)) &&
402            IsOrrImmed(Memory<int32_t>(pc + kInstrSize)) &&
403            IsOrrImmed(Memory<int32_t>(pc + 2 * kInstrSize)) &&
404            IsOrrImmed(Memory<int32_t>(pc + 3 * kInstrSize)));
405     if (icache_flush_mode != SKIP_ICACHE_FLUSH) {
406       Assembler::FlushICache(pc, 4 * kInstrSize);
407     }
408   } else {
409     intptr_t branch_offset = target - pc - Instruction::kPcLoadDelta;
410     Instruction* branch = Instruction::At(pc);
411     branch->SetBranchOffset(branch_offset);
412     if (icache_flush_mode != SKIP_ICACHE_FLUSH) {
413       Assembler::FlushICache(pc, kInstrSize);
414     }
415   }
416 }
417 
EnsureSpace(Assembler * assembler)418 EnsureSpace::EnsureSpace(Assembler* assembler) { assembler->CheckBuffer(); }
419 
420 template <typename T>
CanAcquireVfp()421 bool UseScratchRegisterScope::CanAcquireVfp() const {
422   VfpRegList* available = assembler_->GetScratchVfpRegisterList();
423   DCHECK_NOT_NULL(available);
424   for (int index = 0; index < T::kNumRegisters; index++) {
425     T reg = T::from_code(index);
426     uint64_t mask = reg.ToVfpRegList();
427     if ((*available & mask) == mask) {
428       return true;
429     }
430   }
431   return false;
432 }
433 
434 template <typename T>
AcquireVfp()435 T UseScratchRegisterScope::AcquireVfp() {
436   VfpRegList* available = assembler_->GetScratchVfpRegisterList();
437   DCHECK_NOT_NULL(available);
438   for (int index = 0; index < T::kNumRegisters; index++) {
439     T reg = T::from_code(index);
440     uint64_t mask = reg.ToVfpRegList();
441     if ((*available & mask) == mask) {
442       *available &= ~mask;
443       return reg;
444     }
445   }
446   UNREACHABLE();
447 }
448 
449 }  // namespace internal
450 }  // namespace v8
451 
452 #endif  // V8_ARM_ASSEMBLER_ARM_INL_H_
453