Skip to content

Commit 719ee25

Browse files
committed
[constant-folding] When simplifying destructures, do not handle cases where we have a non-trivial value with .none ownership.
In certain cases, in OSSA, non-trivial values with .none ownership can be merged into .owned aggregates such that when we extract the value back out from the aggregate we lost the information that the original value was not .owned. As an example of this consider the following SIL: bb0(%0 : @owned Builtin.NativeObject): %1 = enum $Optional<Builtin.NativeObject>, #Optional.none!enumelt %2 = tuple(%0 : $Builtin.NativeObject, %1 : $Optional<Builtin.NativeObject>) (%3, %4) = destructure_tuple %2 : $(Builtin.NativeObject, Optional<Builtin.NativeObject>) In this case, %4 has .owned ownership, while %1 has .none ownership. This is because we have lost the refined information that we originally had a .none value as input to the tuple we are destructuring. Due to this when we RAUW, we would need to insert a destroy on %4 to make sure that we maintain ossa invariants. This is safe since the destroy_value will be on a dynamically .none value, which is a no-op. That being said, the intention here was actually not to implement this pattern in the code (as can be seen by not handling @owned destructures). Thus this commit just makes the constant folding code more conservative to ensure that we do not try to handle this case.
1 parent 17d5d10 commit 719ee25

File tree

2 files changed

+73
-6
lines changed

2 files changed

+73
-6
lines changed

lib/SILOptimizer/Utils/ConstantFolding.cpp

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1414,9 +1414,22 @@ static bool constantFoldInstruction(Operand *Op, Optional<bool> &ResultsInError,
14141414
[&](Operand &op) -> SILValue {
14151415
SILValue operandValue = op.get();
14161416
auto ownershipKind = operandValue.getOwnershipKind();
1417-
if (ownershipKind.isCompatibleWith(ValueOwnershipKind::Guaranteed))
1418-
return operandValue;
1419-
return SILValue();
1417+
1418+
// First check if we are not compatible with guaranteed. This means
1419+
// we would be Owned or Unowned. If so, return SILValue().
1420+
if (!ownershipKind.isCompatibleWith(ValueOwnershipKind::Guaranteed))
1421+
return SILValue();
1422+
1423+
// Otherwise check if our operand is non-trivial and None. In cases
1424+
// like that, the non-trivial type could be replacing an owned value
1425+
// where we lost that our underlying value is None due to
1426+
// intermediate aggregate literal operations. In that case, we /do
1427+
// not/ want to eliminate the destructure.
1428+
if (ownershipKind == ValueOwnershipKind::None &&
1429+
!operandValue->getType().isTrivial(*Struct->getFunction()))
1430+
return SILValue();
1431+
1432+
return operandValue;
14201433
});
14211434
return true;
14221435
}
@@ -1435,9 +1448,22 @@ static bool constantFoldInstruction(Operand *Op, Optional<bool> &ResultsInError,
14351448
[&](Operand &op) -> SILValue {
14361449
SILValue operandValue = op.get();
14371450
auto ownershipKind = operandValue.getOwnershipKind();
1438-
if (ownershipKind.isCompatibleWith(ValueOwnershipKind::Guaranteed))
1439-
return operandValue;
1440-
return SILValue();
1451+
1452+
// First check if we are not compatible with guaranteed. This means
1453+
// we would be Owned or Unowned. If so, return SILValue().
1454+
if (!ownershipKind.isCompatibleWith(ValueOwnershipKind::Guaranteed))
1455+
return SILValue();
1456+
1457+
// Otherwise check if our operand is non-trivial and None. In cases
1458+
// like that, the non-trivial type could be replacing an owned value
1459+
// where we lost that our underlying value is None due to
1460+
// intermediate aggregate literal operations. In that case, we /do
1461+
// not/ want to eliminate the destructure.
1462+
if (ownershipKind == ValueOwnershipKind::None &&
1463+
!operandValue->getType().isTrivial(*Tuple->getFunction()))
1464+
return SILValue();
1465+
1466+
return operandValue;
14411467
});
14421468
return true;
14431469
}

