@@ -54,24 +54,6 @@ private extension Instruction {
5454 }
5555}
5656
57- private extension Argument {
58- func verify( _ context: FunctionPassContext ) {
59- if let phi = Phi ( self ) , phi. value. ownership == . guaranteed {
60- var forwardingBorrowedFromFound = false
61- for use in phi. value. uses {
62- require ( use. instruction is BorrowedFromInst ,
63- " guaranteed phi: \( self ) \n has non borrowed-from use: \( use) " )
64- if use. index == 0 {
65- require ( !forwardingBorrowedFromFound, " phi \( self ) has multiple forwarding borrowed-from uses " )
66- forwardingBorrowedFromFound = true
67- }
68- }
69- require ( forwardingBorrowedFromFound,
70- " missing forwarding borrowed-from user of guaranteed phi \( phi) " )
71- }
72- }
73- }
74-
7557extension BorrowedFromInst : VerifiableInstruction {
7658 func verify( _ context: FunctionPassContext ) {
7759 var computedEVs = Stack < Value > ( context)
@@ -92,121 +74,20 @@ extension BorrowedFromInst : VerifiableInstruction {
9274 }
9375}
9476
95- extension LoadBorrowInst : VerifiableInstruction {
77+ private extension Argument {
9678 func verify( _ context: FunctionPassContext ) {
97- if isUnchecked {
98- return
99- }
100-
101- var mutatingInstructions = MutatingUsesWalker ( context)
102- defer { mutatingInstructions. deinitialize ( ) }
103-
104- mutatingInstructions. findMutatingUses ( of: self . address)
105- mutatingInstructions. verifyNoMutatingUsesInLiverange ( of: self )
106- }
107- }
108-
109- // Used to check if any instruction is mutating the memory location within the liverange of a `load_borrow`.
110- // Note that it is not checking if an instruction _may_ mutate the memory, but it's checking if any instruction
111- // _definitely_ will mutate the memory.
112- // Otherwise the risk would be too big for false alarms. It also means that this verification is not perfect and
113- // might miss some subtle violations.
114- private struct MutatingUsesWalker : AddressDefUseWalker {
115- let context : FunctionPassContext
116- var mutatingInstructions : InstructionSet
117-
118- init ( _ context: FunctionPassContext ) {
119- self . context = context
120- self . mutatingInstructions = InstructionSet ( context)
121- }
122-
123- mutating func deinitialize( ) {
124- self . mutatingInstructions. deinitialize ( )
125- }
126-
127- mutating func findMutatingUses( of address: Value ) {
128- _ = walkDownUses ( ofAddress: address, path: UnusedWalkingPath ( ) )
129- }
130-
131- mutating func verifyNoMutatingUsesInLiverange( of load: LoadBorrowInst ) {
132- // The liverange of a `load_borrow` can end in a branch instruction. In such cases we handle the re-borrow liveranges
133- // (starting at the `borrowed_from` in the successor block) separately.
134- // This worklist contains the starts of the individual linear liveranges,
135- // i.e. `load_borrow` and `borrowed_from` instructions.
136- var linearLiveranges = SpecificInstructionWorklist < SingleValueInstruction > ( context)
137- defer { linearLiveranges. deinitialize ( ) }
138-
139- linearLiveranges. pushIfNotVisited ( load)
140- while let startInst = linearLiveranges. pop ( ) {
141- verifyNoMutatingUsesInLinearLiverange ( of: startInst)
142-
143- for use in startInst. uses {
144- if let phi = Phi ( using: use) {
145- linearLiveranges. pushIfNotVisited ( phi. borrowedFrom!)
146- }
147- }
148- }
149- }
150-
151- private mutating func verifyNoMutatingUsesInLinearLiverange( of startInst: SingleValueInstruction ) {
152- assert ( startInst is LoadBorrowInst || startInst is BorrowedFromInst )
153-
154- var instWorklist = InstructionWorklist ( context)
155- defer { instWorklist. deinitialize ( ) }
156-
157- // It would be good enough to only consider `end_borrow`s (and branches), but adding all users doesn't harm.
158- for use in startInst. uses {
159- instWorklist. pushPredecessors ( of: use. instruction, ignoring: startInst)
160- }
161-
162- while let inst = instWorklist. pop ( ) {
163- require ( !mutatingInstructions. contains ( inst) ,
164- " Load borrow invalidated by a local write " , atInstruction: inst)
165- instWorklist. pushPredecessors ( of: inst, ignoring: startInst)
166- }
167- }
168-
169- mutating func leafUse( address: Operand , path: UnusedWalkingPath ) -> WalkResult {
170- if address. isMutatedAddress {
171- mutatingInstructions. insert ( address. instruction)
172- }
173- return . continueWalk
174- }
175- }
176-
177- private extension Operand {
178- // Returns true if the operand's instruction is definitely writing to the operand's address value.
179- var isMutatedAddress : Bool {
180- assert ( value. type. isAddress)
181- switch instruction {
182- case let store as StoringInstruction :
183- return self == store. destinationOperand
184- case let copy as SourceDestAddrInstruction :
185- if self == copy. destinationOperand {
186- return true
187- } else if self == copy. sourceOperand && copy. isTakeOfSrc {
188- return true
189- }
190- return false
191- case let load as LoadInst :
192- return load. loadOwnership == . take
193- case let partialApply as PartialApplyInst :
194- if !partialApply. isOnStack,
195- let convention = partialApply. convention ( of: self )
196- {
197- switch convention {
198- case . indirectIn, . indirectInGuaranteed:
199- // Such operands are consumed by the `partial_apply` and therefore cound as "written".
200- return true
201- default :
202- return false
79+ if let phi = Phi ( self ) , phi. value. ownership == . guaranteed {
80+ var forwardingBorrowedFromFound = false
81+ for use in phi. value. uses {
82+ require ( use. instruction is BorrowedFromInst ,
83+ " guaranteed phi: \( self ) \n has non borrowed-from use: \( use) " )
84+ if use. index == 0 {
85+ require ( !forwardingBorrowedFromFound, " phi \( self ) has multiple forwarding borrowed-from uses " )
86+ forwardingBorrowedFromFound = true
20387 }
20488 }
205- return false
206- case is DestroyAddrInst , is IsUniqueInst :
207- return true
208- default :
209- return false
89+ require ( forwardingBorrowedFromFound,
90+ " missing forwarding borrowed-from user of guaranteed phi \( phi) " )
21091 }
21192 }
21293}
0 commit comments