Skip to content

Commit 6c299ad

Browse files
authored
Merge pull request #65573 from meg-gupta/abcoptfix
Handle a gap while matching SIL patterns for hoisting array bound checks
2 parents 5e26417 + 270125b commit 6c299ad

File tree

2 files changed

+244
-12
lines changed

2 files changed

+244
-12
lines changed

lib/SILOptimizer/LoopTransforms/ArrayBoundsCheckOpts.cpp

Lines changed: 58 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -530,6 +530,18 @@ static SILValue getSub(SILLocation Loc, SILValue Val, unsigned SubVal,
530530
return B.createTupleExtract(Loc, AI, 0);
531531
}
532532

533+
static SILValue getAdd(SILLocation Loc, SILValue Val, unsigned AddVal,
534+
SILBuilder &B) {
535+
SmallVector<SILValue, 4> Args(1, Val);
536+
Args.push_back(B.createIntegerLiteral(Loc, Val->getType(), AddVal));
537+
Args.push_back(B.createIntegerLiteral(
538+
Loc, SILType::getBuiltinIntegerType(1, B.getASTContext()), -1));
539+
540+
auto *AI = B.createBuiltinBinaryFunctionWithOverflow(
541+
Loc, "sadd_with_overflow", Args);
542+
return B.createTupleExtract(Loc, AI, 0);
543+
}
544+
533545
/// A canonical induction variable incremented by one from Start to End-1.
534546
struct InductionInfo {
535547
SILArgument *HeaderVal;
@@ -552,12 +564,12 @@ struct InductionInfo {
552564

553565
SILInstruction *getInstruction() { return Inc; }
554566

555-
SILValue getFirstValue() {
556-
return Start;
567+
SILValue getFirstValue(SILLocation &Loc, SILBuilder &B, unsigned AddVal) {
568+
return AddVal != 0 ? getAdd(Loc, Start, AddVal, B) : Start;
557569
}
558570

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

563575
/// If necessary insert an overflow for this induction variable.
@@ -718,28 +730,62 @@ static bool isGuaranteedToBeExecuted(DominanceInfo *DT, SILBasicBlock *Block,
718730
/// induction variable.
719731
class AccessFunction {
720732
InductionInfo *Ind;
733+
bool preIncrement;
734+
735+
AccessFunction(InductionInfo *I, bool isPreIncrement = false)
736+
: Ind(I), preIncrement(isPreIncrement) {}
721737

722-
AccessFunction(InductionInfo *I) { Ind = I; }
723738
public:
724739

725740
operator bool() { return Ind != nullptr; }
726741

727742
static AccessFunction getLinearFunction(SILValue Idx,
728743
InductionAnalysis &IndVars) {
729744
// 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) -> ()
745+
// bb(%ivar)
746+
// %2 = struct $Int(%ivar : $Builtin.Word)
747+
// = apply %check_bounds(%array, %2) :
748+
// or
749+
// bb(%ivar1)
750+
// %ivar2 = builtin "sadd_with_overflow_Int64"(%ivar1,...)
751+
// %t = tuple_extract %ivar2
752+
// %s = struct $Int(%t : $Builtin.Word)
753+
// = apply %check_bounds(%array, %s) :
754+
755+
bool preIncrement = false;
756+
732757
auto ArrayIndexStruct = dyn_cast<StructInst>(Idx);
733758
if (!ArrayIndexStruct)
734759
return nullptr;
735760

736761
auto AsArg =
737762
dyn_cast<SILArgument>(ArrayIndexStruct->getElements()[0]);
738-
if (!AsArg)
739-
return nullptr;
763+
764+
if (!AsArg) {
765+
auto *TupleExtract =
766+
dyn_cast<TupleExtractInst>(ArrayIndexStruct->getElements()[0]);
767+
768+
if (!TupleExtract) {
769+
return nullptr;
770+
}
771+
772+
auto *Builtin = dyn_cast<BuiltinInst>(TupleExtract->getOperand());
773+
if (!Builtin || Builtin->getBuiltinKind() != BuiltinValueKind::SAddOver) {
774+
return nullptr;
775+
}
776+
777+
// We don't check if the second argument to the builtin is loop invariant
778+
// here, because only induction variables with a +1 incremenent are
779+
// considered for bounds check optimization.
780+
AsArg = dyn_cast<SILArgument>(Builtin->getArguments()[0]);
781+
if (!AsArg) {
782+
return nullptr;
783+
}
784+
preIncrement = true;
785+
}
740786

741787
if (auto *Ind = IndVars[AsArg])
742-
return AccessFunction(Ind);
788+
return AccessFunction(Ind, preIncrement);
743789

744790
return nullptr;
745791
}
@@ -759,7 +805,7 @@ class AccessFunction {
759805
SILBuilderWithScope Builder(Preheader->getTerminator(), AI);
760806

761807
// Get the first induction value.
762-
auto FirstVal = Ind->getFirstValue();
808+
auto FirstVal = Ind->getFirstValue(Loc, Builder, preIncrement ? 1 : 0);
763809
// Clone the struct for the start index.
764810
auto Start = cast<SingleValueInstruction>(CheckToHoist.getIndex())
765811
->clone(Preheader->getTerminator());
@@ -771,7 +817,7 @@ class AccessFunction {
771817
NewCheck->setOperand(1, Start);
772818

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

test/SILOptimizer/abcopts.sil

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1096,6 +1096,192 @@ 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: [[MINUS1:%.*]] = integer_literal $Builtin.Int1, -1
1110+
// RANGECHECK: [[TRUE:%.*]] = struct $Bool ([[MINUS1]] : $Builtin.Int1)
1111+
// RANGECHECK: [[ZERO:%.*]] = integer_literal $Builtin.Int32, 0
1112+
// RANGECHECK: [[INTZERO:%.*]] = struct $Int32 ([[ZERO]] : $Builtin.Int32)
1113+
// RANGECHECK: [[FOUR:%.*]] = integer_literal $Builtin.Int32, 4
1114+
// RANGECHECK: [[ONE:%.*]] = integer_literal $Builtin.Int32, 1
1115+
// RANGECHECK: [[ANOTHERTRUE:%.*]] = integer_literal $Builtin.Int1, -1
1116+
// RANGECHECK: [[INTONEADD:%.*]] = builtin "sadd_with_overflow_Int32"([[ZERO]] : $Builtin.Int32, [[ONE]] : $Builtin.Int32, [[ANOTHERTRUE]] : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1)
1117+
// RANGECHECK: [[INTONEEX:%.*]] = tuple_extract [[INTONEADD]] : $(Builtin.Int32, Builtin.Int1), 0
1118+
// RANGECHECK: [[INTONE:%.*]] = struct $Int32 ([[INTONEEX]] : $Builtin.Int32)
1119+
// RANGECHECK: [[A1:%.*]] = apply [[CB]]([[INTONE]], [[TRUE]], %0) : $@convention(method) (Int32, Bool, @owned ArrayInt) -> _DependenceToken
1120+
// RANGECHECK: [[INTFOUR:%.*]] = struct $Int32 ([[FOUR]] : $Builtin.Int32)
1121+
// RANGECHECK: [[A2:%.*]] = apply [[CB]]([[INTFOUR]], [[TRUE]], %0) : $@convention(method) (Int32, Bool, @owned ArrayInt) -> _DependenceToken
1122+
// RANGECHECK: bb1
1123+
// RANGECHECK-NOT: apply [[CB]]
1124+
// RANGECHECK-LABEL: } // end sil function 'hoist_new_ind_pattern1'
1125+
sil @hoist_new_ind_pattern1 : $@convention(thin) (@owned ArrayInt) -> () {
1126+
bb0(%0 : $ArrayInt):
1127+
%minus1 = integer_literal $Builtin.Int1, -1
1128+
%true = struct $Bool(%minus1 : $Builtin.Int1)
1129+
%zero = integer_literal $Builtin.Int32, 0
1130+
%int0 = struct $Int32 (%zero : $Builtin.Int32)
1131+
%one = integer_literal $Builtin.Int32, 1
1132+
%four = integer_literal $Builtin.Int32, 4
1133+
%intfour = struct $Int32 (%four : $Builtin.Int32)
1134+
%cb = function_ref @checkbounds : $@convention(method) (Int32, Bool, @owned ArrayInt) -> _DependenceToken
1135+
apply %cb(%int0, %true, %0) : $@convention(method) (Int32, Bool, @owned ArrayInt) -> _DependenceToken
1136+
br bb1(%zero : $Builtin.Int32)
1137+
1138+
bb1(%4 : $Builtin.Int32):
1139+
%5 = builtin "sadd_with_overflow_Int32"(%4 : $Builtin.Int32, %one : $Builtin.Int32, %minus1 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1)
1140+
%6 = tuple_extract %5 : $(Builtin.Int32, Builtin.Int1), 0
1141+
%7 = tuple_extract %5 : $(Builtin.Int32, Builtin.Int1), 1
1142+
%8 = struct $Int32 (%6 : $Builtin.Int32)
1143+
%9 = builtin "cmp_eq_Int32"(%6 : $Builtin.Int32, %four : $Builtin.Int32) : $Builtin.Int1
1144+
apply %cb(%8, %true, %0) : $@convention(method) (Int32, Bool, @owned ArrayInt) -> _DependenceToken
1145+
cond_br %9, bb3, bb2
1146+
1147+
bb2:
1148+
br bb1(%6 : $Builtin.Int32)
1149+
1150+
bb3:
1151+
%t = tuple ()
1152+
return %t : $()
1153+
}
1154+
1155+
// Currently this is not optimized because the induction var increment is 2
1156+
// Support for this can be added by updating induction variable analysis
1157+
// RANGECHECK-LABEL: sil @hoist_new_ind_pattern2 :
1158+
// RANGECHECK: [[CB:%.*]] = function_ref @checkbounds : $@convention(method) (Int32, Bool, @owned ArrayInt) -> _DependenceToken
1159+
// RANGECHECK: bb1
1160+
// RANGECHECK: apply [[CB]]
1161+
// RANGECHECK-LABEL: } // end sil function 'hoist_new_ind_pattern2'
1162+
sil @hoist_new_ind_pattern2 : $@convention(thin) (@owned ArrayInt) -> () {
1163+
bb0(%0 : $ArrayInt):
1164+
%minus1 = integer_literal $Builtin.Int1, -1
1165+
%true = struct $Bool(%minus1 : $Builtin.Int1)
1166+
%zero = integer_literal $Builtin.Int32, 0
1167+
%int0 = struct $Int32 (%zero : $Builtin.Int32)
1168+
%two = integer_literal $Builtin.Int32, 2
1169+
%four = integer_literal $Builtin.Int32, 4
1170+
%intfour = struct $Int32 (%four : $Builtin.Int32)
1171+
%cb = function_ref @checkbounds : $@convention(method) (Int32, Bool, @owned ArrayInt) -> _DependenceToken
1172+
apply %cb(%int0, %true, %0) : $@convention(method) (Int32, Bool, @owned ArrayInt) -> _DependenceToken
1173+
br bb1(%zero : $Builtin.Int32)
1174+
1175+
bb1(%4 : $Builtin.Int32):
1176+
%5 = builtin "sadd_with_overflow_Int32"(%4 : $Builtin.Int32, %two : $Builtin.Int32, %minus1 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1)
1177+
%6 = tuple_extract %5 : $(Builtin.Int32, Builtin.Int1), 0
1178+
%7 = tuple_extract %5 : $(Builtin.Int32, Builtin.Int1), 1
1179+
cond_fail %7 : $Builtin.Int1
1180+
%8 = struct $Int32 (%6 : $Builtin.Int32)
1181+
%9 = builtin "cmp_eq_Int32"(%6 : $Builtin.Int32, %four : $Builtin.Int32) : $Builtin.Int1
1182+
apply %cb(%8, %true, %0) : $@convention(method) (Int32, Bool, @owned ArrayInt) -> _DependenceToken
1183+
cond_br %9, bb3, bb2
1184+
1185+
bb2:
1186+
br bb1(%6 : $Builtin.Int32)
1187+
1188+
bb3:
1189+
%t = tuple ()
1190+
return %t : $()
1191+
}
1192+
1193+
// This is currently not optimized because access function is not recognized
1194+
// SIL pattern for :
1195+
// for var index in 0...24
1196+
// {
1197+
// ...a[index + index]...
1198+
// }
1199+
//
1200+
// RANGECHECK-LABEL: sil @hoist_new_ind_pattern3 :
1201+
// RANGECHECK: [[CB:%.*]] = function_ref @checkbounds : $@convention(method) (Int32, Bool, @owned ArrayInt) -> _DependenceToken
1202+
// RANGECHECK: bb1
1203+
// RANGECHECK: apply [[CB]]
1204+
// RANGECHECK-LABEL: } // end sil function 'hoist_new_ind_pattern3'
1205+
sil @hoist_new_ind_pattern3 : $@convention(thin) (@owned ArrayInt) -> () {
1206+
bb0(%0 : $ArrayInt):
1207+
%minus1 = integer_literal $Builtin.Int1, -1
1208+
%true = struct $Bool(%minus1 : $Builtin.Int1)
1209+
%zero = integer_literal $Builtin.Int32, 0
1210+
%int0 = struct $Int32 (%zero : $Builtin.Int32)
1211+
%one = integer_literal $Builtin.Int32, 1
1212+
%four = integer_literal $Builtin.Int32, 4
1213+
%intfour = struct $Int32 (%four : $Builtin.Int32)
1214+
%cb = function_ref @checkbounds : $@convention(method) (Int32, Bool, @owned ArrayInt) -> _DependenceToken
1215+
apply %cb(%int0, %true, %0) : $@convention(method) (Int32, Bool, @owned ArrayInt) -> _DependenceToken
1216+
br bb1(%zero : $Builtin.Int32)
1217+
1218+
bb1(%4 : $Builtin.Int32):
1219+
%5 = builtin "sadd_with_overflow_Int32"(%4 : $Builtin.Int32, %one : $Builtin.Int32, %minus1 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1)
1220+
%6 = tuple_extract %5 : $(Builtin.Int32, Builtin.Int1), 0
1221+
%7 = tuple_extract %5 : $(Builtin.Int32, Builtin.Int1), 1
1222+
%8 = struct $Int32 (%6 : $Builtin.Int32)
1223+
%9 = builtin "cmp_eq_Int32"(%6 : $Builtin.Int32, %four : $Builtin.Int32) : $Builtin.Int1
1224+
%10 = builtin "sadd_with_overflow_Int32"(%6 : $Builtin.Int32, %6 : $Builtin.Int32, %minus1 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1)
1225+
%11 = tuple_extract %10 : $(Builtin.Int32, Builtin.Int1), 0
1226+
%12 = tuple_extract %10 : $(Builtin.Int32, Builtin.Int1), 1
1227+
%13 = struct $Int32 (%11 : $Builtin.Int32)
1228+
apply %cb(%13, %true, %0) : $@convention(method) (Int32, Bool, @owned ArrayInt) -> _DependenceToken
1229+
cond_br %9, bb3, bb2
1230+
1231+
bb2:
1232+
br bb1(%6 : $Builtin.Int32)
1233+
1234+
bb3:
1235+
%t = tuple ()
1236+
return %t : $()
1237+
}
1238+
1239+
1240+
// RANGECHECK-LABEL: sil @hoist_new_ind_pattern4 :
1241+
// RANGECHECK: [[MINUS1:%.*]] = integer_literal $Builtin.Int1, -1
1242+
// RANGECHECK: [[TRUE:%.*]] = struct $Bool ([[MINUS1]] : $Builtin.Int1)
1243+
// RANGECHECK: [[ZERO:%.*]] = integer_literal $Builtin.Int32, 0
1244+
// RANGECHECK: [[INTZERO:%.*]] = struct $Int32 ([[ZERO]] : $Builtin.Int32)
1245+
// RANGECHECK: [[CB:%.*]] = function_ref @checkbounds2 : $@convention(method) (Int32, Bool, @owned Array<Int>) -> _DependenceToken
1246+
// RANGECHECK: apply [[CB]]([[INTZERO]], [[TRUE]], %0) : $@convention(method) (Int32, Bool, @owned Array<Int>) -> _DependenceToken
1247+
// RANGECHECK-NOT: apply [[CB]]
1248+
// RANGECHECK-LABEL: } // end sil function 'hoist_new_ind_pattern4'
1249+
// for i in 0..<a.count
1250+
// {
1251+
// ...a[i]...
1252+
// }
1253+
sil @hoist_new_ind_pattern4 : $@convention(thin) (@owned Array<Int>) -> () {
1254+
bb0(%0 : $Array<Int>):
1255+
%minus1 = integer_literal $Builtin.Int1, -1
1256+
%true = struct $Bool(%minus1 : $Builtin.Int1)
1257+
%zero = integer_literal $Builtin.Int32, 0
1258+
%int0 = struct $Int32 (%zero : $Builtin.Int32)
1259+
%one = integer_literal $Builtin.Int32, 1
1260+
%f1 = function_ref @getCount2 : $@convention(method) (@owned Array<Int>) -> Int32
1261+
%t1 = apply %f1(%0) : $@convention(method) (@owned Array<Int>) -> Int32
1262+
%count = struct_extract %t1 : $Int32, #Int32._value
1263+
%cb = function_ref @checkbounds2 : $@convention(method) (Int32, Bool, @owned Array<Int>) -> _DependenceToken
1264+
apply %cb(%int0, %true, %0) : $@convention(method) (Int32, Bool, @owned Array<Int>) -> _DependenceToken
1265+
br bb1(%zero : $Builtin.Int32)
1266+
1267+
bb1(%4 : $Builtin.Int32):
1268+
%5 = builtin "sadd_with_overflow_Int32"(%4 : $Builtin.Int32, %one : $Builtin.Int32, %minus1 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1)
1269+
%6 = tuple_extract %5 : $(Builtin.Int32, Builtin.Int1), 0
1270+
%7 = tuple_extract %5 : $(Builtin.Int32, Builtin.Int1), 1
1271+
cond_fail %7 : $Builtin.Int1
1272+
%8 = struct $Int32 (%6 : $Builtin.Int32)
1273+
apply %cb(%8, %true, %0) : $@convention(method) (Int32, Bool, @owned Array<Int>) -> _DependenceToken
1274+
%9 = builtin "cmp_eq_Int32"(%6 : $Builtin.Int32, %count : $Builtin.Int32) : $Builtin.Int1
1275+
cond_br %9, bb3, bb2
1276+
1277+
bb2:
1278+
br bb1(%6 : $Builtin.Int32)
1279+
1280+
bb3:
1281+
%t = tuple ()
1282+
return %t : $()
1283+
}
1284+
10991285
sil public_external [_semantics "array.check_subscript"] @checkbounds_no_meth : $@convention(thin) (Int32, Bool, @owned ArrayInt) -> _DependenceToken {
11001286
bb0(%0: $Int32, %1: $Bool, %2: $ArrayInt):
11011287
unreachable

0 commit comments

Comments
 (0)