test/SILOptimizer/constant_propagation_ownership.sil

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@ class Klass {}
2929
sil @klass_allocator : $@convention(method) (@thick Klass.Type) -> @owned Klass
3030
sil @generic_user : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> ()
3131

32+
struct NativeObjectOptNativeObjectPair {
33+
var lhs: Builtin.NativeObject
34+
var rhs: Optional<Builtin.NativeObject>
35+
}
36+
3237
///////////
3338
// Tests //
3439
///////////
@@ -1181,3 +1186,39 @@ bb3:
11811186
%9999 = tuple()
11821187
return %9999 : $()
11831188
}
1189+
1190+
// CHECK-LABEL: sil [ossa] @do_not_RAUW_owned_destructure_with_nontrivial_none_value_tuple : $@convention(thin) (@owned Builtin.NativeObject) -> () {
1191+
// CHECK: ({{%.*}}, [[DESTRUCTURE_RESULT_2:%.*]]) = destructure_tuple
1192+
// CHECK: store [[DESTRUCTURE_RESULT_2]] to [init]
1193+
// CHECK: } // end sil function 'do_not_RAUW_owned_destructure_with_nontrivial_none_value_tuple'
1194+
sil [ossa] @do_not_RAUW_owned_destructure_with_nontrivial_none_value_tuple : $@convention(thin) (@owned Builtin.NativeObject) -> () {
1195+
bb0(%arg : @owned $Builtin.NativeObject):
1196+
%0 = alloc_stack $Optional<Builtin.NativeObject>
1197+
%1 = enum $Optional<Builtin.NativeObject>, #Optional.none!enumelt
1198+
%2 = tuple(%arg : $Builtin.NativeObject, %1 : $Optional<Builtin.NativeObject>)
1199+
(%3, %4) = destructure_tuple %2 : $(Builtin.NativeObject, Optional<Builtin.NativeObject>)
1200+
store %4 to [init] %0 : $*Optional<Builtin.NativeObject>
1201+
destroy_addr %0 : $*Optional<Builtin.NativeObject>
1202+
dealloc_stack %0 : $*Optional<Builtin.NativeObject>
1203+
destroy_value %3 : $Builtin.NativeObject
1204+
%9999 = tuple()
1205+
return %9999 : $()
1206+
}
1207+
1208+
// CHECK-LABEL: sil [ossa] @do_not_RAUW_owned_destructure_with_nontrivial_none_value_struct : $@convention(thin) (@owned Builtin.NativeObject) -> () {
1209+
// CHECK: ({{%.*}}, [[DESTRUCTURE_RESULT_2:%.*]]) = destructure_struct
1210+
// CHECK: store [[DESTRUCTURE_RESULT_2]] to [init]
1211+
// CHECK: } // end sil function 'do_not_RAUW_owned_destructure_with_nontrivial_none_value_struct'
1212+
sil [ossa] @do_not_RAUW_owned_destructure_with_nontrivial_none_value_struct : $@convention(thin) (@owned Builtin.NativeObject) -> () {
1213+
bb0(%arg : @owned $Builtin.NativeObject):
1214+
%0 = alloc_stack $Optional<Builtin.NativeObject>
1215+
%1 = enum $Optional<Builtin.NativeObject>, #Optional.none!enumelt
1216+
%2 = struct $NativeObjectOptNativeObjectPair (%arg : $Builtin.NativeObject, %1 : $Optional<Builtin.NativeObject>)
1217+
(%3, %4) = destructure_struct %2 : $NativeObjectOptNativeObjectPair
1218+
store %4 to [init] %0 : $*Optional<Builtin.NativeObject>
1219+
destroy_addr %0 : $*Optional<Builtin.NativeObject>
1220+
dealloc_stack %0 : $*Optional<Builtin.NativeObject>
1221+
destroy_value %3 : $Builtin.NativeObject
1222+
%9999 = tuple()
1223+
return %9999 : $()
1224+
}

0 commit comments

Comments
 (0)