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