|
36 | 36 | #ifndef SWIFT_SIL_MEMACCESSUTILS_H
|
37 | 37 | #define SWIFT_SIL_MEMACCESSUTILS_H
|
38 | 38 |
|
| 39 | +#include "swift/SIL/ApplySite.h" |
39 | 40 | #include "swift/SIL/InstructionUtils.h"
|
40 | 41 | #include "swift/SIL/SILArgument.h"
|
41 | 42 | #include "swift/SIL/SILBasicBlock.h"
|
| 43 | +#include "swift/SIL/SILGlobalVariable.h" |
42 | 44 | #include "swift/SIL/SILInstruction.h"
|
43 | 45 | #include "llvm/ADT/DenseMap.h"
|
44 | 46 |
|
@@ -117,10 +119,6 @@ class AccessedStorage {
|
117 | 119 |
|
118 | 120 | static const char *getKindName(Kind k);
|
119 | 121 |
|
120 |
| - /// If the given address source is an identified access base, return the kind |
121 |
| - /// of access base. Otherwise, return Unidentified. |
122 |
| - static AccessedStorage::Kind classify(SILValue base); |
123 |
| - |
124 | 122 | /// Directly create an AccessedStorage for class property access.
|
125 | 123 | static AccessedStorage forClass(SILValue object, unsigned propertyIndex) {
|
126 | 124 | AccessedStorage storage;
|
@@ -406,6 +404,58 @@ template <> struct DenseMapInfo<swift::AccessedStorage> {
|
406 | 404 |
|
407 | 405 | namespace swift {
|
408 | 406 |
|
| 407 | +/// Abstract CRTP class for a visitor passed to \c visitAccessUseDefChain. |
| 408 | +template<typename Impl, typename Result = void> |
| 409 | +class AccessUseDefChainVisitor { |
| 410 | +protected: |
| 411 | + Impl &asImpl() { |
| 412 | + return static_cast<Impl&>(*this); |
| 413 | + } |
| 414 | +public: |
| 415 | + // Subclasses can provide a method for any identified access base: |
| 416 | + // Result visitBase(SILValue base, AccessedStorage::Kind kind); |
| 417 | + |
| 418 | + // Visitors for specific identified access kinds. These default to calling out |
| 419 | + // to visitIdentified. |
| 420 | + |
| 421 | + Result visitClassAccess(RefElementAddrInst *field) { |
| 422 | + return asImpl().visitBase(field, AccessedStorage::Class); |
| 423 | + } |
| 424 | + Result visitArgumentAccess(SILFunctionArgument *arg) { |
| 425 | + return asImpl().visitBase(arg, AccessedStorage::Argument); |
| 426 | + } |
| 427 | + Result visitBoxAccess(AllocBoxInst *box) { |
| 428 | + return asImpl().visitBase(box, AccessedStorage::Box); |
| 429 | + } |
| 430 | + /// The argument may be either a GlobalAddrInst or the ApplyInst for a global accessor function. |
| 431 | + Result visitGlobalAccess(SILValue global) { |
| 432 | + return asImpl().visitBase(global, AccessedStorage::Global); |
| 433 | + } |
| 434 | + Result visitYieldAccess(BeginApplyResult *yield) { |
| 435 | + return asImpl().visitBase(yield, AccessedStorage::Yield); |
| 436 | + } |
| 437 | + Result visitStackAccess(AllocStackInst *stack) { |
| 438 | + return asImpl().visitBase(stack, AccessedStorage::Stack); |
| 439 | + } |
| 440 | + Result visitNestedAccess(BeginAccessInst *access) { |
| 441 | + return asImpl().visitBase(access, AccessedStorage::Nested); |
| 442 | + } |
| 443 | + |
| 444 | + // Visitors for unidentified base values. |
| 445 | + |
| 446 | + Result visitUnidentified(SILValue base) { |
| 447 | + return asImpl().visitBase(base, AccessedStorage::Unidentified); |
| 448 | + } |
| 449 | + |
| 450 | + // Subclasses must provide implementations to visit non-access bases |
| 451 | + // and phi arguments, and for incomplete projections from the access: |
| 452 | + // void visitNonAccess(SILValue base); |
| 453 | + // void visitPhi(SILPhiArgument *phi); |
| 454 | + // void visitIncomplete(SILValue projectedAddr, SILValue parentAddr); |
| 455 | + |
| 456 | + Result visit(SILValue sourceAddr); |
| 457 | +}; |
| 458 | + |
409 | 459 | /// Given an address accessed by an instruction that reads or modifies
|
410 | 460 | /// memory, return an AccessedStorage object that identifies the formal access.
|
411 | 461 | ///
|
@@ -468,6 +518,192 @@ void visitAccessedAddress(SILInstruction *I,
|
468 | 518 | /// instruction following this begin_access was not also erased.
|
469 | 519 | SILBasicBlock::iterator removeBeginAccess(BeginAccessInst *beginAccess);
|
470 | 520 |
|
| 521 | +/// Return true if the given address value is produced by a special address |
| 522 | +/// producer that is only used for local initialization, not formal access. |
| 523 | +bool isAddressForLocalInitOnly(SILValue sourceAddr); |
| 524 | +/// Return true if the given apply invokes a global addressor defined in another |
| 525 | +/// module. |
| 526 | +bool isExternalGlobalAddressor(ApplyInst *AI); |
| 527 | +/// Return true if the given StructExtractInst extracts the RawPointer from |
| 528 | +/// Unsafe[Mutable]Pointer. |
| 529 | +bool isUnsafePointerExtraction(StructExtractInst *SEI); |
| 530 | +/// Given a block argument address base, check if it is actually a box projected |
| 531 | +/// from a switch_enum. This is a valid pattern at any SIL stage resulting in a |
| 532 | +/// block-type phi. In later SIL stages, the optimizer may form address-type |
| 533 | +/// phis, causing this assert if called on those cases. |
| 534 | +void checkSwitchEnumBlockArg(SILPhiArgument *arg); |
| 535 | + |
| 536 | +template<typename Impl, typename Result> |
| 537 | +Result AccessUseDefChainVisitor<Impl, Result>::visit(SILValue sourceAddr) { |
| 538 | + // Handle immediately-identifiable instructions. |
| 539 | + while (true) { |
| 540 | + switch (sourceAddr->getKind()) { |
| 541 | + // An AllocBox is a fully identified memory location. |
| 542 | + case ValueKind::AllocBoxInst: |
| 543 | + return asImpl().visitBoxAccess(cast<AllocBoxInst>(sourceAddr)); |
| 544 | + // An AllocStack is a fully identified memory location, which may occur |
| 545 | + // after inlining code already subjected to stack promotion. |
| 546 | + case ValueKind::AllocStackInst: |
| 547 | + return asImpl().visitStackAccess(cast<AllocStackInst>(sourceAddr)); |
| 548 | + case ValueKind::GlobalAddrInst: |
| 549 | + return asImpl().visitGlobalAccess(sourceAddr); |
| 550 | + case ValueKind::ApplyInst: { |
| 551 | + FullApplySite apply(cast<ApplyInst>(sourceAddr)); |
| 552 | + if (auto *funcRef = apply.getReferencedFunctionOrNull()) { |
| 553 | + if (getVariableOfGlobalInit(funcRef)) { |
| 554 | + return asImpl().visitGlobalAccess(sourceAddr); |
| 555 | + } |
| 556 | + } |
| 557 | + // Try to classify further below. |
| 558 | + break; |
| 559 | + } |
| 560 | + case ValueKind::RefElementAddrInst: |
| 561 | + return asImpl().visitClassAccess(cast<RefElementAddrInst>(sourceAddr)); |
| 562 | + // A yield is effectively a nested access, enforced independently in |
| 563 | + // the caller and callee. |
| 564 | + case ValueKind::BeginApplyResult: |
| 565 | + return asImpl().visitYieldAccess(cast<BeginApplyResult>(sourceAddr)); |
| 566 | + // A function argument is effectively a nested access, enforced |
| 567 | + // independently in the caller and callee. |
| 568 | + case ValueKind::SILFunctionArgument: |
| 569 | + return asImpl().visitArgumentAccess(cast<SILFunctionArgument>(sourceAddr)); |
| 570 | + // View the outer begin_access as a separate location because nested |
| 571 | + // accesses do not conflict with each other. |
| 572 | + case ValueKind::BeginAccessInst: |
| 573 | + return asImpl().visitNestedAccess(cast<BeginAccessInst>(sourceAddr)); |
| 574 | + default: |
| 575 | + // Try to classify further below. |
| 576 | + break; |
| 577 | + } |
| 578 | + |
| 579 | + // If the sourceAddr producer cannot immediately be classified, follow the |
| 580 | + // use-def chain of sourceAddr, box, or RawPointer producers. |
| 581 | + assert(sourceAddr->getType().isAddress() |
| 582 | + || isa<SILBoxType>(sourceAddr->getType().getASTType()) |
| 583 | + || isa<BuiltinRawPointerType>(sourceAddr->getType().getASTType())); |
| 584 | + |
| 585 | + // Handle other unidentified address sources. |
| 586 | + switch (sourceAddr->getKind()) { |
| 587 | + default: |
| 588 | + if (isAddressForLocalInitOnly(sourceAddr)) |
| 589 | + return asImpl().visitUnidentified(sourceAddr); |
| 590 | + return asImpl().visitNonAccess(sourceAddr); |
| 591 | + |
| 592 | + case ValueKind::SILUndef: |
| 593 | + return asImpl().visitUnidentified(sourceAddr); |
| 594 | + |
| 595 | + case ValueKind::ApplyInst: |
| 596 | + if (isExternalGlobalAddressor(cast<ApplyInst>(sourceAddr))) |
| 597 | + return asImpl().visitUnidentified(sourceAddr); |
| 598 | + |
| 599 | + // Don't currently allow any other calls to return an accessed address. |
| 600 | + return asImpl().visitNonAccess(sourceAddr); |
| 601 | + |
| 602 | + case ValueKind::StructExtractInst: |
| 603 | + // Handle nested access to a KeyPath projection. The projection itself |
| 604 | + // uses a Builtin. However, the returned UnsafeMutablePointer may be |
| 605 | + // converted to an address and accessed via an inout argument. |
| 606 | + if (isUnsafePointerExtraction(cast<StructExtractInst>(sourceAddr))) |
| 607 | + return asImpl().visitUnidentified(sourceAddr); |
| 608 | + return asImpl().visitNonAccess(sourceAddr); |
| 609 | + |
| 610 | + case ValueKind::SILPhiArgument: { |
| 611 | + auto *phiArg = cast<SILPhiArgument>(sourceAddr); |
| 612 | + if (phiArg->isPhiArgument()) { |
| 613 | + return asImpl().visitPhi(phiArg); |
| 614 | + } |
| 615 | + |
| 616 | + // A non-phi block argument may be a box value projected out of |
| 617 | + // switch_enum. Address-type block arguments are not allowed. |
| 618 | + if (sourceAddr->getType().isAddress()) |
| 619 | + return asImpl().visitNonAccess(sourceAddr); |
| 620 | + |
| 621 | + checkSwitchEnumBlockArg(cast<SILPhiArgument>(sourceAddr)); |
| 622 | + return asImpl().visitUnidentified(sourceAddr); |
| 623 | + } |
| 624 | + // Load a box from an indirect payload of an opaque enum. |
| 625 | + // We must have peeked past the project_box earlier in this loop. |
| 626 | + // (the indirectness makes it a box, the load is for address-only). |
| 627 | + // |
| 628 | + // %payload_adr = unchecked_take_enum_data_addr %enum : $*Enum, #Enum.case |
| 629 | + // %box = load [take] %payload_adr : $*{ var Enum } |
| 630 | + // |
| 631 | + // FIXME: this case should go away with opaque values. |
| 632 | + // |
| 633 | + // Otherwise return invalid AccessedStorage. |
| 634 | + case ValueKind::LoadInst: |
| 635 | + if (sourceAddr->getType().is<SILBoxType>()) { |
| 636 | + SILValue operAddr = cast<LoadInst>(sourceAddr)->getOperand(); |
| 637 | + assert(isa<UncheckedTakeEnumDataAddrInst>(operAddr)); |
| 638 | + return asImpl().visitIncomplete(sourceAddr, operAddr); |
| 639 | + } |
| 640 | + return asImpl().visitNonAccess(sourceAddr); |
| 641 | + |
| 642 | + // ref_tail_addr project an address from a reference. |
| 643 | + // This is a valid address producer for nested @inout argument |
| 644 | + // access, but it is never used for formal access of identified objects. |
| 645 | + case ValueKind::RefTailAddrInst: |
| 646 | + return asImpl().visitUnidentified(sourceAddr); |
| 647 | + |
| 648 | + // Inductive single-operand cases: |
| 649 | + // Look through address casts to find the source address. |
| 650 | + case ValueKind::MarkUninitializedInst: |
| 651 | + case ValueKind::OpenExistentialAddrInst: |
| 652 | + case ValueKind::UncheckedAddrCastInst: |
| 653 | + // Inductive cases that apply to any type. |
| 654 | + case ValueKind::CopyValueInst: |
| 655 | + case ValueKind::MarkDependenceInst: |
| 656 | + // Look through a project_box to identify the underlying alloc_box as the |
| 657 | + // accesed object. It must be possible to reach either the alloc_box or the |
| 658 | + // containing enum in this loop, only looking through simple value |
| 659 | + // propagation such as copy_value. |
| 660 | + case ValueKind::ProjectBoxInst: |
| 661 | + // Handle project_block_storage just like project_box. |
| 662 | + case ValueKind::ProjectBlockStorageInst: |
| 663 | + // Look through begin_borrow in case a local box is borrowed. |
| 664 | + case ValueKind::BeginBorrowInst: |
| 665 | + return asImpl().visitIncomplete(sourceAddr, |
| 666 | + cast<SingleValueInstruction>(sourceAddr)->getOperand(0)); |
| 667 | + |
| 668 | + // Access to a Builtin.RawPointer. Treat this like the inductive cases |
| 669 | + // above because some RawPointers originate from identified locations. See |
| 670 | + // the special case for global addressors, which return RawPointer, |
| 671 | + // above. AddressToPointer is also handled because it results from inlining a |
| 672 | + // global addressor without folding the AddressToPointer->PointerToAddress. |
| 673 | + // |
| 674 | + // If the inductive search does not find a valid addressor, it will |
| 675 | + // eventually reach the default case that returns in invalid location. This |
| 676 | + // is correct for RawPointer because, although accessing a RawPointer is |
| 677 | + // legal SIL, there is no way to guarantee that it doesn't access class or |
| 678 | + // global storage, so returning a valid unidentified storage object would be |
| 679 | + // incorrect. It is the caller's responsibility to know that formal access |
| 680 | + // to such a location can be safely ignored. |
| 681 | + // |
| 682 | + // For example: |
| 683 | + // |
| 684 | + // - KeyPath Builtins access RawPointer. However, the caller can check |
| 685 | + // that the access `isFromBuilin` and ignore the storage. |
| 686 | + // |
| 687 | + // - lldb generates RawPointer access for debugger variables, but SILGen |
| 688 | + // marks debug VarDecl access as 'Unsafe' and SIL passes don't need the |
| 689 | + // AccessedStorage for 'Unsafe' access. |
| 690 | + case ValueKind::PointerToAddressInst: |
| 691 | + case ValueKind::AddressToPointerInst: |
| 692 | + return asImpl().visitIncomplete(sourceAddr, |
| 693 | + cast<SingleValueInstruction>(sourceAddr)->getOperand(0)); |
| 694 | + |
| 695 | + // Address-to-address subobject projections. |
| 696 | + case ValueKind::StructElementAddrInst: |
| 697 | + case ValueKind::TupleElementAddrInst: |
| 698 | + case ValueKind::UncheckedTakeEnumDataAddrInst: |
| 699 | + case ValueKind::TailAddrInst: |
| 700 | + case ValueKind::IndexAddrInst: |
| 701 | + return asImpl().visitIncomplete(sourceAddr, |
| 702 | + cast<SingleValueInstruction>(sourceAddr)->getOperand(0)); |
| 703 | + } |
| 704 | + }; |
| 705 | +} |
| 706 | + |
471 | 707 | } // end namespace swift
|
472 | 708 |
|
473 | 709 | #endif
|
0 commit comments