Skip to content

Commit 06042c1

Browse files
authored
Merge pull request #79483 from eeckstein/fix-temp-lvalue-opt
TempLValueOpt: avoid creating invalid apply argument aliasing.
2 parents ee360e0 + f0b7bdb commit 06042c1

File tree

3 files changed

+139
-0
lines changed

3 files changed

+139
-0
lines changed

lib/SILOptimizer/Transforms/TempLValueOpt.cpp

+46
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ class TempLValueOptPass : public SILFunctionTransform {
8383
private:
8484
void tempLValueOpt(CopyAddrInst *copyInst);
8585
void combineCopyAndDestroy(CopyAddrInst *copyInst);
86+
bool hasInvalidApplyArgumentAliasing(SILInstruction *inst, SILValue tempAddr,
87+
SILValue destAddr, AliasAnalysis *aa);
8688
};
8789

8890
void TempLValueOptPass::run() {
@@ -230,6 +232,11 @@ void TempLValueOptPass::tempLValueOpt(CopyAddrInst *copyInst) {
230232
projections.contains(inst) == 0)
231233
return;
232234

235+
// Check if replacing the temporary with destination would invalidate an applied
236+
// function's alias rules of indirect arguments.
237+
if (hasInvalidApplyArgumentAliasing(inst, temporary, destination, AA))
238+
return;
239+
233240
if (needDestroyEarly && isDeinitBarrier(inst, bca))
234241
return;
235242
}
@@ -269,6 +276,45 @@ void TempLValueOptPass::tempLValueOpt(CopyAddrInst *copyInst) {
269276
invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions);
270277
}
271278

