1// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2// for details. All rights reserved. Use of this source code is governed by a
3// BSD-style license that can be found in the LICENSE file.
4
5#include "vm/globals.h" // Needed here to get TARGET_ARCH_ARM.
6#if defined(TARGET_ARCH_ARM)
7
8#include "vm/instructions.h"
9#include "vm/instructions_arm.h"
10
11#include "vm/constants.h"
12#include "vm/cpu.h"
13#include "vm/object.h"
14#include "vm/reverse_pc_lookup_cache.h"
15
16namespace dart {
17
18CallPattern::CallPattern(uword pc, const Code& code)
19 : object_pool_(ObjectPool::Handle(code.GetObjectPool())),
20 target_code_pool_index_(-1) {
21 ASSERT(code.ContainsInstructionAt(pc));
22 // Last instruction: blx lr.
23 ASSERT(*(reinterpret_cast<uint32_t*>(pc) - 1) == 0xe12fff3e);
24
25 Register reg;
26 InstructionPattern::DecodeLoadWordFromPool(pc - 2 * Instr::kInstrSize, &reg,
27 &target_code_pool_index_);
28 ASSERT(reg == CODE_REG);
29}
30
31ICCallPattern::ICCallPattern(uword pc, const Code& code)
32 : object_pool_(ObjectPool::Handle(code.GetObjectPool())),
33 target_pool_index_(-1),
34 data_pool_index_(-1) {
35 ASSERT(code.ContainsInstructionAt(pc));
36 // Last instruction: blx lr.
37 ASSERT(*(reinterpret_cast<uint32_t*>(pc) - 1) == 0xe12fff3e);
38
39 Register reg;
40 uword data_load_end = InstructionPattern::DecodeLoadWordFromPool(
41 pc - 2 * Instr::kInstrSize, &reg, &target_pool_index_);
42 ASSERT(reg == CODE_REG);
43
44 InstructionPattern::DecodeLoadWordFromPool(data_load_end, &reg,
45 &data_pool_index_);
46 ASSERT(reg == R9);
47}
48
49NativeCallPattern::NativeCallPattern(uword pc, const Code& code)
50 : object_pool_(ObjectPool::Handle(code.GetObjectPool())),
51 end_(pc),
52 native_function_pool_index_(-1),
53 target_code_pool_index_(-1) {
54 ASSERT(code.ContainsInstructionAt(pc));
55 // Last instruction: blx lr.
56 ASSERT(*(reinterpret_cast<uint32_t*>(end_) - 1) == 0xe12fff3e);
57
58 Register reg;
59 uword native_function_load_end = InstructionPattern::DecodeLoadWordFromPool(
60 end_ - 2 * Instr::kInstrSize, &reg, &target_code_pool_index_);
61 ASSERT(reg == CODE_REG);
62 InstructionPattern::DecodeLoadWordFromPool(native_function_load_end, &reg,
63 &native_function_pool_index_);
64 ASSERT(reg == R9);
65}
66
67CodePtr NativeCallPattern::target() const {
68 return static_cast<CodePtr>(object_pool_.ObjectAt(target_code_pool_index_));
69}
70
71void NativeCallPattern::set_target(const Code& new_target) const {
72 object_pool_.SetObjectAt(target_code_pool_index_, new_target);
73 // No need to flush the instruction cache, since the code is not modified.
74}
75
76NativeFunction NativeCallPattern::native_function() const {
77 return reinterpret_cast<NativeFunction>(
78 object_pool_.RawValueAt(native_function_pool_index_));
79}
80
81void NativeCallPattern::set_native_function(NativeFunction func) const {
82 object_pool_.SetRawValueAt(native_function_pool_index_,
83 reinterpret_cast<uword>(func));
84}
85
86// Decodes a load sequence ending at 'end' (the last instruction of the load
87// sequence is the instruction before the one at end). Returns a pointer to
88// the first instruction in the sequence. Returns the register being loaded
89// and the loaded object in the output parameters 'reg' and 'obj'
90// respectively.
91uword InstructionPattern::DecodeLoadObject(uword end,
92 const ObjectPool& object_pool,
93 Register* reg,
94 Object* obj) {
95 uword start = 0;
96 Instr* instr = Instr::At(end - Instr::kInstrSize);
97 if ((instr->InstructionBits() & 0xfff00000) == 0xe5900000) {
98 // ldr reg, [reg, #+offset]
99 intptr_t index = 0;
100 start = DecodeLoadWordFromPool(end, reg, &index);
101 *obj = object_pool.ObjectAt(index);
102 } else {
103 intptr_t value = 0;
104 start = DecodeLoadWordImmediate(end, reg, &value);
105 *obj = static_cast<ObjectPtr>(value);
106 }
107 return start;
108}
109
110// Decodes a load sequence ending at 'end' (the last instruction of the load
111// sequence is the instruction before the one at end). Returns a pointer to
112// the first instruction in the sequence. Returns the register being loaded
113// and the loaded immediate value in the output parameters 'reg' and 'value'
114// respectively.
115uword InstructionPattern::DecodeLoadWordImmediate(uword end,
116 Register* reg,
117 intptr_t* value) {
118 uword start = end - Instr::kInstrSize;
119 int32_t instr = Instr::At(start)->InstructionBits();
120 intptr_t imm = 0;
121 if ((instr & 0xfff00000) == 0xe3400000) { // movt reg, #imm_hi
122 imm |= (instr & 0xf0000) << 12;
123 imm |= (instr & 0xfff) << 16;
124 start -= Instr::kInstrSize;
125 instr = Instr::At(start)->InstructionBits();
126 }
127 ASSERT((instr & 0xfff00000) == 0xe3000000); // movw reg, #imm_lo
128 imm |= (instr & 0xf0000) >> 4;
129 imm |= instr & 0xfff;
130 *reg = static_cast<Register>((instr & 0xf000) >> 12);
131 *value = imm;
132 return start;
133}
134
135void InstructionPattern::EncodeLoadWordImmediate(uword end,
136 Register reg,
137 intptr_t value) {
138 uint16_t low16 = value & 0xffff;
139 uint16_t high16 = (value >> 16) & 0xffff;
140
141 // movw reg, #imm_lo
142 uint32_t movw_instr = 0xe3000000;
143 movw_instr |= (low16 >> 12) << 16;
144 movw_instr |= (reg << 12);
145 movw_instr |= (low16 & 0xfff);
146
147 // movt reg, #imm_hi
148 uint32_t movt_instr = 0xe3400000;
149 movt_instr |= (high16 >> 12) << 16;
150 movt_instr |= (reg << 12);
151 movt_instr |= (high16 & 0xfff);
152
153 uint32_t* cursor = reinterpret_cast<uint32_t*>(end);
154 *(--cursor) = movt_instr;
155 *(--cursor) = movw_instr;
156
157#if defined(DEBUG)
158 Register decoded_reg;
159 intptr_t decoded_value;
160 DecodeLoadWordImmediate(end, &decoded_reg, &decoded_value);
161 ASSERT(reg == decoded_reg);
162 ASSERT(value == decoded_value);
163#endif
164}
165
166static bool IsLoadWithOffset(int32_t instr,
167 Register base,
168 intptr_t* offset,
169 Register* dst) {
170 if ((instr & 0xffff0000) == (0xe5900000 | (base << 16))) {
171 // ldr reg, [base, #+offset]
172 *offset = instr & 0xfff;
173 *dst = static_cast<Register>((instr & 0xf000) >> 12);
174 return true;
175 }
176 return false;
177}
178
179// Decodes a load sequence ending at 'end' (the last instruction of the load
180// sequence is the instruction before the one at end). Returns a pointer to
181// the first instruction in the sequence. Returns the register being loaded
182// and the index in the pool being read from in the output parameters 'reg'
183// and 'index' respectively.
184uword InstructionPattern::DecodeLoadWordFromPool(uword end,
185 Register* reg,
186 intptr_t* index) {
187 uword start = end - Instr::kInstrSize;
188 int32_t instr = Instr::At(start)->InstructionBits();
189 intptr_t offset = 0;
190 if (IsLoadWithOffset(instr, PP, &offset, reg)) {
191 // ldr reg, [PP, #+offset]
192 } else {
193 ASSERT((instr & 0xfff00000) == 0xe5900000); // ldr reg, [reg, #+offset]
194 offset = instr & 0xfff;
195 start -= Instr::kInstrSize;
196 instr = Instr::At(start)->InstructionBits();
197 if ((instr & 0xffff0000) == (0xe2850000 | (PP << 16))) {
198 // add reg, pp, operand
199 const intptr_t rot = (instr & 0xf00) >> 7;
200 const intptr_t imm8 = instr & 0xff;
201 offset += (imm8 >> rot) | (imm8 << (32 - rot));
202 *reg = static_cast<Register>((instr & 0xf000) >> 12);
203 } else {
204 ASSERT((instr & 0xffff0000) == (0xe0800000 | (PP << 16)));
205 // add reg, pp, reg
206 intptr_t value = 0;
207 start = DecodeLoadWordImmediate(start, reg, &value);
208 offset += value;
209 }
210 }
211 *index = ObjectPool::IndexFromOffset(offset);
212 return start;
213}
214
215bool DecodeLoadObjectFromPoolOrThread(uword pc, const Code& code, Object* obj) {
216 ASSERT(code.ContainsInstructionAt(pc));
217
218 int32_t instr = Instr::At(pc)->InstructionBits();
219 intptr_t offset;
220 Register dst;
221 if (IsLoadWithOffset(instr, PP, &offset, &dst)) {
222 intptr_t index = ObjectPool::IndexFromOffset(offset);
223 const ObjectPool& pool = ObjectPool::Handle(code.object_pool());
224 if (!pool.IsNull()) {
225 if (pool.TypeAt(index) == ObjectPool::EntryType::kTaggedObject) {
226 *obj = pool.ObjectAt(index);
227 return true;
228 }
229 }
230 } else if (IsLoadWithOffset(instr, THR, &offset, &dst)) {
231 return Thread::ObjectAtOffset(offset, obj);
232 }
233 // TODO(rmacnak): Sequence for loads beyond 12 bits.
234
235 return false;
236}
237
238CodePtr CallPattern::TargetCode() const {
239 return static_cast<CodePtr>(object_pool_.ObjectAt(target_code_pool_index_));
240}
241
242void CallPattern::SetTargetCode(const Code& target_code) const {
243 object_pool_.SetObjectAt(target_code_pool_index_, target_code);
244}
245
246ObjectPtr ICCallPattern::Data() const {
247 return object_pool_.ObjectAt(data_pool_index_);
248}
249
250void ICCallPattern::SetData(const Object& data) const {
251 ASSERT(data.IsArray() || data.IsICData() || data.IsMegamorphicCache());
252 object_pool_.SetObjectAt(data_pool_index_, data);
253}
254
255CodePtr ICCallPattern::TargetCode() const {
256 return static_cast<CodePtr>(object_pool_.ObjectAt(target_pool_index_));
257}
258
259void ICCallPattern::SetTargetCode(const Code& target_code) const {
260 object_pool_.SetObjectAt(target_pool_index_, target_code);
261}
262
263SwitchableCallPatternBase::SwitchableCallPatternBase(const Code& code)
264 : object_pool_(ObjectPool::Handle(code.GetObjectPool())),
265 data_pool_index_(-1),
266 target_pool_index_(-1) {}
267
268ObjectPtr SwitchableCallPatternBase::data() const {
269 return object_pool_.ObjectAt(data_pool_index_);
270}
271
272void SwitchableCallPatternBase::SetData(const Object& data) const {
273 ASSERT(!Object::Handle(object_pool_.ObjectAt(data_pool_index_)).IsCode());
274 object_pool_.SetObjectAt(data_pool_index_, data);
275}
276
277SwitchableCallPattern::SwitchableCallPattern(uword pc, const Code& code)
278 : SwitchableCallPatternBase(code) {
279 ASSERT(code.ContainsInstructionAt(pc));
280 // Last instruction: blx lr.
281 ASSERT(*(reinterpret_cast<uint32_t*>(pc) - 1) == 0xe12fff3e);
282
283 Register reg;
284 uword data_load_end = InstructionPattern::DecodeLoadWordFromPool(
285 pc - Instr::kInstrSize, &reg, &data_pool_index_);
286 ASSERT(reg == R9);
287 InstructionPattern::DecodeLoadWordFromPool(data_load_end - Instr::kInstrSize,
288 &reg, &target_pool_index_);
289 ASSERT(reg == CODE_REG);
290}
291
292CodePtr SwitchableCallPattern::target() const {
293 return static_cast<CodePtr>(object_pool_.ObjectAt(target_pool_index_));
294}
295void SwitchableCallPattern::SetTarget(const Code& target) const {
296 ASSERT(Object::Handle(object_pool_.ObjectAt(target_pool_index_)).IsCode());
297 object_pool_.SetObjectAt(target_pool_index_, target);
298}
299
300BareSwitchableCallPattern::BareSwitchableCallPattern(uword pc, const Code& code)
301 : SwitchableCallPatternBase(code) {
302 ASSERT(code.ContainsInstructionAt(pc));
303 // Last instruction: blx lr.
304 ASSERT(*(reinterpret_cast<uint32_t*>(pc) - 1) == 0xe12fff3e);
305
306 Register reg;
307 uword data_load_end = InstructionPattern::DecodeLoadWordFromPool(
308 pc - Instr::kInstrSize, &reg, &data_pool_index_);
309 ASSERT(reg == R9);
310
311 InstructionPattern::DecodeLoadWordFromPool(data_load_end, &reg,
312 &target_pool_index_);
313 ASSERT(reg == LR);
314}
315
316CodePtr BareSwitchableCallPattern::target() const {
317 const uword pc = object_pool_.RawValueAt(target_pool_index_);
318 CodePtr result = ReversePc::Lookup(IsolateGroup::Current(), pc);
319 if (result != Code::null()) {
320 return result;
321 }
322 result = ReversePc::Lookup(Dart::vm_isolate()->group(), pc);
323 if (result != Code::null()) {
324 return result;
325 }
326 UNREACHABLE();
327}
328
329void BareSwitchableCallPattern::SetTarget(const Code& target) const {
330 ASSERT(object_pool_.TypeAt(target_pool_index_) ==
331 ObjectPool::EntryType::kImmediate);
332 object_pool_.SetRawValueAt(target_pool_index_,
333 target.MonomorphicEntryPoint());
334}
335
336ReturnPattern::ReturnPattern(uword pc) : pc_(pc) {}
337
338bool ReturnPattern::IsValid() const {
339 Instr* bx_lr = Instr::At(pc_);
340 const int32_t B4 = 1 << 4;
341 const int32_t B21 = 1 << 21;
342 const int32_t B24 = 1 << 24;
343 int32_t instruction = (static_cast<int32_t>(AL) << kConditionShift) | B24 |
344 B21 | (0xfff << 8) | B4 |
345 (static_cast<int32_t>(LR) << kRmShift);
346 return bx_lr->InstructionBits() == instruction;
347}
348
349bool PcRelativeCallPattern::IsValid() const {
350 // bl.<cond> <offset>
351 const uint32_t word = *reinterpret_cast<uint32_t*>(pc_);
352 const uint32_t branch = 0x05;
353 const uword type = ((word >> kTypeShift) & ((1 << kTypeBits) - 1));
354 const uword link = ((word >> kLinkShift) & ((1 << kLinkBits) - 1));
355 return type == branch && link == 1;
356}
357
358bool PcRelativeTailCallPattern::IsValid() const {
359 // b.<cond> <offset>
360 const uint32_t word = *reinterpret_cast<uint32_t*>(pc_);
361 const uint32_t branch = 0x05;
362 const uword type = ((word >> kTypeShift) & ((1 << kTypeBits) - 1));
363 const uword link = ((word >> kLinkShift) & ((1 << kLinkBits) - 1));
364 return type == branch && link == 0;
365}
366
367void PcRelativeTrampolineJumpPattern::Initialize() {
368#if !defined(DART_PRECOMPILED_RUNTIME)
369 uint32_t* add_pc =
370 reinterpret_cast<uint32_t*>(pattern_start_ + 2 * Instr::kInstrSize);
371 *add_pc = kAddPcEncoding;
372 set_distance(0);
373#else
374 UNREACHABLE();
375#endif
376}
377
378int32_t PcRelativeTrampolineJumpPattern::distance() {
379#if !defined(DART_PRECOMPILED_RUNTIME)
380 const uword end = pattern_start_ + 2 * Instr::kInstrSize;
381 Register reg;
382 intptr_t value;
383 InstructionPattern::DecodeLoadWordImmediate(end, &reg, &value);
384 value -= kDistanceOffset;
385 ASSERT(reg == TMP);
386 return value;
387#else
388 UNREACHABLE();
389 return 0;
390#endif
391}
392
393void PcRelativeTrampolineJumpPattern::set_distance(int32_t distance) {
394#if !defined(DART_PRECOMPILED_RUNTIME)
395 const uword end = pattern_start_ + 2 * Instr::kInstrSize;
396 InstructionPattern::EncodeLoadWordImmediate(end, TMP,
397 distance + kDistanceOffset);
398#else
399 UNREACHABLE();
400#endif
401}
402
403bool PcRelativeTrampolineJumpPattern::IsValid() const {
404#if !defined(DART_PRECOMPILED_RUNTIME)
405 const uword end = pattern_start_ + 2 * Instr::kInstrSize;
406 Register reg;
407 intptr_t value;
408 InstructionPattern::DecodeLoadWordImmediate(end, &reg, &value);
409
410 uint32_t* add_pc =
411 reinterpret_cast<uint32_t*>(pattern_start_ + 2 * Instr::kInstrSize);
412
413 return reg == TMP && *add_pc == kAddPcEncoding;
414#else
415 UNREACHABLE();
416 return false;
417#endif
418}
419
420intptr_t TypeTestingStubCallPattern::GetSubtypeTestCachePoolIndex() {
421 // Calls to the type testing stubs look like:
422 // ldr R9, ...
423 // ldr R3, [PP+idx]
424 // blx R9
425 // or
426 // ldr R3, [PP+idx]
427 // blx pc+<offset>
428
429 // Ensure the caller of the type testing stub (whose return address is [pc_])
430 // branched via `blx R9` or a pc-relative call.
431 uword pc = pc_ - Instr::kInstrSize;
432 const uint32_t blx_r9 = 0xe12fff39;
433 if (*reinterpret_cast<uint32_t*>(pc) != blx_r9) {
434 PcRelativeCallPattern pattern(pc);
435 RELEASE_ASSERT(pattern.IsValid());
436 }
437
438 const uword load_instr_end = pc;
439
440 Register reg;
441 intptr_t pool_index = -1;
442 InstructionPattern::DecodeLoadWordFromPool(load_instr_end, &reg, &pool_index);
443 ASSERT(reg == R3);
444 return pool_index;
445}
446
447} // namespace dart
448
449#endif // defined TARGET_ARCH_ARM
450