Skip to content

Commit 1c459b6

Browse files
committed
[move-only-addr] Wire up the move only address checker to Field Sensitive Pruned Liveness Boundary.
This let me fix an issue around hoisting destroy_addr/store [assign] when working in multi-block cfgs. I also added some .sil tests.
1 parent 0e7b04a commit 1c459b6

9 files changed

+2749
-871
lines changed

include/swift/SIL/FieldSensitivePrunedLiveness.h

Lines changed: 421 additions & 57 deletions
Large diffs are not rendered by default.

lib/SIL/Utils/FieldSensitivePrunedLiveness.cpp

Lines changed: 669 additions & 101 deletions
Large diffs are not rendered by default.

lib/SILOptimizer/Mandatory/MoveOnlyAddressChecker.cpp

Lines changed: 935 additions & 669 deletions
Large diffs are not rendered by default.

lib/SILOptimizer/Mandatory/MoveOnlyDiagnostics.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,21 @@ void DiagnosticEmitter::emitAddressDiagnostic(MarkMustCheckInst *markedValue,
294294
}
295295

296296
if (isInOutEndOfFunction) {
297+
if (auto *pbi = dyn_cast<ProjectBoxInst>(markedValue->getOperand())) {
298+
if (auto *fArg = dyn_cast<SILFunctionArgument>(pbi->getOperand())) {
299+
if (fArg->isClosureCapture()) {
300+
diagnose(
301+
astContext,
302+
markedValue->getDefiningInstruction()->getLoc().getSourceLoc(),
303+
diag::
304+
sil_moveonlychecker_inout_not_reinitialized_before_end_of_closure,
305+
varName);
306+
diagnose(astContext, violatingUse->getLoc().getSourceLoc(),
307+
diag::sil_moveonlychecker_consuming_use_here);
308+
return;
309+
}
310+
}
311+
}
297312
if (auto *fArg = dyn_cast<SILFunctionArgument>(markedValue->getOperand())) {
298313
if (fArg->isClosureCapture()) {
299314
diagnose(
Lines changed: 344 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,344 @@
1+
// RUN: %target-sil-opt -sil-move-only-address-checker -enable-experimental-move-only -enable-sil-verify-all %s | %FileCheck %s
2+
3+
sil_stage raw
4+
5+
import Swift
6+
7+
public class CopyableKlass {}
8+
9+
@_moveOnly
10+
public class Klass {
11+
var intField: Int
12+
var k: Klass?
13+
init()
14+
}
15+
16+
sil @get_klass : $@convention(thin) () -> @owned Klass
17+
sil @nonConsumingUseKlass : $@convention(thin) (@guaranteed Klass) -> ()
18+
19+
sil @copyableClassConsume : $@convention(thin) (@owned CopyableKlass) -> () // user: %24
20+
sil @copyableClassUseMoveOnlyWithoutEscaping : $@convention(thin) (@guaranteed CopyableKlass) -> () // user: %16
21+
22+
@_moveOnly
23+
public struct NonTrivialStruct {
24+
var k = Klass()
25+
var copyableK = CopyableKlass()
26+
var nonTrivialStruct2 = NonTrivialStruct2()
27+
}
28+
29+
@_moveOnly
30+
public struct NonTrivialStructPair {
31+
var lhs: NonTrivialStruct
32+
var rhs: NonTrivialStruct
33+
}
34+
35+
@_moveOnly
36+
public struct NonTrivialStruct2 {
37+
var copyableKlass = CopyableKlass()
38+
}
39+
40+
@_moveOnly struct MyBufferView<T> {
41+
var ptr: UnsafeBufferPointer<T>
42+
}
43+
44+
///////////
45+
// Tests //
46+
///////////
47+
48+
// CHECK-LABEL: sil [ossa] @simpleInitReturn : $@convention(thin) (@owned NonTrivialStruct) -> @owned NonTrivialStruct {
49+
// CHECK: bb0([[ARG:%.*]] : @owned $NonTrivialStruct):
50+
// CHECK-NEXT: [[ALLOC_STACK:%.*]] = alloc_stack [lexical]
51+
// CHECK-NEXT: store [[ARG]] to [init] [[ALLOC_STACK]]
52+
// CHECK-NEXT: [[RESULT:%.*]] = load [take] [[ALLOC_STACK]]
53+
// CHECK-NEXT: dealloc_stack [[ALLOC_STACK]]
54+
// CHECK-NEXT: return [[RESULT]]
55+
// CHECK: } // end sil function 'simpleInitReturn'
56+
sil [ossa] @simpleInitReturn : $@convention(thin) (@owned NonTrivialStruct) -> @owned NonTrivialStruct {
57+
bb0(%0 : @owned $NonTrivialStruct):
58+
%1 = alloc_stack [lexical] $NonTrivialStruct
59+
%2 = mark_must_check [no_implicit_copy] %1 : $*NonTrivialStruct
60+
store %0 to [init] %2 : $*NonTrivialStruct
61+
%3 = load [copy] %2 : $*NonTrivialStruct
62+
destroy_addr %2 : $*NonTrivialStruct
63+
dealloc_stack %1 : $*NonTrivialStruct
64+
return %3 : $NonTrivialStruct
65+
}
66+
67+
sil [ossa] @simpleInitReturn2 : $@convention(thin) (@owned NonTrivialStruct, @owned NonTrivialStruct) -> @owned NonTrivialStructPair {
68+
bb0(%arg1 : @owned $NonTrivialStruct, %arg2 : @owned $NonTrivialStruct):
69+
%1 = alloc_stack [lexical] $NonTrivialStruct
70+
%2 = mark_must_check [no_implicit_copy] %1 : $*NonTrivialStruct
71+
store %arg1 to [init] %2 : $*NonTrivialStruct
72+
%3 = load [copy] %2 : $*NonTrivialStruct
73+
destroy_addr %2 : $*NonTrivialStruct
74+
75+
store %arg2 to [init] %2 : $*NonTrivialStruct
76+
%3a = load [copy] %2 : $*NonTrivialStruct
77+
destroy_addr %2 : $*NonTrivialStruct
78+
dealloc_stack %1 : $*NonTrivialStruct
79+
%result = struct $NonTrivialStructPair(%3 : $NonTrivialStruct, %3a : $NonTrivialStruct)
80+
return %result : $NonTrivialStructPair
81+
}
82+
83+
// CHECK-LABEL: sil [ossa] @simpleInitReturnMoveOnlyField : $@convention(thin) (@owned NonTrivialStruct) -> @owned Klass {
84+
// CHECK: bb0([[ARG:%.*]] : @owned $NonTrivialStruct):
85+
// CHECK-NEXT: [[ALLOC_STACK:%.*]] = alloc_stack [lexical]
86+
// CHECK-NEXT: store [[ARG]] to [init] [[ALLOC_STACK]]
87+
// CHECK-NEXT: [[GEP_1:%.*]] = struct_element_addr [[ALLOC_STACK]] : $*NonTrivialStruct, #NonTrivialStruct.copyableK
88+
// CHECK-NEXT: [[GEP_2:%.*]] = struct_element_addr [[ALLOC_STACK]] : $*NonTrivialStruct, #NonTrivialStruct.nonTrivialStruct2
89+
// CHECK-NEXT: destroy_addr [[GEP_1]]
90+
// CHECK-NEXT: destroy_addr [[GEP_2]]
91+
// CHECK-NEXT: [[GEP_3:%.*]] = struct_element_addr [[ALLOC_STACK]] : $*NonTrivialStruct, #NonTrivialStruct.k
92+
// CHECK-NEXT: [[RESULT:%.*]] = load [take] [[GEP_3]]
93+
// CHECK-NEXT: dealloc_stack [[ALLOC_STACK]]
94+
// CHECK-NEXT: return [[RESULT]]
95+
// CHECK: } // end sil function 'simpleInitReturnMoveOnlyField'
96+
sil [ossa] @simpleInitReturnMoveOnlyField : $@convention(thin) (@owned NonTrivialStruct) -> @owned Klass {
97+
bb0(%0 : @owned $NonTrivialStruct):
98+
%1 = alloc_stack [lexical] $NonTrivialStruct
99+
%2 = mark_must_check [no_implicit_copy] %1 : $*NonTrivialStruct
100+
store %0 to [init] %2 : $*NonTrivialStruct
101+
%3a = struct_element_addr %2 : $*NonTrivialStruct, #NonTrivialStruct.k
102+
%3 = load [copy] %3a : $*Klass
103+
destroy_addr %2 : $*NonTrivialStruct
104+
dealloc_stack %1 : $*NonTrivialStruct
105+
return %3 : $Klass
106+
}
107+
108+
// CHECK-LABEL: sil [ossa] @simpleInitReturnMoveOnlyFieldMultiBlock : $@convention(thin) (@owned NonTrivialStruct) -> @owned Klass {
109+
// CHECK: bb0([[ARG:%.*]] : @owned $NonTrivialStruct):
110+
// CHECK-NEXT: [[ALLOC_STACK:%.*]] = alloc_stack [lexical]
111+
// CHECK-NEXT: store [[ARG]] to [init] [[ALLOC_STACK]]
112+
// CHECK-NEXT: [[GEP_1:%.*]] = struct_element_addr [[ALLOC_STACK]] : $*NonTrivialStruct, #NonTrivialStruct.copyableK
113+
// CHECK-NEXT: [[GEP_2:%.*]] = struct_element_addr [[ALLOC_STACK]] : $*NonTrivialStruct, #NonTrivialStruct.nonTrivialStruct2
114+
// CHECK-NEXT: destroy_addr [[GEP_1]]
115+
// CHECK-NEXT: destroy_addr [[GEP_2]]
116+
// CHECK-NEXT: cond_br undef, bb1, bb2
117+
//
118+
// CHECK: bb1:
119+
// CHECK-NEXT: [[GEP:%.*]] = struct_element_addr [[ALLOC_STACK]]
120+
// CHECK-NEXT: [[RESULT:%.*]] = load [take] [[GEP]]
121+
// CHECK-NEXT: br bb3([[RESULT]] :
122+
//
123+
// CHECK: bb2:
124+
// CHECK-NEXT: [[GEP:%.*]] = struct_element_addr [[ALLOC_STACK]]
125+
// CHECK-NEXT: [[RESULT:%.*]] = load [take] [[GEP]]
126+
// CHECK-NEXT: br bb3([[RESULT]] :
127+
128+
// CHECK: bb3([[RESULT:%.*]] :
129+
// CHECK-NEXT: dealloc_stack [[ALLOC_STACK]]
130+
// CHECK-NEXT: return [[RESULT]]
131+
// CHECK: } // end sil function 'simpleInitReturnMoveOnlyFieldMultiBlock'
132+
sil [ossa] @simpleInitReturnMoveOnlyFieldMultiBlock : $@convention(thin) (@owned NonTrivialStruct) -> @owned Klass {
133+
bb0(%0 : @owned $NonTrivialStruct):
134+
%1 = alloc_stack [lexical] $NonTrivialStruct
135+
%2 = mark_must_check [no_implicit_copy] %1 : $*NonTrivialStruct
136+
store %0 to [init] %2 : $*NonTrivialStruct
137+
cond_br undef, bb1, bb2
138+
139+
bb1:
140+
%3a = struct_element_addr %2 : $*NonTrivialStruct, #NonTrivialStruct.k
141+
%3 = load [copy] %3a : $*Klass
142+
br bb3(%3 : $Klass)
143+
144+
bb2:
145+
%4a = struct_element_addr %2 : $*NonTrivialStruct, #NonTrivialStruct.k
146+
%4 = load [copy] %4a : $*Klass
147+
br bb3(%4 : $Klass)
148+
149+
bb3(%5 : @owned $Klass):
150+
destroy_addr %2 : $*NonTrivialStruct
151+
dealloc_stack %1 : $*NonTrivialStruct
152+
return %5 : $Klass
153+
}
154+
155+
// CHECK-LABEL: sil [ossa] @simpleInitReturnMoveOnlyFieldMultiBlock2 : $@convention(thin) (@owned NonTrivialStruct) -> @owned Klass {
156+
// CHECK: bb0([[ARG:%.*]] : @owned $NonTrivialStruct):
157+
// CHECK-NEXT: [[ALLOC_STACK:%.*]] = alloc_stack [lexical]
158+
// CHECK-NEXT: store [[ARG]] to [init] [[ALLOC_STACK]]
159+
// CHECK-NEXT: [[GEP_1:%.*]] = struct_element_addr [[ALLOC_STACK]] : $*NonTrivialStruct, #NonTrivialStruct.copyableK
160+
// CHECK-NEXT: [[GEP_2:%.*]] = struct_element_addr [[ALLOC_STACK]] : $*NonTrivialStruct, #NonTrivialStruct.nonTrivialStruct2
161+
// CHECK-NEXT: destroy_addr [[GEP_1]]
162+
// CHECK-NEXT: destroy_addr [[GEP_2]]
163+
// CHECK-NEXT: cond_br undef, bb1, bb2
164+
//
165+
// CHECK: bb1:
166+
// CHECK-NEXT: [[GEP:%.*]] = struct_element_addr [[ALLOC_STACK]]
167+
// CHECK-NEXT: destroy_addr [[GEP]]
168+
// CHECK-NEXT: function_ref get_klass
169+
// CHECK-NEXT: [[FUNC:%.*]] = function_ref @get_klass :
170+
// CHECK-NEXT: [[RESULT:%.*]] = apply [[FUNC]]()
171+
// CHECK-NEXT: br bb3([[RESULT]] :
172+
//
173+
// CHECK: bb2:
174+
// CHECK-NEXT: [[GEP:%.*]] = struct_element_addr [[ALLOC_STACK]]
175+
// CHECK-NEXT: [[RESULT:%.*]] = load [take] [[GEP]]
176+
// CHECK-NEXT: br bb3([[RESULT]] :
177+
178+
// CHECK: bb3([[RESULT:%.*]] :
179+
// CHECK-NEXT: dealloc_stack [[ALLOC_STACK]]
180+
// CHECK-NEXT: return [[RESULT]]
181+
// CHECK: } // end sil function 'simpleInitReturnMoveOnlyFieldMultiBlock2'
182+
sil [ossa] @simpleInitReturnMoveOnlyFieldMultiBlock2 : $@convention(thin) (@owned NonTrivialStruct) -> @owned Klass {
183+
bb0(%0 : @owned $NonTrivialStruct):
184+
%1 = alloc_stack [lexical] $NonTrivialStruct
185+
%2 = mark_must_check [no_implicit_copy] %1 : $*NonTrivialStruct
186+
store %0 to [init] %2 : $*NonTrivialStruct
187+
cond_br undef, bb1, bb2
188+
189+
bb1:
190+
%f = function_ref @get_klass : $@convention(thin) () -> @owned Klass
191+
%3 = apply %f() : $@convention(thin) () -> @owned Klass
192+
br bb3(%3 : $Klass)
193+
194+
bb2:
195+
%4a = struct_element_addr %2 : $*NonTrivialStruct, #NonTrivialStruct.k
196+
%4 = load [copy] %4a : $*Klass
197+
br bb3(%4 : $Klass)
198+
199+
bb3(%5 : @owned $Klass):
200+
destroy_addr %2 : $*NonTrivialStruct
201+
dealloc_stack %1 : $*NonTrivialStruct
202+
return %5 : $Klass
203+
}
204+
205+
// CHECK-LABEL: sil [ossa] @simpleInitReturnMoveOnlyFieldMultiBlock3 : $@convention(thin) (@owned NonTrivialStruct) -> @owned Klass {
206+
// CHECK: bb0([[ARG:%.*]] : @owned $NonTrivialStruct):
207+
// CHECK-NEXT: [[ALLOC_STACK:%.*]] = alloc_stack [lexical]
208+
// CHECK-NEXT: store [[ARG]] to [init] [[ALLOC_STACK]]
209+
// CHECK-NEXT: [[GEP_1:%.*]] = struct_element_addr [[ALLOC_STACK]] : $*NonTrivialStruct, #NonTrivialStruct.copyableK
210+
// CHECK-NEXT: [[GEP_2:%.*]] = struct_element_addr [[ALLOC_STACK]] : $*NonTrivialStruct, #NonTrivialStruct.nonTrivialStruct2
211+
// CHECK-NEXT: destroy_addr [[GEP_1]]
212+
// CHECK-NEXT: destroy_addr [[GEP_2]]
213+
// CHECK-NEXT: cond_br undef, bb1, bb2
214+
//
215+
// CHECK: bb1:
216+
// CHECK-NEXT: [[GEP:%.*]] = struct_element_addr [[ALLOC_STACK]]
217+
// CHECK-NEXT: destroy_addr [[GEP]]
218+
// CHECK-NEXT: function_ref get_klass
219+
// CHECK-NEXT: [[FUNC:%.*]] = function_ref @get_klass :
220+
// CHECK-NEXT: [[RESULT:%.*]] = apply [[FUNC]]()
221+
// CHECK-NEXT: [[GEP:%.*]] = struct_element_addr [[ALLOC_STACK]]
222+
// CHECK-NEXT: store [[RESULT]] to [init] [[GEP]]
223+
// CHECK-NEXT: [[RESULT:%.*]] = load [take] [[GEP]]
224+
// CHECK-NEXT: br bb3([[RESULT]] :
225+
//
226+
// CHECK: bb2:
227+
// CHECK-NEXT: [[GEP:%.*]] = struct_element_addr [[ALLOC_STACK]]
228+
// CHECK-NEXT: [[RESULT:%.*]] = load [take] [[GEP]]
229+
// CHECK-NEXT: br bb3([[RESULT]] :
230+
//
231+
// CHECK: bb3([[RESULT:%.*]] :
232+
// CHECK-NEXT: dealloc_stack [[ALLOC_STACK]]
233+
// CHECK-NEXT: return [[RESULT]]
234+
// CHECK: } // end sil function 'simpleInitReturnMoveOnlyFieldMultiBlock3'
235+
sil [ossa] @simpleInitReturnMoveOnlyFieldMultiBlock3 : $@convention(thin) (@owned NonTrivialStruct) -> @owned Klass {
236+
bb0(%0 : @owned $NonTrivialStruct):
237+
%1 = alloc_stack [lexical] $NonTrivialStruct
238+
%2 = mark_must_check [no_implicit_copy] %1 : $*NonTrivialStruct
239+
store %0 to [init] %2 : $*NonTrivialStruct
240+
cond_br undef, bb1, bb2
241+
242+
bb1:
243+
%f = function_ref @get_klass : $@convention(thin) () -> @owned Klass
244+
%3 = apply %f() : $@convention(thin) () -> @owned Klass
245+
%3a = struct_element_addr %2 : $*NonTrivialStruct, #NonTrivialStruct.k
246+
store %3 to [assign] %3a : $*Klass
247+
%3b = load [copy] %3a : $*Klass
248+
br bb3(%3b : $Klass)
249+
250+
bb2:
251+
%4a = struct_element_addr %2 : $*NonTrivialStruct, #NonTrivialStruct.k
252+
%4 = load [copy] %4a : $*Klass
253+
br bb3(%4 : $Klass)
254+
255+
bb3(%5 : @owned $Klass):
256+
destroy_addr %2 : $*NonTrivialStruct
257+
dealloc_stack %1 : $*NonTrivialStruct
258+
return %5 : $Klass
259+
}
260+
261+
// CHECK-LABEL: sil [ossa] @useVarKlassNoErrorSimple : $@convention(thin) (@owned Klass) -> () {
262+
// CHECK: bb0([[ARG:%.*]] : @owned $Klass):
263+
// CHECK: [[PTR:%.*]] = alloc_stack [lexical] $Klass, var, name "k"
264+
// CHECK: store [[ARG]] to [init] [[PTR]]
265+
// CHECK: [[ACCESS:%.*]] = begin_access [modify] [static] [[PTR]]
266+
// CHECK: [[LOAD:%.*]] = load [take] [[ACCESS]]
267+
// CHECK: end_access [[ACCESS]]
268+
// CHECK: } // end sil function 'useVarKlassNoErrorSimple'
269+
sil [ossa] @useVarKlassNoErrorSimple : $@convention(thin) (@owned Klass) -> () {
270+
bb0(%arg : @owned $Klass):
271+
%0 = alloc_stack [lexical] $Klass, var, name "k"
272+
%1 = mark_must_check [no_implicit_copy] %0 : $*Klass
273+
store %arg to [init] %1 : $*Klass
274+
%12 = begin_access [read] [static] %1 : $*Klass
275+
%13 = load_borrow %12 : $*Klass
276+
%14 = function_ref @nonConsumingUseKlass : $@convention(thin) (@guaranteed Klass) -> ()
277+
%15 = apply %14(%13) : $@convention(thin) (@guaranteed Klass) -> ()
278+
end_borrow %13 : $Klass
279+
end_access %12 : $*Klass
280+
%18 = begin_access [read] [static] %1 : $*Klass
281+
%19 = load [copy] %18 : $*Klass
282+
end_access %18 : $*Klass
283+
%21 = move_value [lexical] %19 : $Klass
284+
%22 = mark_must_check [no_implicit_copy] %21 : $Klass
285+
debug_value %22 : $Klass, let, name "k2"
286+
%24 = copy_value %22 : $Klass
287+
%25 = move_value %24 : $Klass
288+
destroy_value %25 : $Klass
289+
destroy_value %22 : $Klass
290+
destroy_addr %1 : $*Klass
291+
dealloc_stack %0 : $*Klass
292+
%30 = tuple ()
293+
return %30 : $()
294+
}
295+
296+
// CHECK-LABEL: sil [ossa] @classSimpleNonConsumingUseTest : $@convention(thin) (@owned Klass, @owned Klass) -> () {
297+
// CHECK: [[STACK:%.*]] = alloc_stack [lexical] $Klass, var, name "x2"
298+
// CHECK: store {{%.*}} to [init] [[STACK]]
299+
// CHECK: destroy_addr [[STACK]]
300+
// CHECK: [[ACCESS:%.*]] = begin_access [modify] [static] [[STACK]]
301+
// CHECK: store {{%.*}} to [init] [[ACCESS]]
302+
// CHECK: end_access [[ACCESS]]
303+
// CHECK: [[ACCESS:%.*]] = begin_access [read] [static] [[STACK]]
304+
// CHECK: [[BORROW:%.*]] = load_borrow [[ACCESS]]
305+
// CHECK: apply {{%.*}}([[BORROW]]) : $@convention(thin) (@guaranteed Klass) -> ()
306+
// CHECK: end_borrow [[BORROW]]
307+
// CHECK: end_access [[ACCESS]]
308+
// CHECK: destroy_addr [[STACK]]
309+
// CHECK: } // end sil function 'classSimpleNonConsumingUseTest'
310+
sil [ossa] @classSimpleNonConsumingUseTest : $@convention(thin) (@owned Klass, @owned Klass) -> () {
311+
bb0(%0 : @owned $Klass, %1 : @owned $Klass):
312+
%4 = alloc_stack [lexical] $Klass, var, name "x2"
313+
%5 = mark_must_check [no_implicit_copy] %4 : $*Klass
314+
store %0 to [init] %5 : $*Klass
315+
%9 = begin_access [modify] [static] %5 : $*Klass
316+
store %1 to [assign] %9 : $*Klass
317+
end_access %9 : $*Klass
318+
%12 = begin_access [read] [static] %5 : $*Klass
319+
%13 = load_borrow %12 : $*Klass
320+
%14 = function_ref @nonConsumingUseKlass : $@convention(thin) (@guaranteed Klass) -> ()
321+
%15 = apply %14(%13) : $@convention(thin) (@guaranteed Klass) -> ()
322+
end_borrow %13 : $Klass
323+
end_access %12 : $*Klass
324+
destroy_addr %5 : $*Klass
325+
dealloc_stack %4 : $*Klass
326+
%21 = tuple ()
327+
return %21 : $()
328+
}
329+
330+
// CHECK-LABEL: sil [ossa] @myBufferViewSetter : $@convention(method) <T> (UnsafeBufferPointer<T>, @inout MyBufferView<T>) -> () {
331+
// CHECK-NOT: destroy_addr
332+
// CHECK: } // end sil function 'myBufferViewSetter'
333+
sil [ossa] @myBufferViewSetter : $@convention(method) <T> (UnsafeBufferPointer<T>, @inout MyBufferView<T>) -> () {
334+
bb0(%0 : $UnsafeBufferPointer<T>, %1 : $*MyBufferView<T>):
335+
debug_value %0 : $UnsafeBufferPointer<T>, let, name "value", argno 1, implicit
336+
%3 = mark_must_check [no_implicit_copy] %1 : $*MyBufferView<T>
337+
debug_value %3 : $*MyBufferView<T>, var, name "self", argno 2, implicit, expr op_deref
338+
%5 = begin_access [modify] [static] %3 : $*MyBufferView<T>
339+
%6 = struct_element_addr %5 : $*MyBufferView<T>, #MyBufferView.ptr
340+
store %0 to [trivial] %6 : $*UnsafeBufferPointer<T>
341+
end_access %5 : $*MyBufferView<T>
342+
%9 = tuple ()
343+
return %9 : $()
344+
}

0 commit comments

Comments
 (0)