Skip to content

Commit a779e49

Browse files
authored
Merge pull request swiftlang#82804 from eeckstein/simplify_negated_comparisons
InstructionSimplification: simplify negated integer comparisons
2 parents 235f140 + 5064297 commit a779e49

14 files changed

+454
-40
lines changed

SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyBuiltin.swift

Lines changed: 82 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
import SIL
1414

15-
extension BuiltinInst : OnoneSimplifiable {
15+
extension BuiltinInst : OnoneSimplifiable, SILCombineSimplifiable {
1616
func simplify(_ context: SimplifyContext) {
1717
switch id {
1818
case .IsConcrete:
@@ -53,6 +53,8 @@ extension BuiltinInst : OnoneSimplifiable {
5353
constantFoldIntegerEquality(isEqual: true, context)
5454
case .ICMP_NE:
5555
constantFoldIntegerEquality(isEqual: false, context)
56+
case .Xor:
57+
simplifyNegation(context)
5658
default:
5759
if let literal = constantFold(context) {
5860
uses.replaceAll(with: literal, context)
@@ -165,6 +167,11 @@ private extension BuiltinInst {
165167
case .Sizeof:
166168
value = ty.getStaticSize(context: context)
167169
case .Strideof:
170+
if isUsedAsStrideOfIndexRawPointer(context) {
171+
// Constant folding `stride` would prevent index_raw_pointer simplification.
172+
// See `simplifyIndexRawPointer` in SimplifyPointerToAddress.swift.
173+
return
174+
}
168175
value = ty.getStaticStride(context: context)
169176
case .Alignof:
170177
value = ty.getStaticAlignment(context: context)
@@ -181,7 +188,33 @@ private extension BuiltinInst {
181188
uses.replaceAll(with: literal, context)
182189
context.erase(instruction: self)
183190
}
184-
191+
192+
private func isUsedAsStrideOfIndexRawPointer(_ context: SimplifyContext) -> Bool {
193+
var worklist = ValueWorklist(context)
194+
defer { worklist.deinitialize() }
195+
worklist.pushIfNotVisited(self)
196+
while let v = worklist.pop() {
197+
for use in v.uses {
198+
switch use.instruction {
199+
case let builtin as BuiltinInst:
200+
switch builtin.id {
201+
case .SMulOver, .TruncOrBitCast, .SExtOrBitCast, .ZExtOrBitCast:
202+
worklist.pushIfNotVisited(builtin)
203+
default:
204+
break
205+
}
206+
case let tupleExtract as TupleExtractInst where tupleExtract.fieldIndex == 0:
207+
worklist.pushIfNotVisited(tupleExtract)
208+
case is IndexRawPointerInst:
209+
return true
210+
default:
211+
break
212+
}
213+
}
214+
}
215+
return false
216+
}
217+
185218
func optimizeArgumentToThinMetatype(argument: Int, _ context: SimplifyContext) {
186219
let type: Type
187220

@@ -224,6 +257,53 @@ private extension BuiltinInst {
224257
}
225258
return false
226259
}
260+
261+
/// Replaces a builtin "xor", which negates its operand comparison
262+
/// ```
263+
/// %3 = builtin "cmp_slt_Int64"(%1, %2) : $Builtin.Int1
264+
/// %4 = integer_literal $Builtin.Int1, -1
265+
/// %5 = builtin "xor_Int1"(%3, %4) : $Builtin.Int1
266+
/// ```
267+
/// with the negated comparison
268+
/// ```
269+
/// %5 = builtin "cmp_ge_Int64"(%1, %2) : $Builtin.Int1
270+
/// ```
271+
func simplifyNegation(_ context: SimplifyContext) {
272+
assert(id == .Xor)
273+
guard let one = arguments[1] as? IntegerLiteralInst,
274+
let oneValue = one.value,
275+
oneValue == -1,
276+
let lhsBuiltin = arguments[0] as? BuiltinInst,
277+
lhsBuiltin.type.isBuiltinInteger,
278+
let negatedBuiltinName = lhsBuiltin.negatedComparisonBuiltinName
279+
else {
280+
return
281+
}
282+
283+
let builder = Builder(before: lhsBuiltin, context)
284+
let negated = builder.createBuiltinBinaryFunction(name: negatedBuiltinName,
285+
operandType: lhsBuiltin.arguments[0].type,
286+
resultType: lhsBuiltin.type,
287+
arguments: Array(lhsBuiltin.arguments))
288+
self.replace(with: negated, context)
289+
}
290+
291+
private var negatedComparisonBuiltinName: String? {
292+
switch id {
293+
case .ICMP_EQ: return "cmp_ne"
294+
case .ICMP_NE: return "cmp_eq"
295+
case .ICMP_SLE: return "cmp_sgt"
296+
case .ICMP_SLT: return "cmp_sge"
297+
case .ICMP_SGE: return "cmp_slt"
298+
case .ICMP_SGT: return "cmp_sle"
299+
case .ICMP_ULE: return "cmp_ugt"
300+
case .ICMP_ULT: return "cmp_uge"
301+
case .ICMP_UGE: return "cmp_ult"
302+
case .ICMP_UGT: return "cmp_ule"
303+
default:
304+
return nil
305+
}
306+
}
227307
}
228308

229309
private extension Value {

SwiftCompilerSources/Sources/Optimizer/PassManager/PassRegistration.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ private func registerSwiftPasses() {
111111
// Instruction passes
112112
registerForSILCombine(BeginBorrowInst.self, { run(BeginBorrowInst.self, $0) })
113113
registerForSILCombine(BeginCOWMutationInst.self, { run(BeginCOWMutationInst.self, $0) })
114+
registerForSILCombine(BuiltinInst.self, { run(BuiltinInst.self, $0) })
114115
registerForSILCombine(FixLifetimeInst.self, { run(FixLifetimeInst.self, $0) })
115116
registerForSILCombine(GlobalValueInst.self, { run(GlobalValueInst.self, $0) })
116117
registerForSILCombine(StrongRetainInst.self, { run(StrongRetainInst.self, $0) })

lib/SILOptimizer/LoopTransforms/BoundsCheckOpts.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,11 @@ static bool isSignedLessEqual(SILValue Start, SILValue End, SILBasicBlock &BB) {
353353
m_Specific(Start), m_Specific(End)),
354354
m_One())))
355355
return true;
356+
// Try to match a cond_fail on "SGT Start, End".
357+
if (match(CF->getOperand(),
358+
m_ApplyInst(BuiltinValueKind::ICMP_SGT,
359+
m_Specific(Start), m_Specific(End))))
360+
return true;
356361
// Try to match a cond_fail on "SLT End, Start".
357362
if (match(CF->getOperand(),
358363
m_ApplyInst(BuiltinValueKind::ICMP_SLT, m_Specific(End),
@@ -368,6 +373,11 @@ static bool isSignedLessEqual(SILValue Start, SILValue End, SILBasicBlock &BB) {
368373
m_Specific(PreInclusiveEnd)),
369374
m_One())))
370375
IsPreInclusiveEndLEQ = true;
376+
if (match(CF->getOperand(),
377+
m_ApplyInst(BuiltinValueKind::ICMP_SGT,
378+
m_Specific(Start),
379+
m_Specific(PreInclusiveEnd))))
380+
IsPreInclusiveEndLEQ = true;
371381
if (match(CF->getOperand(),
372382
m_ApplyInst(BuiltinValueKind::ICMP_SLT,
373383
m_Specific(PreInclusiveEnd), m_Specific(Start))))
@@ -379,6 +389,11 @@ static bool isSignedLessEqual(SILValue Start, SILValue End, SILBasicBlock &BB) {
379389
m_Specific(PreInclusiveEnd)),
380390
m_One())))
381391
IsPreInclusiveEndGTEnd = true;
392+
if (match(CF->getOperand(),
393+
m_ApplyInst(BuiltinValueKind::ICMP_SLE,
394+
m_Specific(End),
395+
m_Specific(PreInclusiveEnd))))
396+
IsPreInclusiveEndGTEnd = true;
382397
if (IsPreInclusiveEndLEQ && IsPreInclusiveEndGTEnd)
383398
return true;
384399
}
@@ -910,6 +925,12 @@ static bool isComparisonKnownFalse(BuiltinInst *Builtin,
910925
IndVar.Cmp != BuiltinValueKind::ICMP_EQ)
911926
return false;
912927

928+
if (match(Builtin, m_ApplyInst(BuiltinValueKind::ICMP_SGE,
929+
m_Specific(IndVar.HeaderVal),
930+
m_Specific(IndVar.End)))) {
931+
return true;
932+
}
933+
913934
// Pattern match a false condition patterns that we can detect and optimize:
914935
// Iteration count < 0 (start)
915936
// Iteration count + 1 <= 0 (start)

lib/SILOptimizer/SILCombiner/SILCombiner.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,6 @@ class SILCombiner :
251251
SILInstruction *visitPartialApplyInst(PartialApplyInst *AI);
252252
SILInstruction *visitBeginApplyInst(BeginApplyInst *BAI);
253253
SILInstruction *optimizeStringObject(BuiltinInst *BI);
254-
SILInstruction *visitBuiltinInst(BuiltinInst *BI);
255254
SILInstruction *visitCondFailInst(CondFailInst *CFI);
256255
SILInstruction *visitRefToRawPointerInst(RefToRawPointerInst *RRPI);
257256
SILInstruction *visitUpcastInst(UpcastInst *UCI);

lib/SILOptimizer/SILCombiner/SILCombinerBuiltinVisitors.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -659,7 +659,7 @@ SILInstruction *SILCombiner::optimizeStringObject(BuiltinInst *BI) {
659659
setBits & andBits);
660660
}
661661