279+
/// Returns true if after replacing tempAddr with destAddr an apply instruction
280+
/// would have invalid aliasing of indirect arguments.
281+
///
282+
/// An indirect argument (except `@inout_aliasable`) must not alias with another
283+
/// indirect argument. Now, if we would replace tempAddr with destAddr in
284+
///
285+
/// apply %f(%tempAddr, %destAddr) : (@in T) -> @out T
286+
///
287+
/// we would invalidate this rule.
288+
/// This is even true if the called function does not read from destAddr.
289+
///
290+
bool TempLValueOptPass::hasInvalidApplyArgumentAliasing(SILInstruction *inst,
291+
SILValue tempAddr,
292+
SILValue destAddr,
293+
AliasAnalysis *aa) {
294+
auto as = FullApplySite::isa(inst);
295+
if (!as)
296+
return false;
297+
298+
bool tempAccessed = false;
299+
bool destAccessed = false;
300+
bool mutatingAccess = false;
301+
for (Operand &argOp : as.getArgumentOperands()) {
302+
auto conv = as.getArgumentConvention(argOp);
303+
if (conv.isExclusiveIndirectParameter()) {
304+
if (aa->mayAlias(tempAddr, argOp.get())) {
305+
tempAccessed = true;
306+
if (!conv.isGuaranteedConventionInCaller())
307+
mutatingAccess = true;
308+
} else if (aa->mayAlias(destAddr, argOp.get())) {
309+
destAccessed = true;
310+
if (!conv.isGuaranteedConventionInCaller())
311+
mutatingAccess = true;
312+
}
313+
}
314+
}
315+
return mutatingAccess && tempAccessed && destAccessed;
316+
}
317+
272318
void TempLValueOptPass::combineCopyAndDestroy(CopyAddrInst *copyInst) {
273319
if (copyInst->isTakeOfSrc())
274320
return;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// RUN: %target-swift-frontend %s -O -sil-verify-all -emit-sil -o /dev/null
2+
3+
4+
// Check that TempLValueOpt does not create invalid SIL which causes a verification error.
5+
6+
public struct S<T> {
7+
internal var a: T? = nil
8+
public var b: T? {a}
9+
public var c: T? = nil
10+
11+
public mutating func set() {
12+
c = b
13+
}
14+
}
15+
16+
public class Klass{}
17+
18+
public func test() -> S<Klass> {
19+
var s = S<Klass>()
20+
s.set()
21+
return s
22+
}
23+
24+

test/SILOptimizer/templvalueopt_ossa.sil

+69
Original file line numberDiff line numberDiff line change
@@ -334,3 +334,72 @@ bb0(%0 : @guaranteed $Any, %1 : $*Any):
334334
%11 = tuple ()
335335
return %11 : $()
336336
}
337+
338+
struct TwoFields {
339+
var a: Child
340+
var b: Child
341+
}
342+
343+
sil @no_read : $@convention(thin) (@in_guaranteed TwoFields) -> @out Child {
344+
[%0: write v**]
345+
[global: ]
346+
}
347+
sil @no_reads : $@convention(thin) (@in_guaranteed Child, @in_guaranteed TwoFields) -> () {
348+
[global: ]
349+
}
350+
351+
// CHECK-LABEL: sil [ossa] @argument_aliasing :
352+
// CHECK: %1 = alloc_stack
353+
// CHECK: apply %{{[0-9]+}}(%1, %0)
354+
// CHECK-LABEL: } // end sil function 'argument_aliasing'
355+
sil [ossa] @argument_aliasing : $@convention(thin) (@inout TwoFields) -> () {
356+
bb0(%0 : $*TwoFields):
357+
%1 = alloc_stack $Child
358+
%2 = function_ref @no_read : $@convention(thin) (@in_guaranteed TwoFields) -> @out Child
359+
%3 = apply %2(%1, %0) : $@convention(thin) (@in_guaranteed TwoFields) -> @out Child
360+
%4 = struct_element_addr %0, #TwoFields.a
361+
copy_addr [take] %1 to %4
362+
dealloc_stack %1
363+
%7 = tuple ()
364+
return %7
365+
}
366+
367+
// CHECK-LABEL: sil [ossa] @no_argument_aliasing :
368+
// CHECK-NOT: alloc_stack
369+
// CHECK: [[A:%.*]] = struct_element_addr %0 : $*TwoFields, #TwoFields.a
370+
// CHECK-NEXT: destroy_addr [[A]]
371+
// CHECK: apply %{{[0-9]+}}([[A]], %1)
372+
// CHECK-NOT: copy_addr
373+
// CHECK-LABEL: } // end sil function 'no_argument_aliasing'
374+
sil [ossa] @no_argument_aliasing : $@convention(thin) (@inout TwoFields, @inout TwoFields) -> () {
375+
bb0(%0 : $*TwoFields, %1 : $*TwoFields):
376+
%2 = alloc_stack $Child
377+
%3 = function_ref @no_read : $@convention(thin) (@in_guaranteed TwoFields) -> @out Child
378+
%4 = apply %3(%2, %1) : $@convention(thin) (@in_guaranteed TwoFields) -> @out Child
379+
%5 = struct_element_addr %0, #TwoFields.a
380+
copy_addr [take] %2 to %5
381+
dealloc_stack %2
382+
%8 = tuple ()
383+
return %8
384+
}
385+
386+
// CHECK-LABEL: sil [ossa] @guaranteed_argument_aliasing :
387+
// CHECK-NOT: alloc_stack
388+
// CHECK: [[A:%.*]] = struct_element_addr %0 : $*TwoFields, #TwoFields.a
389+
// CHECK-NEXT: destroy_addr [[A]]
390+
// CHECK: apply %{{[0-9]+}}([[A]], %0)
391+
// CHECK-NOT: copy_addr
392+
// CHECK-LABEL: } // end sil function 'guaranteed_argument_aliasing'
393+
sil [ossa] @guaranteed_argument_aliasing : $@convention(thin) (@inout TwoFields, @owned Child) -> () {
394+
bb0(%0 : $*TwoFields, %1 : @owned $Child):
395+
%2 = alloc_stack $Child
396+
store %1 to [init] %2
397+
%3 = function_ref @no_reads : $@convention(thin) (@in_guaranteed Child, @in_guaranteed TwoFields) -> ()
398+
%4 = apply %3(%2, %0) : $@convention(thin) (@in_guaranteed Child, @in_guaranteed TwoFields) -> ()
399+
%5 = struct_element_addr %0, #TwoFields.a
400+
copy_addr [take] %2 to %5
401+
dealloc_stack %2
402+
%8 = tuple ()
403+
return %8
404+
}
405+

0 commit comments

Comments
 (0)