diff --git a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyBuiltin.swift b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyBuiltin.swift index 0af3cc3d9ab02..c1ec891ad1e05 100644 --- a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyBuiltin.swift +++ b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyBuiltin.swift @@ -53,6 +53,8 @@ extension BuiltinInst : OnoneSimplifiable { constantFoldIntegerEquality(isEqual: true, context) case .ICMP_NE: constantFoldIntegerEquality(isEqual: false, context) + case .Xor: + simplifyNegation(context) default: if let literal = constantFold(context) { uses.replaceAll(with: literal, context) @@ -224,6 +226,53 @@ private extension BuiltinInst { } return false } + + /// Replaces a builtin "xor", which negates its operand comparison + /// ``` + /// %3 = builtin "cmp_slt_Int64"(%1, %2) : $Builtin.Int1 + /// %4 = integer_literal $Builtin.Int1, -1 + /// %5 = builtin "xor_Int1"(%3, %4) : $Builtin.Int1 + /// ``` + /// with the negated comparison + /// ``` + /// %5 = builtin "cmp_ge_Int64"(%1, %2) : $Builtin.Int1 + /// ``` + func simplifyNegation(_ context: SimplifyContext) { + assert(id == .Xor) + guard let one = arguments[1] as? IntegerLiteralInst, + let oneValue = one.value, + oneValue == -1, + let lhsBuiltin = arguments[0] as? BuiltinInst, + lhsBuiltin.type.isBuiltinInteger, + let negatedBuiltinName = lhsBuiltin.negatedComparisonBuiltinName + else { + return + } + + let builder = Builder(before: lhsBuiltin, context) + let negated = builder.createBuiltinBinaryFunction(name: negatedBuiltinName, + operandType: lhsBuiltin.arguments[0].type, + resultType: lhsBuiltin.type, + arguments: Array(lhsBuiltin.arguments)) + self.replace(with: negated, context) + } + + private var negatedComparisonBuiltinName: String? { + switch id { + case .ICMP_EQ: return "cmp_ne" + case .ICMP_NE: return "cmp_eq" + case .ICMP_SLE: return "cmp_sgt" + case .ICMP_SLT: return "cmp_sge" + case .ICMP_SGE: return "cmp_slt" + case .ICMP_SGT: return "cmp_sle" + case .ICMP_ULE: return "cmp_ugt" + case .ICMP_ULT: return "cmp_uge" + case .ICMP_UGE: return "cmp_ult" + case .ICMP_UGT: return "cmp_ule" + default: + return nil + } + } } private extension Value { diff --git a/test/SILOptimizer/simplify_builtin.sil b/test/SILOptimizer/simplify_builtin.sil index c14f349629cef..5aa7398f302b6 100644 --- a/test/SILOptimizer/simplify_builtin.sil +++ b/test/SILOptimizer/simplify_builtin.sil @@ -694,3 +694,166 @@ bb0: return %0 } +// CHECK-LABEL: sil @invert_comparison_eq : +// CHECK: %2 = builtin "cmp_ne_Int64"(%0 : $Builtin.Int64, %1 : $Builtin.Int64) +// CHECK: return %2 +// CHECK: } // end sil function 'invert_comparison_eq' +sil @invert_comparison_eq : $@convention(thin) (Builtin.Int64, Builtin.Int64) -> Builtin.Int1 { +bb0(%0 : $Builtin.Int64, %1 : $Builtin.Int64): + %2 = builtin "cmp_eq_Int64"(%0, %1) : $Builtin.Int1 + %3 = integer_literal $Builtin.Int1, -1 + %4 = builtin "xor_Int1"(%2, %3) : $Builtin.Int1 + return %4 +} + +// CHECK-LABEL: sil @invert_comparison_ne : +// CHECK: %2 = builtin "cmp_eq_Int64"(%0 : $Builtin.Int64, %1 : $Builtin.Int64) +// CHECK: return %2 +// CHECK: } // end sil function 'invert_comparison_ne' +sil @invert_comparison_ne : $@convention(thin) (Builtin.Int64, Builtin.Int64) -> Builtin.Int1 { +bb0(%0 : $Builtin.Int64, %1 : $Builtin.Int64): + %2 = builtin "cmp_ne_Int64"(%0, %1) : $Builtin.Int1 + %3 = integer_literal $Builtin.Int1, -1 + %4 = builtin "xor_Int1"(%2, %3) : $Builtin.Int1 + return %4 +} + +// CHECK-LABEL: sil @invert_comparison_sle : +// CHECK: %2 = builtin "cmp_sgt_Int64"(%0 : $Builtin.Int64, %1 : $Builtin.Int64) +// CHECK: return %2 +// CHECK: } // end sil function 'invert_comparison_sle' +sil @invert_comparison_sle : $@convention(thin) (Builtin.Int64, Builtin.Int64) -> Builtin.Int1 { +bb0(%0 : $Builtin.Int64, %1 : $Builtin.Int64): + %2 = builtin "cmp_sle_Int64"(%0, %1) : $Builtin.Int1 + %3 = integer_literal $Builtin.Int1, -1 + %4 = builtin "xor_Int1"(%2, %3) : $Builtin.Int1 + return %4 +} + +// CHECK-LABEL: sil @invert_comparison_slt : +// CHECK: %2 = builtin "cmp_sge_Int64"(%0 : $Builtin.Int64, %1 : $Builtin.Int64) +// CHECK: return %2 +// CHECK: } // end sil function 'invert_comparison_slt' +sil @invert_comparison_slt : $@convention(thin) (Builtin.Int64, Builtin.Int64) -> Builtin.Int1 { +bb0(%0 : $Builtin.Int64, %1 : $Builtin.Int64): + %2 = builtin "cmp_slt_Int64"(%0, %1) : $Builtin.Int1 + %3 = integer_literal $Builtin.Int1, -1 + %4 = builtin "xor_Int1"(%2, %3) : $Builtin.Int1 + return %4 +} + +// CHECK-LABEL: sil @invert_comparison_sge : +// CHECK: %2 = builtin "cmp_slt_Int64"(%0 : $Builtin.Int64, %1 : $Builtin.Int64) +// CHECK: return %2 +// CHECK: } // end sil function 'invert_comparison_sge' +sil @invert_comparison_sge : $@convention(thin) (Builtin.Int64, Builtin.Int64) -> Builtin.Int1 { +bb0(%0 : $Builtin.Int64, %1 : $Builtin.Int64): + %2 = builtin "cmp_sge_Int64"(%0, %1) : $Builtin.Int1 + %3 = integer_literal $Builtin.Int1, -1 + %4 = builtin "xor_Int1"(%2, %3) : $Builtin.Int1 + return %4 +} + +// CHECK-LABEL: sil @invert_comparison_sgt : +// CHECK: %2 = builtin "cmp_sle_Int64"(%0 : $Builtin.Int64, %1 : $Builtin.Int64) +// CHECK: return %2 +// CHECK: } // end sil function 'invert_comparison_sgt' +sil @invert_comparison_sgt : $@convention(thin) (Builtin.Int64, Builtin.Int64) -> Builtin.Int1 { +bb0(%0 : $Builtin.Int64, %1 : $Builtin.Int64): + %2 = builtin "cmp_sgt_Int64"(%0, %1) : $Builtin.Int1 + %3 = integer_literal $Builtin.Int1, -1 + %4 = builtin "xor_Int1"(%2, %3) : $Builtin.Int1 + return %4 +} + +// CHECK-LABEL: sil @invert_comparison_ule : +// CHECK: %2 = builtin "cmp_ugt_Int64"(%0 : $Builtin.Int64, %1 : $Builtin.Int64) +// CHECK: return %2 +// CHECK: } // end sil function 'invert_comparison_ule' +sil @invert_comparison_ule : $@convention(thin) (Builtin.Int64, Builtin.Int64) -> Builtin.Int1 { +bb0(%0 : $Builtin.Int64, %1 : $Builtin.Int64): + %2 = builtin "cmp_ule_Int64"(%0, %1) : $Builtin.Int1 + %3 = integer_literal $Builtin.Int1, -1 + %4 = builtin "xor_Int1"(%2, %3) : $Builtin.Int1 + return %4 +} + +// CHECK-LABEL: sil @invert_comparison_ult : +// CHECK: %2 = builtin "cmp_uge_Int64"(%0 : $Builtin.Int64, %1 : $Builtin.Int64) +// CHECK: return %2 +// CHECK: } // end sil function 'invert_comparison_ult' +sil @invert_comparison_ult : $@convention(thin) (Builtin.Int64, Builtin.Int64) -> Builtin.Int1 { +bb0(%0 : $Builtin.Int64, %1 : $Builtin.Int64): + %2 = builtin "cmp_ult_Int64"(%0, %1) : $Builtin.Int1 + %3 = integer_literal $Builtin.Int1, -1 + %4 = builtin "xor_Int1"(%2, %3) : $Builtin.Int1 + return %4 +} + +// CHECK-LABEL: sil @invert_comparison_uge : +// CHECK: %2 = builtin "cmp_ult_Int64"(%0 : $Builtin.Int64, %1 : $Builtin.Int64) +// CHECK: return %2 +// CHECK: } // end sil function 'invert_comparison_uge' +sil @invert_comparison_uge : $@convention(thin) (Builtin.Int64, Builtin.Int64) -> Builtin.Int1 { +bb0(%0 : $Builtin.Int64, %1 : $Builtin.Int64): + %2 = builtin "cmp_uge_Int64"(%0, %1) : $Builtin.Int1 + %3 = integer_literal $Builtin.Int1, -1 + %4 = builtin "xor_Int1"(%2, %3) : $Builtin.Int1 + return %4 +} + +// CHECK-LABEL: sil @invert_comparison_ugt : +// CHECK: %2 = builtin "cmp_ule_Int64"(%0 : $Builtin.Int64, %1 : $Builtin.Int64) +// CHECK: return %2 +// CHECK: } // end sil function 'invert_comparison_ugt' +sil @invert_comparison_ugt : $@convention(thin) (Builtin.Int64, Builtin.Int64) -> Builtin.Int1 { +bb0(%0 : $Builtin.Int64, %1 : $Builtin.Int64): + %2 = builtin "cmp_ugt_Int64"(%0, %1) : $Builtin.Int1 + %3 = integer_literal $Builtin.Int1, -1 + %4 = builtin "xor_Int1"(%2, %3) : $Builtin.Int1 + return %4 +} + +// CHECK-LABEL: sil @no_invert_wrong_literal : +// CHECK: builtin "cmp_ugt_Int64" +// CHECK: } // end sil function 'no_invert_wrong_literal' +sil @no_invert_wrong_literal : $@convention(thin) (Builtin.Int64, Builtin.Int64) -> Builtin.Int1 { +bb0(%0 : $Builtin.Int64, %1 : $Builtin.Int64): + %2 = builtin "cmp_ugt_Int64"(%0, %1) : $Builtin.Int1 + %3 = integer_literal $Builtin.Int1, 0 + %4 = builtin "xor_Int1"(%2, %3) : $Builtin.Int1 + return %4 +} + +// CHECK-LABEL: sil @no_invert_unknown_rhs : +// CHECK: builtin "cmp_ugt_Int64" +// CHECK: } // end sil function 'no_invert_unknown_rhs' +sil @no_invert_unknown_rhs : $@convention(thin) (Builtin.Int64, Builtin.Int64, Builtin.Int1) -> Builtin.Int1 { +bb0(%0 : $Builtin.Int64, %1 : $Builtin.Int64, %2 : $Builtin.Int1): + %3 = builtin "cmp_ugt_Int64"(%0, %1) : $Builtin.Int1 + %4 = builtin "xor_Int1"(%3, %2) : $Builtin.Int1 + return %4 +} + +// CHECK-LABEL: sil @no_invert_no_comparison : +// CHECK: %2 = builtin "xor_Int1" +// CHECK: return %2 +// CHECK: } // end sil function 'no_invert_no_comparison' +sil @no_invert_no_comparison : $@convention(thin) (Builtin.Int1) -> Builtin.Int1 { +bb0(%0 : $Builtin.Int1): + %1 = integer_literal $Builtin.Int1, -1 + %2 = builtin "xor_Int1"(%0, %1) : $Builtin.Int1 + return %2 +} + +// CHECK-LABEL: sil @no_invert_wrong_comparison : +// CHECK: builtin "fcmp_ord_FPIEEE32" +// CHECK: } // end sil function 'no_invert_wrong_comparison' +sil @no_invert_wrong_comparison : $@convention(thin) (Builtin.FPIEEE32, Builtin.FPIEEE32) -> Builtin.Int1 { +bb0(%0 : $Builtin.FPIEEE32, %1 : $Builtin.FPIEEE32): + %2 = builtin "fcmp_ord_FPIEEE32"(%0 : $Builtin.FPIEEE32, %1 : $Builtin.FPIEEE32) : $Builtin.Int1 + %3 = integer_literal $Builtin.Int1, -1 + %4 = builtin "xor_Int1"(%2, %3) : $Builtin.Int1 + return %4 +} + diff --git a/test/SILOptimizer/span_bounds_check_tests.swift b/test/SILOptimizer/span_bounds_check_tests.swift index 99522d0113b7e..0d7bbd5d493d4 100644 --- a/test/SILOptimizer/span_bounds_check_tests.swift +++ b/test/SILOptimizer/span_bounds_check_tests.swift @@ -353,3 +353,18 @@ public func inout_span_sum_iterate_to_unknown_with_trap_dontopt(_ v: inout Span< return sum } +// Check that the loop is fully optimized, including bounds check elimination and vectorization. + +// CHECK-IR-LABEL: define {{.*}} @"$s4test4loop4over5usingSis4SpanVys5UInt8VG_AFySiGtF" +// CHECK-IR: vector.body: +// CHECK-IR: ret +public func loop(over a: borrowing Span, using b: borrowing Span) -> Int { + var result = 0 + precondition(UInt8.max < b.count) + for i in a.indices { + let idx = Int(a[i]) + result &+= b[idx] + } + return result +} +