662-
SILInstruction *SILCombiner::visitBuiltinInst(BuiltinInst *I) {
662+
SILInstruction *SILCombiner::legacyVisitBuiltinInst(BuiltinInst *I) {
663663
if (I->getBuiltinInfo().ID == BuiltinValueKind::CanBeObjCClass)
664664
return optimizeBuiltinCanBeObjCClass(I, Builder);
665665
if (I->getBuiltinInfo().ID == BuiltinValueKind::IsConcrete)

lib/SILOptimizer/SILCombiner/Simplifications.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ INSTRUCTION_SIMPLIFICATION(UncheckedEnumDataInst)
5454
INSTRUCTION_SIMPLIFICATION(WitnessMethodInst)
5555
INSTRUCTION_SIMPLIFICATION(EndCOWMutationAddrInst)
5656
INSTRUCTION_SIMPLIFICATION_WITH_LEGACY(AllocStackInst)
57+
INSTRUCTION_SIMPLIFICATION_WITH_LEGACY(BuiltinInst)
5758
INSTRUCTION_SIMPLIFICATION_WITH_LEGACY(UnconditionalCheckedCastInst)
5859
INSTRUCTION_SIMPLIFICATION_WITH_LEGACY(ApplyInst)
5960
INSTRUCTION_SIMPLIFICATION_WITH_LEGACY(TryApplyInst)

test/Frontend/OptimizationOptions-with-stdlib-checks.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,7 @@ func test_partial_safety_check(x: Int, y: Int) -> Int {
104104
// In release mode keep succinct library precondition checks (trap).
105105
// RELEASE-LABEL: sil hidden @$s19OptimizationOptions22testprecondition_check1x1yS2i_SitF : $@convention(thin) (Int, Int) -> Int {
106106
// RELEASE-NOT: "Fatal error"
107-
// RELEASE: %[[V2:.+]] = builtin "xor_Int1"(%{{.+}}, %{{.+}})
108-
// RELEASE: cond_fail %[[V2]]
107+
// RELEASE: cond_fail %{{[0-9]}}, "precondition failure"
109108
// RELEASE: return
110109

111110
// In unchecked mode remove library precondition checks.

test/Frontend/OptimizationOptions-without-stdlib-checks.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,7 @@ func test_partial_safety_check(x: Int, y: Int) -> Int {
101101
// In release mode keep succinct library precondition checks (trap).
102102
// RELEASE-LABEL: sil hidden @$s19OptimizationOptions22testprecondition_check1x1yS2i_SitF : $@convention(thin) (Int, Int) -> Int {
103103
// RELEASE-NOT: "Fatal error"
104-
// RELEASE: %[[V2:.+]] = builtin "xor_Int1"(%{{.+}}, %{{.+}})
105-
// RELEASE: cond_fail %[[V2]]
104+
// RELEASE: cond_fail %{{[0-9]}}, "precondition failure"
106105
// RELEASE: return
107106

108107
// In unchecked mode remove library precondition checks.

0 commit comments

Comments
 (0)