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