Skip to content

Commit b527020

Browse files
committed
LoopInvariantCodeMotion: bail on split load [take]
We currently don't support split `load [take]`, i.e. `load [take]` which does _not_ load all non-trivial fields of the initial value.
1 parent fc6302e commit b527020

File tree

2 files changed

+104
-1
lines changed

2 files changed

+104
-1
lines changed

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/LoopInvariantCodeMotion.swift

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -727,7 +727,18 @@ private extension MovableInstructions {
727727
guard let firstStore else {
728728
return false
729729
}
730-
730+
731+
// We currently don't support split `load [take]`, i.e. `load [take]` which does _not_ load all
732+
// non-trivial fields of the initial value.
733+
for case let load as LoadInst in loadsAndStores {
734+
if load.loadOwnership == .take,
735+
let path = accessPath.getProjection(to: load.address.accessPath),
736+
!firstStore.destination.type.isProjectingEntireNonTrivialMembers(path: path, in: load.parentFunction)
737+
{
738+
return false
739+
}
740+
}
741+
731742
var ssaUpdater = SSAUpdater(
732743
function: firstStore.parentFunction,
733744
type: firstStore.destination.type.objectType,
@@ -883,6 +894,35 @@ private extension MovableInstructions {
883894
}
884895
}
885896

897+
private extension Type {
898+
func isProjectingEntireNonTrivialMembers(path: SmallProjectionPath, in function: Function) -> Bool {
899+
let (kind, index, subPath) = path.pop()
900+
switch kind {
901+
case .root:
902+
return true
903+
case .structField:
904+
guard let fields = getNominalFields(in: function) else {
905+
return false
906+
}
907+
for (fieldIdx, fieldType) in fields.enumerated() {
908+
if fieldIdx != index && !fieldType.isTrivial(in: function) {
909+
return false
910+
}
911+
}
912+
return fields[index].isProjectingEntireNonTrivialMembers(path: subPath, in: function)
913+
case .tupleField:
914+
for (elementIdx, elementType) in tupleElements.enumerated() {
915+
if elementIdx != index && !elementType.isTrivial(in: function) {
916+
return false
917+
}
918+
}
919+
return tupleElements[index].isProjectingEntireNonTrivialMembers(path: subPath, in: function)
920+
default:
921+
fatalError("path is not materializable")
922+
}
923+
}
924+
}
925+
886926
private extension Instruction {
887927
/// Returns `true` if this instruction follows the default hoisting heuristic which means it
888928
/// is not a terminator, allocation or deallocation and either a hoistable array semantics call or doesn't have memory effects.

test/SILOptimizer/licm.sil

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@ struct S {
2323
var s: String
2424
}
2525

26+
struct S2 {
27+
var i: Int
28+
var s1: String
29+
var s2: String
30+
}
31+
2632
struct Pair {
2733
var t: (a: Int, b: Int)
2834
}
@@ -2088,3 +2094,60 @@ bb3:
20882094
%r = tuple ()
20892095
return %r
20902096
}
2097+
2098+
// Just check that LICM doesn't produce invalid SIL.
2099+
// CHECK-LABEL: sil [ossa] @partial_load_take_is_not_supported :
2100+
// CHECK-LABEL: } // end sil function 'partial_load_take_is_not_supported'
2101+
sil [ossa] @partial_load_take_is_not_supported : $@convention(thin) (@inout S2, Int) -> () {
2102+
bb0(%0 : $*S2, %1 : $Int):
2103+
br bb1
2104+
2105+
bb1:
2106+
%4 = struct_element_addr %0, #S2.s1
2107+
%5 = load [take] %4
2108+
%6 = struct_element_addr %0, #S2.s2
2109+
%7 = load [take] %6
2110+
%8 = struct $S2 (%1, %5, %7)
2111+
store %8 to [init] %0
2112+
cond_br undef, bb2, bb3
2113+
2114+
bb2:
2115+
br bb1
2116+
2117+
bb3:
2118+
%r = tuple ()
2119+
return %r
2120+
}
2121+
2122+
// CHECK-LABEL: sil [ossa] @partial_load_copy :
2123+
// CHECK: bb1([[V:%.*]] : @owned $S2):
2124+
// CHECK: [[B1:%.*]] = begin_borrow [[V]]
2125+
// CHECK-NEXT: [[SE1:%.*]] = struct_extract [[B1]] : $S2, #S2.s1
2126+
// CHECK-NEXT: = copy_value [[SE1]]
2127+
// CHECK: [[B2:%.*]] = begin_borrow [[V]]
2128+
// CHECK-NEXT: [[SE2:%.*]] = struct_extract [[B2]] : $S2, #S2.s2
2129+
// CHECK-NEXT: = copy_value [[SE2]]
2130+
// CHECK-LABEL: } // end sil function 'partial_load_copy'
2131+
sil [ossa] @partial_load_copy : $@convention(thin) (@inout S2, Int) -> () {
2132+
bb0(%0 : $*S2, %1 : $Int):
2133+
br bb1
2134+
2135+
bb1:
2136+
%4 = struct_element_addr %0, #S2.s1
2137+
%5 = load [copy] %4
2138+
%6 = struct_element_addr %0, #S2.s2
2139+
%7 = load [copy] %6
2140+
%8 = struct $S2 (%1, %5, %7)
2141+
%9 = load [take] %0
2142+
destroy_value %9
2143+
store %8 to [init] %0
2144+
cond_br undef, bb2, bb3
2145+
2146+
bb2:
2147+
br bb1
2148+
2149+
bb3:
2150+
%r = tuple ()
2151+
return %r
2152+
}
2153+

0 commit comments

Comments
 (0)