|
12 | 12 | #include "vm/dart_entry.h"
|
13 | 13 | #include "vm/instructions.h"
|
14 | 14 | #include "vm/object.h"
|
| 15 | +#include "vm/object_store.h" |
15 | 16 | #include "vm/raw_object.h"
|
| 17 | +#include "vm/reverse_pc_lookup_cache.h" |
16 | 18 |
|
17 | 19 | namespace dart {
|
18 | 20 |
|
@@ -209,12 +211,41 @@ class PoolPointerCall : public ValueObject {
|
209 | 211 | // load guarded cid load ICData load MegamorphicCache
|
210 | 212 | // load monomorphic target <-> load ICLookup stub -> load MMLookup stub
|
211 | 213 | // call target.entry call stub.entry call stub.entry
|
212 |
| -class SwitchableCall : public ValueObject { |
| 214 | +class SwitchableCallBase : public ValueObject { |
213 | 215 | public:
|
214 |
| - SwitchableCall(uword return_address, const Code& code) |
| 216 | + explicit SwitchableCallBase(const Code& code) |
215 | 217 | : object_pool_(ObjectPool::Handle(code.GetObjectPool())),
|
216 | 218 | target_index_(-1),
|
217 |
| - data_index_(-1) { |
| 219 | + data_index_(-1) {} |
| 220 | + |
| 221 | + intptr_t data_index() const { return data_index_; } |
| 222 | + intptr_t target_index() const { return target_index_; } |
| 223 | + |
| 224 | + RawObject* data() const { return object_pool_.ObjectAt(data_index()); } |
| 225 | + |
| 226 | + void SetData(const Object& data) const { |
| 227 | + ASSERT(!Object::Handle(object_pool_.ObjectAt(data_index())).IsCode()); |
| 228 | + object_pool_.SetObjectAt(data_index(), data); |
| 229 | + // No need to flush the instruction cache, since the code is not modified. |
| 230 | + } |
| 231 | + |
| 232 | + protected: |
| 233 | + ObjectPool& object_pool_; |
| 234 | + intptr_t target_index_; |
| 235 | + intptr_t data_index_; |
| 236 | + |
| 237 | + private: |
| 238 | + DISALLOW_IMPLICIT_CONSTRUCTORS(SwitchableCallBase); |
| 239 | +}; |
| 240 | + |
| 241 | +// See [SwitchableCallBase] for a switchable calls in general. |
| 242 | +// |
| 243 | +// The target slot is always a [Code] object: Either the code of the |
| 244 | +// monomorphic function or a stub code. |
| 245 | +class SwitchableCall : public SwitchableCallBase { |
| 246 | + public: |
| 247 | + SwitchableCall(uword return_address, const Code& code) |
| 248 | + : SwitchableCallBase(code) { |
218 | 249 | uword pc = return_address;
|
219 | 250 |
|
220 | 251 | // callq RCX
|
@@ -277,33 +308,96 @@ class SwitchableCall : public ValueObject {
|
277 | 308 | ASSERT(Object::Handle(object_pool_.ObjectAt(target_index_)).IsCode());
|
278 | 309 | }
|
279 | 310 |
|
280 |
| - intptr_t data_index() const { return data_index_; } |
281 |
| - intptr_t target_index() const { return target_index_; } |
| 311 | + void SetTarget(const Code& target) const { |
| 312 | + ASSERT(Object::Handle(object_pool_.ObjectAt(target_index())).IsCode()); |
| 313 | + object_pool_.SetObjectAt(target_index(), target); |
| 314 | + // No need to flush the instruction cache, since the code is not modified. |
| 315 | + } |
282 | 316 |
|
283 |
| - RawObject* data() const { return object_pool_.ObjectAt(data_index()); } |
284 | 317 | RawCode* target() const {
|
285 | 318 | return reinterpret_cast<RawCode*>(object_pool_.ObjectAt(target_index()));
|
286 | 319 | }
|
| 320 | +}; |
287 | 321 |
|
288 |
| - void SetData(const Object& data) const { |
289 |
| - ASSERT(!Object::Handle(object_pool_.ObjectAt(data_index())).IsCode()); |
290 |
| - object_pool_.SetObjectAt(data_index(), data); |
291 |
| - // No need to flush the instruction cache, since the code is not modified. |
| 322 | +// See [SwitchableCallBase] for a switchable calls in general. |
| 323 | +// |
| 324 | +// The target slot is always a direct entrypoint address: Either the entry point |
| 325 | +// of the monomorphic function or a stub entry point. |
| 326 | +class BareSwitchableCall : public SwitchableCallBase { |
| 327 | + public: |
| 328 | + BareSwitchableCall(uword return_address, const Code& code) |
| 329 | + : SwitchableCallBase(code) { |
| 330 | + object_pool_ = ObjectPool::RawCast( |
| 331 | + Isolate::Current()->object_store()->global_object_pool()); |
| 332 | + |
| 333 | + uword pc = return_address; |
| 334 | + |
| 335 | + // callq RCX |
| 336 | + static int16_t call_pattern[] = { |
| 337 | + 0xff, 0xd1, // |
| 338 | + }; |
| 339 | + if (MatchesPattern(pc, call_pattern, ARRAY_SIZE(call_pattern))) { |
| 340 | + pc -= ARRAY_SIZE(call_pattern); |
| 341 | + } else { |
| 342 | + FATAL1("Failed to decode at %" Px, pc); |
| 343 | + } |
| 344 | + |
| 345 | + // movq RBX, [PP + offset] |
| 346 | + static int16_t load_data_disp8[] = { |
| 347 | + 0x49, 0x8b, 0x5f, -1, // |
| 348 | + }; |
| 349 | + static int16_t load_data_disp32[] = { |
| 350 | + 0x49, 0x8b, 0x9f, -1, -1, -1, -1, |
| 351 | + }; |
| 352 | + if (MatchesPattern(pc, load_data_disp8, ARRAY_SIZE(load_data_disp8))) { |
| 353 | + pc -= ARRAY_SIZE(load_data_disp8); |
| 354 | + data_index_ = IndexFromPPLoadDisp8(pc + 3); |
| 355 | + } else if (MatchesPattern(pc, load_data_disp32, |
| 356 | + ARRAY_SIZE(load_data_disp32))) { |
| 357 | + pc -= ARRAY_SIZE(load_data_disp32); |
| 358 | + data_index_ = IndexFromPPLoadDisp32(pc + 3); |
| 359 | + } else { |
| 360 | + FATAL1("Failed to decode at %" Px, pc); |
| 361 | + } |
| 362 | + ASSERT(!Object::Handle(object_pool_.ObjectAt(data_index_)).IsCode()); |
| 363 | + |
| 364 | + // movq RCX, [PP + offset] |
| 365 | + static int16_t load_code_disp8[] = { |
| 366 | + 0x49, 0x8b, 0x4f, -1, // |
| 367 | + }; |
| 368 | + static int16_t load_code_disp32[] = { |
| 369 | + 0x49, 0x8b, 0x8f, -1, -1, -1, -1, |
| 370 | + }; |
| 371 | + if (MatchesPattern(pc, load_code_disp8, ARRAY_SIZE(load_code_disp8))) { |
| 372 | + pc -= ARRAY_SIZE(load_code_disp8); |
| 373 | + target_index_ = IndexFromPPLoadDisp8(pc + 3); |
| 374 | + } else if (MatchesPattern(pc, load_code_disp32, |
| 375 | + ARRAY_SIZE(load_code_disp32))) { |
| 376 | + pc -= ARRAY_SIZE(load_code_disp32); |
| 377 | + target_index_ = IndexFromPPLoadDisp32(pc + 3); |
| 378 | + } else { |
| 379 | + FATAL1("Failed to decode at %" Px, pc); |
| 380 | + } |
| 381 | + ASSERT(object_pool_.TypeAt(target_index_) == ObjectPool::kImmediate); |
292 | 382 | }
|
293 | 383 |
|
294 | 384 | void SetTarget(const Code& target) const {
|
295 |
| - ASSERT(Object::Handle(object_pool_.ObjectAt(target_index())).IsCode()); |
296 |
| - object_pool_.SetObjectAt(target_index(), target); |
297 |
| - // No need to flush the instruction cache, since the code is not modified. |
| 385 | + ASSERT(object_pool_.TypeAt(target_index()) == ObjectPool::kImmediate); |
| 386 | + object_pool_.SetRawValueAt(target_index(), target.MonomorphicEntryPoint()); |
298 | 387 | }
|
299 | 388 |
|
300 |
| - protected: |
301 |
| - const ObjectPool& object_pool_; |
302 |
| - intptr_t target_index_; |
303 |
| - intptr_t data_index_; |
304 |
| - |
305 |
| - private: |
306 |
| - DISALLOW_IMPLICIT_CONSTRUCTORS(SwitchableCall); |
| 389 | + RawCode* target() const { |
| 390 | + const uword pc = object_pool_.RawValueAt(target_index()); |
| 391 | + auto rct = Isolate::Current()->reverse_pc_lookup_cache(); |
| 392 | + if (rct->Contains(pc)) { |
| 393 | + return rct->Lookup(pc); |
| 394 | + } |
| 395 | + rct = Dart::vm_isolate()->reverse_pc_lookup_cache(); |
| 396 | + if (rct->Contains(pc)) { |
| 397 | + return rct->Lookup(pc); |
| 398 | + } |
| 399 | + UNREACHABLE(); |
| 400 | + } |
307 | 401 | };
|
308 | 402 |
|
309 | 403 | RawCode* CodePatcher::GetStaticCallTargetAt(uword return_address,
|
@@ -360,23 +454,39 @@ void CodePatcher::PatchSwitchableCallAt(uword return_address,
|
360 | 454 | const Object& data,
|
361 | 455 | const Code& target) {
|
362 | 456 | ASSERT(caller_code.ContainsInstructionAt(return_address));
|
363 |
| - SwitchableCall call(return_address, caller_code); |
364 |
| - call.SetData(data); |
365 |
| - call.SetTarget(target); |
| 457 | + if (FLAG_precompiled_mode && FLAG_use_bare_instructions) { |
| 458 | + BareSwitchableCall call(return_address, caller_code); |
| 459 | + call.SetData(data); |
| 460 | + call.SetTarget(target); |
| 461 | + } else { |
| 462 | + SwitchableCall call(return_address, caller_code); |
| 463 | + call.SetData(data); |
| 464 | + call.SetTarget(target); |
| 465 | + } |
366 | 466 | }
|
367 | 467 |
|
368 | 468 | RawCode* CodePatcher::GetSwitchableCallTargetAt(uword return_address,
|
369 | 469 | const Code& caller_code) {
|
370 | 470 | ASSERT(caller_code.ContainsInstructionAt(return_address));
|
371 |
| - SwitchableCall call(return_address, caller_code); |
372 |
| - return call.target(); |
| 471 | + if (FLAG_precompiled_mode && FLAG_use_bare_instructions) { |
| 472 | + BareSwitchableCall call(return_address, caller_code); |
| 473 | + return call.target(); |
| 474 | + } else { |
| 475 | + SwitchableCall call(return_address, caller_code); |
| 476 | + return call.target(); |
| 477 | + } |
373 | 478 | }
|
374 | 479 |
|
375 | 480 | RawObject* CodePatcher::GetSwitchableCallDataAt(uword return_address,
|
376 | 481 | const Code& caller_code) {
|
377 | 482 | ASSERT(caller_code.ContainsInstructionAt(return_address));
|
378 |
| - SwitchableCall call(return_address, caller_code); |
379 |
| - return call.data(); |
| 483 | + if (FLAG_precompiled_mode && FLAG_use_bare_instructions) { |
| 484 | + BareSwitchableCall call(return_address, caller_code); |
| 485 | + return call.data(); |
| 486 | + } else { |
| 487 | + SwitchableCall call(return_address, caller_code); |
| 488 | + return call.data(); |
| 489 | + } |
380 | 490 | }
|
381 | 491 |
|
382 | 492 | void CodePatcher::PatchNativeCallAt(uword return_address,
|
|
0 commit comments