Skip to content

Commit 66334f8

Browse files
authored
Merge pull request swiftlang#79290 from artemcm/ConstProtoSIL
[Compile Time Values] Implement a mandatory SIL pass to verify '@const' values
2 parents 2936a07 + 281f84d commit 66334f8

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

70 files changed

+961
-222
lines changed

SwiftCompilerSources/Sources/AST/Declarations.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,9 @@ final public class AssociatedTypeDecl: TypeDecl {}
8383

8484
final public class ModuleDecl: TypeDecl {}
8585

86-
public class AbstractStorageDecl: ValueDecl {}
86+
public class AbstractStorageDecl: ValueDecl {
87+
final public var isConst: Bool { bridged.AbstractStorage_isConst() }
88+
}
8789

8890
public class VarDecl: AbstractStorageDecl {}
8991

SwiftCompilerSources/Sources/Optimizer/ModulePasses/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
# See http://swift.org/CONTRIBUTORS.txt for Swift project authors
88

99
swift_compiler_sources(Optimizer
10+
DiagnoseUnknownConstValues.swift
1011
MandatoryPerformanceOptimizations.swift
1112
ReadOnlyGlobalVariables.swift
1213
StackProtection.swift
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
//===--------- DiagnoseUnknownConstValues.swift --------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import SIL
14+
import AST
15+
16+
/// Performs mandatory diagnostic pass for emitting errors for '@const' values which the compiler was not able to
17+
/// simplify/interpret/reduce to a symbolic value.
18+
///
19+
let diagnoseUnknownConstValues = ModulePass(name: "diagnose-unknown-const-values") {
20+
(moduleContext: ModulePassContext) in
21+
var constExprEvaluator: ConstExpressionEvaluator = .init()
22+
defer { constExprEvaluator.deinitialize() }
23+
24+
// Verify all const globals to be initialized with compile-time known values
25+
verifyGlobals(moduleContext)
26+
27+
// Verify @const lets appearing as local variables
28+
verifyLocals(constExprEvaluator: &constExprEvaluator,
29+
moduleContext)
30+
31+
// For each function call, ensure arguments to @const parameters are all compile-time known values
32+
verifyCallArguments(constExprEvaluator: &constExprEvaluator,
33+
moduleContext)
34+
35+
// For each `@const` function, ensure it is fully evaluable/interpretable at compile time
36+
verifyFunctions(moduleContext)
37+
}
38+
39+
private func verifyGlobals(_ context: ModulePassContext) {
40+
for gv in context.globalVariables where gv.isConst {
41+
if gv.staticInitValue == nil {
42+
context.diagnosticEngine.diagnose(gv.varDecl?.location.sourceLoc,
43+
.require_const_initializer_for_const)
44+
}
45+
}
46+
}
47+
48+
private func verifyLocals(constExprEvaluator: inout ConstExpressionEvaluator,
49+
_ context: ModulePassContext) {
50+
for f in context.functions {
51+
for i in f.instructions {
52+
if let dbi = i as? DebugValueInst {
53+
verifyLocal(debugValueInst: dbi, constExprState: &constExprEvaluator, in: context)
54+
}
55+
}
56+
}
57+
}
58+
59+
private func verifyLocal(debugValueInst: DebugValueInst,
60+
constExprState: inout ConstExpressionEvaluator,
61+
in context: ModulePassContext) {
62+
guard let localVarDecl = debugValueInst.varDecl,
63+
!(localVarDecl is ParamDecl),
64+
localVarDecl.isConst else {
65+
return
66+
}
67+
68+
if !constExprState.isConstantValue(debugValueInst.operand.value) {
69+
context.diagnosticEngine.diagnose(debugValueInst.location.sourceLoc,
70+
.require_const_initializer_for_const)
71+
}
72+
}
73+
74+
private func verifyCallArguments(constExprEvaluator: inout ConstExpressionEvaluator,
75+
_ context: ModulePassContext) {
76+
for f in context.functions {
77+
for i in f.instructions {
78+
// TODO: Consider closures (partial_apply)
79+
if let apply = i as? FullApplySite {
80+
verifyCallArguments(apply: apply, constExprState: &constExprEvaluator, in: context)
81+
}
82+
}
83+
}
84+
}
85+
86+
private func verifyCallArguments(apply: FullApplySite,
87+
constExprState: inout ConstExpressionEvaluator,
88+
in context: ModulePassContext) {
89+
guard let calleeFn = apply.referencedFunction else {
90+
return
91+
}
92+
for (paramIdx, param) in calleeFn.convention.parameters.enumerated() where param.hasOption(.const) {
93+
let matchingOperand = apply.parameterOperands[paramIdx]
94+
if !constExprState.isConstantValue(matchingOperand.value) {
95+
context.diagnosticEngine.diagnose(apply.location.sourceLoc,
96+
.require_const_arg_for_parameter)
97+
}
98+
}
99+
}
100+
101+
private func verifyFunctions(_ context: ModulePassContext) {
102+
// TODO: Implement
103+
}

SwiftCompilerSources/Sources/Optimizer/ModulePasses/MandatoryPerformanceOptimizations.swift

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,7 @@ let mandatoryPerformanceOptimizations = ModulePass(name: "mandatory-performance-
3535
if moduleContext.options.enableEmbeddedSwift {
3636
worklist.addAllNonGenericFunctions(of: moduleContext)
3737
} else {
38-
worklist.addAllPerformanceAnnotatedFunctions(of: moduleContext)
39-
worklist.addAllAnnotatedGlobalInitOnceFunctions(of: moduleContext)
38+
worklist.addAllMandatoryRequiredFunctions(of: moduleContext)
4039
}
4140

4241
optimizeFunctionsTopDown(using: &worklist, moduleContext)
@@ -483,10 +482,29 @@ fileprivate struct FunctionWorklist {
483482
}
484483
return nil
485484
}
486-
487-
mutating func addAllPerformanceAnnotatedFunctions(of moduleContext: ModulePassContext) {
488-
for f in moduleContext.functions where f.performanceConstraints != .none {
489-
pushIfNotVisited(f)
485+
486+
mutating func addAllMandatoryRequiredFunctions(of moduleContext: ModulePassContext) {
487+
for f in moduleContext.functions {
488+
// Performance annotated functions
489+
if f.performanceConstraints != .none {
490+
pushIfNotVisited(f)
491+
}
492+
493+
// Annotated global init-once functions
494+
if f.isGlobalInitOnceFunction {
495+
if let global = f.getInitializedGlobal(),
496+
global.mustBeInitializedStatically {
497+
pushIfNotVisited(f)
498+
}
499+
}
500+
501+
// @const global init-once functions
502+
if f.isGlobalInitOnceFunction {
503+
if let global = f.getInitializedGlobal(),
504+
global.mustBeInitializedStatically {
505+
pushIfNotVisited(f)
506+
}
507+
}
490508
}
491509
}
492510

@@ -497,15 +515,6 @@ fileprivate struct FunctionWorklist {
497515
return
498516
}
499517

500-
mutating func addAllAnnotatedGlobalInitOnceFunctions(of moduleContext: ModulePassContext) {
501-
for f in moduleContext.functions where f.isGlobalInitOnceFunction {
502-
if let global = f.getInitializedGlobal(),
503-
global.mustBeInitializedStatically {
504-
pushIfNotVisited(f)
505-
}
506-
}
507-
}
508-
509518
mutating func addCallees(of function: Function) {
510519
for inst in function.instructions {
511520
switch inst {

SwiftCompilerSources/Sources/Optimizer/PassManager/PassRegistration.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ private func registerForSILCombine<InstType: SILCombineSimplifiable>(
6464
private func registerSwiftPasses() {
6565
// Module passes
6666
registerPass(mandatoryPerformanceOptimizations, { mandatoryPerformanceOptimizations.run($0) })
67+
registerPass(diagnoseUnknownConstValues, { diagnoseUnknownConstValues.run($0)})
6768
registerPass(readOnlyGlobalVariablesPass, { readOnlyGlobalVariablesPass.run($0) })
6869
registerPass(stackProtection, { stackProtection.run($0) })
6970

SwiftCompilerSources/Sources/SIL/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ add_swift_compiler_module(SIL
1616
ASTExtensions.swift
1717
BasicBlock.swift
1818
Builder.swift
19+
ConstExpressionEvaluator.swift
1920
DeclRef.swift
2021
Effects.swift
2122
ForwardingInstruction.swift
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
//===--- ConstExpressionEvaluator.swift - ---------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import AST
14+
import SILBridging
15+
16+
public struct ConstExpressionEvaluator {
17+
var bridged: BridgedConstExprFunctionState
18+
19+
public init(bridged: BridgedConstExprFunctionState) { self.bridged = bridged }
20+
public init() {
21+
self.bridged = BridgedConstExprFunctionState.create()
22+
}
23+
24+
mutating public func isConstantValue(_ value: Value) -> Bool {
25+
return bridged.isConstantValue(value.bridged)
26+
}
27+
28+
/// TODO: once we have move-only types, make this a real deinit.
29+
mutating public func deinitialize() {
30+
bridged.deinitialize()
31+
}
32+
}

SwiftCompilerSources/Sources/SIL/FunctionConvention.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,15 @@ public struct ParameterInfo : CustomStringConvertible {
163163
public let convention: ArgumentConvention
164164
public let options: UInt8
165165
public let hasLoweredAddresses: Bool
166+
167+
// Must be kept consistent with 'SILParameterInfo::Flag'
168+
public enum Flag : UInt8 {
169+
case notDifferentiable = 0x1
170+
case sending = 0x2
171+
case isolated = 0x4
172+
case implicitLeading = 0x8
173+
case const = 0x10
174+
};
166175

167176
public init(type: CanonicalType, convention: ArgumentConvention, options: UInt8, hasLoweredAddresses: Bool) {
168177
self.type = type
@@ -193,6 +202,10 @@ public struct ParameterInfo : CustomStringConvertible {
193202
public var description: String {
194203
"\(convention): \(type)"
195204
}
205+
206+
public func hasOption(_ flag: Flag) -> Bool {
207+
return options & flag.rawValue != 0
208+
}
196209
}
197210

198211
extension FunctionConvention {

SwiftCompilerSources/Sources/SIL/GlobalVariable.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,14 @@ final public class GlobalVariable : CustomStringConvertible, HasShortDescription
7171
return bridged.mustBeInitializedStatically()
7272
}
7373

74+
public var isConst: Bool {
75+
return bridged.isConstValue()
76+
}
77+
78+
public var sourceLocation: SourceLoc? {
79+
return SourceLoc(bridged: bridged.getSourceLocation())
80+
}
81+
7482
public static func ==(lhs: GlobalVariable, rhs: GlobalVariable) -> Bool {
7583
lhs === rhs
7684
}

include/swift/AST/ASTBridging.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,7 @@ struct BridgedDeclObj {
384384
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedSourceLoc Value_getNameLoc() const;
385385
BRIDGED_INLINE bool hasClangNode() const;
386386
BRIDGED_INLINE bool Value_isObjC() const;
387+
BRIDGED_INLINE bool AbstractStorage_isConst() const;
387388
BRIDGED_INLINE bool GenericType_isGenericAtAnyLevel() const;
388389
BRIDGED_INLINE bool NominalType_isGlobalActor() const;
389390
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE OptionalBridgedDeclObj NominalType_getValueTypeDestructor() const;

0 commit comments

Comments
 (0)