Skip to content

Optimizer: add some floating point optimizations #80404

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Apr 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions include/swift/AST/SemanticAttrs.def
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ SEMANTICS_ATTR(TYPENAME, "typeName")
SEMANTICS_ATTR(OPTIMIZE_SIL_SPECIALIZE_GENERIC_NEVER, "optimize.sil.specialize.generic.never")
SEMANTICS_ATTR(OPTIMIZE_SIL_SPECIALIZE_GENERIC_PARTIAL_NEVER,
"optimize.sil.specialize.generic.partial.never")
SEMANTICS_ATTR(OPTIMIZE_SIL_INLINE_CONSTANT_ARGUMENTS,
"optimize.sil.inline.constant.arguments")
SEMANTICS_ATTR(OPTIMIZE_SIL_SPECIALIZE_GENERIC_SIZE_NEVER,
"optimize.sil.specialize.generic.size.never")
SEMANTICS_ATTR(OPTIMIZE_SIL_SPECIALIZE_OWNED2GUARANTEE_NEVER,
Expand Down
32 changes: 32 additions & 0 deletions lib/SILOptimizer/Transforms/PerformanceInliner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,33 @@ bool isProfitableToInlineAutodiffVJP(SILFunction *vjp, SILFunction *caller,
return true;
}

static bool isConstantValue(SILValue v, ValueSet &visited) {
if (!visited.insert(v))
return true;

if (isa<LiteralInst>(v))
return true;
if (auto *s = dyn_cast<StructInst>(v)) {
for (Operand &op : s->getAllOperands()) {
if (!isConstantValue(op.get(), visited))
return false;
}
return true;
}
return false;
}

static bool hasConstantArguments(FullApplySite fas) {
ValueSet visited(fas.getFunction());
for (Operand &op : fas.getArgumentOperands()) {
if (!fas.isIndirectResultOperand(op)) {
if (!isConstantValue(op.get(), visited))
return false;
}
}
return true;
}

bool SILPerformanceInliner::isProfitableToInline(
FullApplySite AI, Weight CallerWeight, ConstantTracker &callerTracker,
int &NumCallerBlocks,
Expand Down Expand Up @@ -524,6 +551,11 @@ bool SILPerformanceInliner::isProfitableToInline(
return true;
}

if (Callee->hasSemanticsAttr(semantics::OPTIMIZE_SIL_INLINE_CONSTANT_ARGUMENTS) &&
hasConstantArguments(AI)) {
return true;
}

// Bail out if this generic call can be optimized by means of
// the generic specialization, because we prefer generic specialization
// to inlining of generics.
Expand Down
360 changes: 70 additions & 290 deletions lib/SILOptimizer/Utils/ConstantFolding.cpp

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions stdlib/public/core/FloatingPointTypes.swift.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,7 @@ extension ${Self}: BinaryFloatingPoint {
}

@inlinable
@_semantics("optimize.sil.inline.constant.arguments")
public var exponent: Int {
if !isFinite { return .max }
if isZero { return .min }
Expand Down Expand Up @@ -884,6 +885,7 @@ extension ${Self}: BinaryFloatingPoint {
}

@inlinable
@_semantics("optimize.sil.inline.constant.arguments")
public var significandWidth: Int {
let trailingZeroBits = significandBitPattern.trailingZeroBitCount
if isNormal {
Expand Down
1 change: 1 addition & 0 deletions stdlib/public/core/Integers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2696,6 +2696,7 @@ extension FixedWidthInteger {
extension FixedWidthInteger {
@inlinable
@_semantics("optimize.sil.specialize.generic.partial.never")
@_semantics("optimize.sil.inline.constant.arguments")
public // @testable
static func _convert<Source: BinaryFloatingPoint>(
from source: Source
Expand Down
6 changes: 4 additions & 2 deletions test/AutoDiff/SILOptimizer/pullback_inlining.swift
Original file line number Diff line number Diff line change
Expand Up @@ -86,5 +86,7 @@ func caller_of_more_complex_pb_with_control_flow() -> Float {
gradient(at: Float(1), of: more_complex_pb_with_control_flow)
}

// CHECK: decision {{.*}} $s17pullback_inlining33more_complex_pb_with_control_flow1xS2f_tFTJpSpSr
// CHECK-NEXT: "pullback of pullback_inlining.more_complex_pb_with_control_flow(x:)" inlined into "caller_of_more_complex_pb_with_control_flow"
// TODO: check why this function is not inlined and why it should be inlined
// CHECKx: decision {{.*}} $s17pullback_inlining33more_complex_pb_with_control_flow1xS2f_tFTJpSpSr
// CHECKx-NEXT: "pullback of pullback_inlining.more_complex_pb_with_control_flow(x:)" inlined into "caller_of_more_complex_pb_with_control_flow"

2 changes: 0 additions & 2 deletions test/ConstValues/IntegerExpressions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,5 @@
@const let constGlobal1: Int = (42 + 42 + 42) / 3
@const let constGlobal2: Int = MemoryLayout<UInt32>.size + 4
@const let constGlobal3: Int = Int(17.0 / 3.5)
// expected-error@-1 {{@const value should be initialized with a compile-time value}}
// expected-error@-2 {{global variable must be a compile-time constant}} // Remove this once we common out the diagnostics
@const let constGlobal4: Int = constGlobal1 + 1
@const let constGlobal5: Int = -constGlobal1 + 1
47 changes: 43 additions & 4 deletions test/SILOptimizer/constant_fold_float.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,49 @@ public func dont_fold_inf_cmp(_ f: Float) -> Bool {
(f + 0) < .infinity
}

// CHECK-LABEL: sil @$s4test014dont_fold_inf_D4_cmpSbyF :
// CHECK: builtin "fcmp_olt_FPIEEE32"
// CHECK: } // end sil function '$s4test014dont_fold_inf_D4_cmpSbyF'
public func dont_fold_inf_inf_cmp() -> Bool {
// CHECK-LABEL: sil @$s4test09fold_inf_C4_cmpSbyF :
// CHECK: [[ZERO:%.*]] = integer_literal $Builtin.Int1, 0
// CHECK: [[B:%.*]] = struct $Bool ([[ZERO]])
// CHECK: return [[B]]
// CHECK: } // end sil function '$s4test09fold_inf_C4_cmpSbyF'
public func fold_inf_inf_cmp() -> Bool {
0x1.0p128 < Float.infinity
}

public struct FP32: FixedPoint {
public var bits: Int32

public init(bits: Bits) {
self.bits = bits
}
}

// CHECK-LABEL: sil @$s4test0A19FloatLiteralFoldingAA4FP32VyF :
// CHECK: [[L:%.*]] = integer_literal $Builtin.Int32, 65536
// CHECK: [[I:%.*]] = struct $Int32 ([[L]])
// CHECK: [[F:%.*]] = struct $FP32 ([[I]])
// CHECK: return [[F]]
// CHECK: } // end sil function '$s4test0A19FloatLiteralFoldingAA4FP32VyF'
public func testFloatLiteralFolding() -> FP32 {
return 1.0
}

public protocol FixedPoint: ExpressibleByFloatLiteral {
associatedtype Bits: FixedWidthInteger
var bits: Bits { get set }

init(bits: Bits)
}

extension FixedPoint {
public init(floatLiteral value: Double) {
self.init(value)
}

init<F: BinaryFloatingPoint>(_ value: F) {
let s = F(sign: .plus, exponent: F.Exponent(16), significand: 1)
let r = (s * value).rounded(.toNearestOrEven)
self.init(bits: Bits(exactly: r)!)
}
}

78 changes: 63 additions & 15 deletions test/SILOptimizer/constant_propagation.sil
Original file line number Diff line number Diff line change
Expand Up @@ -885,29 +885,29 @@ bb0(%0 : $Builtin.FPIEEE32):
// CHECK: } // end sil function 'dont_fold_comparison_with_inf'
}

sil @dont_fold_comparison_with_inf2 : $@convention(thin) () -> Builtin.Int1 {
sil @fold_comparison_with_inf : $@convention(thin) () -> Builtin.Int1 {
bb0:
%2 = float_literal $Builtin.FPIEEE32, 0x7F800000 // +Inf // user: %3
%5 = integer_literal $Builtin.Int32, 2139095040
%6 = builtin "bitcast_Int32_FPIEEE32"(%5 : $Builtin.Int32) : $Builtin.FPIEEE32
%9 = builtin "fcmp_olt_FPIEEE32"(%2 : $Builtin.FPIEEE32, %6 : $Builtin.FPIEEE32) : $Builtin.Int1
return %9 : $Builtin.Int1

// CHECK-LABEL: sil @dont_fold_comparison_with_inf2 :
// CHECK: [[R:%.*]] = builtin "fcmp_olt_FPIEEE32"
// CHECK-LABEL: sil @fold_comparison_with_inf :
// CHECK: [[R:%.*]] = integer_literal $Builtin.Int1, 0
// CHECK: return [[R]]
// CHECK: } // end sil function 'dont_fold_comparison_with_inf2'
// CHECK: } // end sil function 'fold_comparison_with_inf'
}

// fold float comparison operations with Infinity/NaN when the other argument is not constant
sil @fold_float_comparison_with_non_constant_arg : $@convention(thin) (Float) -> () {
bb0(%0: $Float):
%1 = struct_extract %0 : $Float, #Float._value

%2 = integer_literal $Builtin.Int32, 2143289344 // user: %3
%2 = integer_literal $Builtin.Int32, 2143289344
%3 = builtin "bitcast_Int32_FPIEEE32"(%2 : $Builtin.Int32) : $Builtin.FPIEEE32 // NaN

%4 = integer_literal $Builtin.Int32, 2139095040 // user: %4
%4 = integer_literal $Builtin.Int32, 2139095040
%5 = builtin "bitcast_Int32_FPIEEE32"(%4 : $Builtin.Int32) : $Builtin.FPIEEE32 // Inf

%6 = builtin "fcmp_oge_FPIEEE32"(%3 : $Builtin.FPIEEE32, %1 : $Builtin.FPIEEE32) : $Builtin.Int1
Expand All @@ -917,20 +917,19 @@ bb0(%0: $Float):
return %8 : $()

// CHECK-LABEL: sil @fold_float_comparison_with_non_constant_arg
// CHECK: bb0(%0 : $Float):
// CHECK-NEXT: %1 = struct_extract %0 : $Float, #Float._value // user: %5
// CHECK-NEXT: %2 = integer_literal $Builtin.Int32, 2139095040 // user: %3
// CHECK-NEXT: %3 = builtin "bitcast_Int32_FPIEEE32"(%2 : $Builtin.Int32) : $Builtin.FPIEEE32 // user: %5
// CHECK: bb0(%0 : $Float):
// CHECK-NEXT: %1 = struct_extract %0 : $Float, #Float._value
// CHECK-NEXT: %2 = float_literal $Builtin.FPIEEE32, 0x7F800000 // +Inf

// Comparison with NaN is always folded
// CHECK-NEXT: %4 = integer_literal $Builtin.Int1, 0
// CHECK-NEXT: %3 = integer_literal $Builtin.Int1, 0

// Comparison with Inf is not folded unless the other argument can be proven to be constant
// CHECK-NEXT: %5 = builtin "fcmp_oge_FPIEEE32"(%3 : $Builtin.FPIEEE32, %1 : $Builtin.FPIEEE32) : $Builtin.Int1
// CHECK-NEXT: %4 = builtin "fcmp_oge_FPIEEE32"(%2 : $Builtin.FPIEEE32, %1 : $Builtin.FPIEEE32) : $Builtin.Int1

// CHECK-NEXT: %6 = tuple () // user: %7
// CHECK-NEXT: return %6 : $() // id: %7
// CHECK-NEXT: } // end sil function 'fold_float_comparison_with_non_constant_arg'
// CHECK-NEXT: %5 = tuple ()
// CHECK-NEXT: return %5
// CHECK-NEXT: } // end sil function 'fold_float_comparison_with_non_constant_arg'
}

sil @fold_inf_comparisons_with_itself : $@convention(thin) () -> () {
Expand Down Expand Up @@ -987,6 +986,55 @@ sil @fold_inf_comparisons_with_itself : $@convention(thin) () -> () {
// CHECK: } // end sil function 'fold_inf_comparisons_with_itself'
}


// CHECK-LABEL: sil @fold_cast_to_fp :
// CHECK: bb0:
// CHECK-NEXT: %0 = float_literal $Builtin.FPIEEE64, 0x3FF0000000000000 // 1
// CHECK-NEXT: return %0 : $Builtin.FPIEEE64
// CHECK: } // end sil function 'fold_cast_to_fp'
sil @fold_cast_to_fp : $@convention(thin) () -> Builtin.FPIEEE64 {
bb0:
%0 = integer_literal $Builtin.Int64, 1
%1 = builtin "sitofp_Int64_FPIEEE64"(%0) : $Builtin.FPIEEE64
return %1
}

// CHECK-LABEL: sil @fold_bitcast_to_fp :
// CHECK: bb0:
// CHECK-NEXT: %0 = float_literal $Builtin.FPIEEE64, 0x3FF0000000000000
// CHECK-NEXT: return %0
// CHECK: } // end sil function 'fold_bitcast_to_fp'
sil @fold_bitcast_to_fp : $@convention(thin) () -> Builtin.FPIEEE64 {
bb0:
%0 = integer_literal $Builtin.Int64, 4607182418800017408
%1 = builtin "bitcast_Int64_FPIEEE64"(%0) : $Builtin.FPIEEE64
return %1
}

// CHECK-LABEL: sil @fold_bitcast_to_int :
// CHECK: bb0:
// CHECK-NEXT: %0 = integer_literal $Builtin.Int64, 4607182418800017408
// CHECK-NEXT: return %0
// CHECK: } // end sil function 'fold_bitcast_to_int'
sil @fold_bitcast_to_int : $@convention(thin) () -> Builtin.Int64 {
bb0:
%0 = float_literal $Builtin.FPIEEE64, 0x3FF0000000000000
%1 = builtin "bitcast_FPIEEE64_Int64"(%0) : $Builtin.Int64
return %1
}

// CHECK-LABEL: sil @fold_rint :
// CHECK: bb0:
// CHECK-NEXT: %0 = float_literal $Builtin.FPIEEE64, 0x40F0000000000000 // 65536
// CHECK-NEXT: return %0 : $Builtin.FPIEEE64
// CHECK: } // end sil function 'fold_rint'
sil @fold_rint : $@convention(thin) () -> Builtin.FPIEEE64 {
bb0:
%0 = float_literal $Builtin.FPIEEE64, 0x40F0000000000000 // 65536
%1 = builtin "int_rint_FPIEEE64"(%0) : $Builtin.FPIEEE64
return %1
}

// fold float comparison operations with opaque values, that may be constant
// but are hidden behind a struct or a tuple.
sil @fold_float_comparison_between_opaque_val : $@convention(thin) () -> () {
Expand Down