Skip to content

Commit 37857ed

Browse files
committed
Handle a gap while matching SIL patterns for hoisting array bound checks
1 parent 8f99736 commit 37857ed

File tree

2 files changed

+169
-9
lines changed

2 files changed

+169
-9
lines changed

lib/SILOptimizer/LoopTransforms/ArrayBoundsCheckOpts.cpp

Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -556,8 +556,8 @@ struct InductionInfo {
556556
return Start;
557557
}
558558

559-
SILValue getLastValue(SILLocation &Loc, SILBuilder &B) {
560-
return getSub(Loc, End, 1, B);
559+
SILValue getLastValue(SILLocation &Loc, SILBuilder &B, unsigned SubVal) {
560+
return SubVal != 0 ? getSub(Loc, End, SubVal, B) : End;
561561
}
562562

563563
/// If necessary insert an overflow for this induction variable.
@@ -718,28 +718,62 @@ static bool isGuaranteedToBeExecuted(DominanceInfo *DT, SILBasicBlock *Block,
718718
/// induction variable.
719719
class AccessFunction {
720720
InductionInfo *Ind;
721+
bool preIncrement;
722+
723+
AccessFunction(InductionInfo *I, bool isPreIncrement = false)
724+
: Ind(I), preIncrement(isPreIncrement) {}
721725

722-
AccessFunction(InductionInfo *I) { Ind = I; }
723726
public:
724727

725728
operator bool() { return Ind != nullptr; }
726729

727730
static AccessFunction getLinearFunction(SILValue Idx,
728731
InductionAnalysis &IndVars) {
729732
// Match the actual induction variable buried in the integer struct.
730-
// %2 = struct $Int(%1 : $Builtin.Word)
731-
// = apply %check_bounds(%array, %2) : $@convention(thin) (Int, ArrayInt) -> ()
733+
// bb(%ivar)
734+
// %2 = struct $Int(%ivar : $Builtin.Word)
735+
// = apply %check_bounds(%array, %2) :
736+
// or
737+
// bb(%ivar1)
738+
// %ivar2 = builtin "sadd_with_overflow_Int64"(%ivar1,...)
739+
// %t = tuple_extract %ivar2
740+
// %s = struct $Int(%t : $Builtin.Word)
741+
// = apply %check_bounds(%array, %s) :
742+
743+
bool preIncrement = false;
744+
732745
auto ArrayIndexStruct = dyn_cast<StructInst>(Idx);
733746
if (!ArrayIndexStruct)
734747
return nullptr;
735748

736749
auto AsArg =
737750
dyn_cast<SILArgument>(ArrayIndexStruct->getElements()[0]);
738-
if (!AsArg)
739-
return nullptr;
751+
752+
if (!AsArg) {
753+
auto *TupleExtract =
754+
dyn_cast<TupleExtractInst>(ArrayIndexStruct->getElements()[0]);
755+
756+
if (!TupleExtract) {
757+
return nullptr;
758+
}
759+
760+
auto *Builtin = dyn_cast<BuiltinInst>(TupleExtract->getOperand());
761+
if (!Builtin || Builtin->getBuiltinKind() != BuiltinValueKind::SAddOver) {
762+
return nullptr;
763+
}
764+
765+
// We don't check if the second argument to the builtin is loop invariant
766+
// here, because only induction variables with a +1 incremenent are
767+
// considered for bounds check optimization.
768+
AsArg = dyn_cast<SILArgument>(Builtin->getArguments()[0]);
769+
if (!AsArg) {
770+
return nullptr;
771+
}
772+
preIncrement = true;
773+
}
740774

741775
if (auto *Ind = IndVars[AsArg])
742-
return AccessFunction(Ind);
776+
return AccessFunction(Ind, preIncrement);
743777

744778
return nullptr;
745779
}
@@ -771,7 +805,7 @@ class AccessFunction {
771805
NewCheck->setOperand(1, Start);
772806

773807
// Get the last induction value.
774-
auto LastVal = Ind->getLastValue(Loc, Builder);
808+
auto LastVal = Ind->getLastValue(Loc, Builder, preIncrement ? 0 : 1);
775809
// Clone the struct for the end index.
776810
auto End = cast<SingleValueInstruction>(CheckToHoist.getIndex())
777811
->clone(Preheader->getTerminator());

test/SILOptimizer/abcopts.sil

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1096,6 +1096,132 @@ bb4(%31 : $Builtin.Int32):
10961096
return %32 : $Int32
10971097
}
10981098

1099+
// SIL pattern for :
1100+
// func hoist_new_ind_pattern1(_ a : [Int]) {
1101+
// for i in 0...4 {
1102+
// ...a[i]...
1103+
// }
1104+
// }
1105+
// Bounds check for the array should be hoisted out of the loop
1106+
//
1107+
// RANGECHECK-LABEL: sil @hoist_new_ind_pattern1 :
1108+
// RANGECHECK: [[CB:%.*]] = function_ref @checkbounds : $@convention(method) (Int32, Bool, @owned ArrayInt) -> _DependenceToken
1109+
// RANGECHECK: bb1
1110+
// RANGECHECK-NOT: apply [[CB]]
1111+
// RANGECHECK-LABEL: } // end sil function 'hoist_new_ind_pattern1'
1112+
sil @hoist_new_ind_pattern1 : $@convention(thin) (@owned ArrayInt) -> () {
1113+
bb0(%0 : $ArrayInt):
1114+
%minus1 = integer_literal $Builtin.Int1, -1
1115+
%true = struct $Bool(%minus1 : $Builtin.Int1)
1116+
%zero = integer_literal $Builtin.Int32, 0
1117+
%int0 = struct $Int32 (%zero : $Builtin.Int32)
1118+
%one = integer_literal $Builtin.Int32, 1
1119+
%four = integer_literal $Builtin.Int32, 4
1120+
%intfour = struct $Int32 (%four : $Builtin.Int32)
1121+
%cb = function_ref @checkbounds : $@convention(method) (Int32, Bool, @owned ArrayInt) -> _DependenceToken
1122+
apply %cb(%int0, %true, %0) : $@convention(method) (Int32, Bool, @owned ArrayInt) -> _DependenceToken
1123+
br bb1(%zero : $Builtin.Int32)
1124+
1125+
bb1(%4 : $Builtin.Int32):
1126+
%5 = builtin "sadd_with_overflow_Int32"(%4 : $Builtin.Int32, %one : $Builtin.Int32, %minus1 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1)
1127+
%6 = tuple_extract %5 : $(Builtin.Int32, Builtin.Int1), 0
1128+
%7 = tuple_extract %5 : $(Builtin.Int32, Builtin.Int1), 1
1129+
%8 = struct $Int32 (%6 : $Builtin.Int32)
1130+
%9 = builtin "cmp_eq_Int32"(%6 : $Builtin.Int32, %four : $Builtin.Int32) : $Builtin.Int1
1131+
apply %cb(%8, %true, %0) : $@convention(method) (Int32, Bool, @owned ArrayInt) -> _DependenceToken
1132+
cond_br %9, bb3, bb2
1133+
1134+
bb2:
1135+
br bb1(%6 : $Builtin.Int32)
1136+
1137+
bb3:
1138+
%t = tuple ()
1139+
return %t : $()
1140+
}
1141+
1142+
// Currently this is not optimized because the induction var increment is 2
1143+
// Support for this can be added by updating induction variable analysis
1144+
// RANGECHECK-LABEL: sil @hoist_new_ind_pattern2 :
1145+
// RANGECHECK: [[CB:%.*]] = function_ref @checkbounds : $@convention(method) (Int32, Bool, @owned ArrayInt) -> _DependenceToken
1146+
// RANGECHECK: bb1
1147+
// RANGECHECK: apply [[CB]]
1148+
// RANGECHECK-LABEL: } // end sil function 'hoist_new_ind_pattern2'
1149+
sil @hoist_new_ind_pattern2 : $@convention(thin) (@owned ArrayInt) -> () {
1150+
bb0(%0 : $ArrayInt):
1151+
%minus1 = integer_literal $Builtin.Int1, -1
1152+
%true = struct $Bool(%minus1 : $Builtin.Int1)
1153+
%zero = integer_literal $Builtin.Int32, 0
1154+
%int0 = struct $Int32 (%zero : $Builtin.Int32)
1155+
%two = integer_literal $Builtin.Int32, 2
1156+
%four = integer_literal $Builtin.Int32, 4
1157+
%intfour = struct $Int32 (%four : $Builtin.Int32)
1158+
%cb = function_ref @checkbounds : $@convention(method) (Int32, Bool, @owned ArrayInt) -> _DependenceToken
1159+
apply %cb(%int0, %true, %0) : $@convention(method) (Int32, Bool, @owned ArrayInt) -> _DependenceToken
1160+
br bb1(%zero : $Builtin.Int32)
1161+
1162+
bb1(%4 : $Builtin.Int32):
1163+
%5 = builtin "sadd_with_overflow_Int32"(%4 : $Builtin.Int32, %two : $Builtin.Int32, %minus1 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1)
1164+
%6 = tuple_extract %5 : $(Builtin.Int32, Builtin.Int1), 0
1165+
%7 = tuple_extract %5 : $(Builtin.Int32, Builtin.Int1), 1
1166+
%8 = struct $Int32 (%6 : $Builtin.Int32)
1167+
%9 = builtin "cmp_eq_Int32"(%6 : $Builtin.Int32, %four : $Builtin.Int32) : $Builtin.Int1
1168+
apply %cb(%8, %true, %0) : $@convention(method) (Int32, Bool, @owned ArrayInt) -> _DependenceToken
1169+
cond_br %9, bb3, bb2
1170+
1171+
bb2:
1172+
br bb1(%6 : $Builtin.Int32)
1173+
1174+
bb3:
1175+
%t = tuple ()
1176+
return %t : $()
1177+
}
1178+
1179+
// This is currently not optimized because access function is not recognized
1180+
// SIL pattern for :
1181+
// for var index in 0...24
1182+
// {
1183+
// ...a[index + index]...
1184+
// }
1185+
//
1186+
// RANGECHECK-LABEL: sil @hoist_new_ind_pattern3 :
1187+
// RANGECHECK: [[CB:%.*]] = function_ref @checkbounds : $@convention(method) (Int32, Bool, @owned ArrayInt) -> _DependenceToken
1188+
// RANGECHECK: bb1
1189+
// RANGECHECK: apply [[CB]]
1190+
// RANGECHECK-LABEL: } // end sil function 'hoist_new_ind_pattern3'
1191+
sil @hoist_new_ind_pattern3 : $@convention(thin) (@owned ArrayInt) -> () {
1192+
bb0(%0 : $ArrayInt):
1193+
%minus1 = integer_literal $Builtin.Int1, -1
1194+
%true = struct $Bool(%minus1 : $Builtin.Int1)
1195+
%zero = integer_literal $Builtin.Int32, 0
1196+
%int0 = struct $Int32 (%zero : $Builtin.Int32)
1197+
%one = integer_literal $Builtin.Int32, 1
1198+
%four = integer_literal $Builtin.Int32, 4
1199+
%intfour = struct $Int32 (%four : $Builtin.Int32)
1200+
%cb = function_ref @checkbounds : $@convention(method) (Int32, Bool, @owned ArrayInt) -> _DependenceToken
1201+
apply %cb(%int0, %true, %0) : $@convention(method) (Int32, Bool, @owned ArrayInt) -> _DependenceToken
1202+
br bb1(%zero : $Builtin.Int32)
1203+
1204+
bb1(%4 : $Builtin.Int32):
1205+
%5 = builtin "sadd_with_overflow_Int32"(%4 : $Builtin.Int32, %one : $Builtin.Int32, %minus1 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1)
1206+
%6 = tuple_extract %5 : $(Builtin.Int32, Builtin.Int1), 0
1207+
%7 = tuple_extract %5 : $(Builtin.Int32, Builtin.Int1), 1
1208+
%8 = struct $Int32 (%6 : $Builtin.Int32)
1209+
%9 = builtin "cmp_eq_Int32"(%6 : $Builtin.Int32, %four : $Builtin.Int32) : $Builtin.Int1
1210+
%10 = builtin "sadd_with_overflow_Int32"(%6 : $Builtin.Int32, %6 : $Builtin.Int32, %minus1 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1)
1211+
%11 = tuple_extract %10 : $(Builtin.Int32, Builtin.Int1), 0
1212+
%12 = tuple_extract %10 : $(Builtin.Int32, Builtin.Int1), 1
1213+
%13 = struct $Int32 (%11 : $Builtin.Int32)
1214+
apply %cb(%13, %true, %0) : $@convention(method) (Int32, Bool, @owned ArrayInt) -> _DependenceToken
1215+
cond_br %9, bb3, bb2
1216+
1217+
bb2:
1218+
br bb1(%6 : $Builtin.Int32)
1219+
1220+
bb3:
1221+
%t = tuple ()
1222+
return %t : $()
1223+
}
1224+
10991225
sil public_external [_semantics "array.check_subscript"] @checkbounds_no_meth : $@convention(thin) (Int32, Bool, @owned ArrayInt) -> _DependenceToken {
11001226
bb0(%0: $Int32, %1: $Bool, %2: $ArrayInt):
11011227
unreachable

0 commit comments

Comments
 (0)