Skip to content

Commit b0b3ea3

Browse files
committed
SILOptimizer: add a pass to perform target specific constant folding.
TargetConstantFolding performs constant folding for target-specific values: ``` MemoryLayout<S>.size MemoryLayout<S>.alignment MemoryLayout<S>.stride ``` Constant folding those expressions in the middle of the SIL pipeline enables other optimizations to e.g. allow such expressions in statically allocated global variables (done by the GlobalOpt pass). The implementation requires to create a temporary IRGenModule, which is used to get actual constant sizes/alignments from IRGen's type lowering. rdar://94831524
1 parent 86992c6 commit b0b3ea3

File tree

5 files changed

+222
-0
lines changed

5 files changed

+222
-0
lines changed

include/swift/SILOptimizer/PassManager/Passes.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,8 @@ PASS(ReleaseHoisting, "release-hoisting",
356356
"SIL release Hoisting")
357357
PASS(LateReleaseHoisting, "late-release-hoisting",
358358
"Late SIL release Hoisting Preserving Epilogues")
359+
PASS(TargetConstantFolding, "target-constant-folding",
360+
"Target specific constant folding")
359361
IRGEN_PASS(LoadableByAddress, "loadable-address",
360362
"SIL Large Loadable type by-address lowering.")
361363
PASS(MandatorySILLinker, "mandatory-linker",

lib/IRGen/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ add_swift_host_library(swiftIRGen STATIC
5757
Outlining.cpp
5858
StructLayout.cpp
5959
SwiftTargetInfo.cpp
60+
TargetConstantFolding.cpp
6061
TypeLayout.cpp
6162
TypeLayoutDumper.cpp
6263
TypeLayoutVerifier.cpp

lib/IRGen/TargetConstantFolding.cpp

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
//===--- TargetConstantFolding.cpp ----------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2022 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+
// This pass lowers loadable SILTypes. On completion, the SILType of every
12+
// function argument is an address instead of the type itself.
13+
// This reduces the code size.
14+
// Consequently, this pass is required for IRGen.
15+
// It is a mandatory IRGen preparation pass (not a diagnostic pass).
16+
//
17+
//===----------------------------------------------------------------------===//
18+
19+
#define DEBUG_TYPE "target-constant-folding"
20+
#include "IRGenModule.h"
21+
#include "swift/SIL/SILBuilder.h"
22+
#include "swift/SILOptimizer/PassManager/Transforms.h"
23+
#include "swift/SILOptimizer/Utils/InstructionDeleter.h"
24+
#include "llvm/Support/Debug.h"
25+
26+
using namespace swift;
27+
using namespace swift::irgen;
28+
29+
namespace {
30+
31+
/// Performs constant folding for target-specific values.
32+
///
33+
/// Specifically, this optimization constant folds
34+
/// ```
35+
/// MemoryLayout<S>.size
36+
/// MemoryLayout<S>.alignment
37+
/// MemoryLayout<S>.stride
38+
/// ```
39+
/// Constant folding those expressions in the middle of the SIL pipeline
40+
/// enables other optimizations to e.g. allow such expressions in statically
41+
/// allocated global variables (done by the GlobalOpt pass).
42+
class TargetConstantFolding : public SILModuleTransform {
43+
private:
44+
/// The entry point to the transformation.
45+
void run() override {
46+
auto *irgenOpts = getModule()->getIRGenOptionsOrNull();
47+
if (!irgenOpts)
48+
return;
49+
50+
// We need an IRGenModule to get the actual sizes from type lowering.
51+
// Creating an IRGenModule involves some effort. Therefore this is a
52+
// module pass rather than a function pass so that this one-time setup
53+
// only needs to be done once and not for all functions in a module.
54+
IRGenerator irgen(*irgenOpts, *getModule());
55+
auto targetMachine = irgen.createTargetMachine();
56+
if (!targetMachine)
57+
return;
58+
IRGenModule IGM(irgen, std::move(targetMachine));
59+
60+
// Scan all instructions in the module for constant foldable instructions.
61+
for (SILFunction &function : *getModule()) {
62+
63+
if (!function.shouldOptimize())
64+
continue;
65+
66+
bool changed = false;
67+
for (SILBasicBlock &block : function) {
68+
InstructionDeleter deleter;
69+
70+
for (SILInstruction *inst : deleter.updatingRange(&block)) {
71+
if (constFold(inst, IGM)) {
72+
deleter.forceDelete(inst);
73+
changed = true;
74+
}
75+
}
76+
deleter.cleanupDeadInstructions();
77+
}
78+
if (changed) {
79+
invalidateAnalysis(&function, SILAnalysis::InvalidationKind::Instructions);
80+
}
81+
}
82+
}
83+
84+
/// Constant fold a single instruction.
85+
///
86+
/// Returns true if `inst` was replaced and can be deleted.
87+
bool constFold(SILInstruction *inst, IRGenModule &IGM) {
88+
auto *bi = dyn_cast<BuiltinInst>(inst);
89+
if (!bi)
90+
return false;
91+
92+
llvm::Constant *c = nullptr;
93+
uint64_t offset = 0;
94+
95+
switch (bi->getBuiltinInfo().ID) {
96+
case BuiltinValueKind::Sizeof:
97+
c = getTypeInfoOfBuiltin(bi, IGM).getStaticSize(IGM);
98+
break;
99+
case BuiltinValueKind::Alignof:
100+
c = getTypeInfoOfBuiltin(bi, IGM).getStaticAlignmentMask(IGM);
101+
// The constant is the alignment _mask_. We get the actual alignment by
102+
// adding 1.
103+
offset = 1;
104+
break;
105+
case BuiltinValueKind::Strideof:
106+
c = getTypeInfoOfBuiltin(bi, IGM).getStaticStride(IGM);
107+
break;
108+
default:
109+
return false;
110+
}
111+
auto *intConst = dyn_cast_or_null<llvm::ConstantInt>(c);
112+
if (!intConst)
113+
return false;
114+
115+
APInt value = intConst->getValue();
116+
value += APInt(value.getBitWidth(), offset);
117+
118+
// Replace the builtin by an integer literal.
119+
SILBuilderWithScope builder(bi);
120+
auto *intLit = builder.createIntegerLiteral(bi->getLoc(), bi->getType(),
121+
value);
122+
bi->replaceAllUsesWith(intLit);
123+
return true;
124+
}
125+
126+
const TypeInfo &getTypeInfoOfBuiltin(BuiltinInst *bi, IRGenModule &IGM) {
127+
SubstitutionMap subs = bi->getSubstitutions();
128+
SILType lowered = IGM.getLoweredType(subs.getReplacementTypes()[0]);
129+
return IGM.getTypeInfo(lowered);
130+
}
131+
};
132+
133+
} // end anonymous namespace
134+
135+
SILTransform *swift::createTargetConstantFolding() {
136+
return new TargetConstantFolding();
137+
}

lib/SILOptimizer/PassManager/PassPipeline.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -666,6 +666,7 @@ static void addMidLevelFunctionPipeline(SILPassPipelinePlan &P) {
666666
static void addClosureSpecializePassPipeline(SILPassPipelinePlan &P) {
667667
P.startPipeline("ClosureSpecialize");
668668
P.addDeadFunctionAndGlobalElimination();
669+
P.addTargetConstantFolding();
669670
P.addDeadStoreElimination();
670671
P.addDeadObjectElimination();
671672

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
// RUN: %target-swift-frontend -primary-file %s -O -sil-verify-all -module-name=test -emit-sil | %FileCheck %s
2+
3+
// Make a runtime test to check that the values are correct.
4+
// RUN: %empty-directory(%t)
5+
// RUN: %target-build-swift -O -module-name=test %s -o %t/a.out
6+
// RUN: %target-run %t/a.out | %FileCheck %s -check-prefix=CHECK-OUTPUT
7+
8+
// REQUIRES: executable_test,swift_stdlib_no_asserts,optimized_stdlib
9+
10+
11+
struct S {
12+
var i: Int
13+
var b: Int8
14+
15+
// CHECK-LABEL: sil_global hidden [let] @$s4test1SV4sizeSivpZ : $Int = {
16+
// CHECK-NEXT: integer_literal
17+
static let size = MemoryLayout<S>.size
18+
19+
// CHECK-LABEL: sil_global hidden [let] @$s4test1SV9alignmentSivpZ : $Int = {
20+
// CHECK-NEXT: integer_literal
21+
static let alignment = MemoryLayout<S>.alignment
22+
23+
// CHECK-LABEL: sil_global hidden [let] @$s4test1SV6strideSivpZ : $Int = {
24+
// CHECK-NEXT: integer_literal
25+
static let stride = MemoryLayout<S>.stride
26+
27+
static let doubleSize = MemoryLayout<S>.size * 2
28+
}
29+
30+
// CHECK-LABEL: sil @$s4test14noConstantSizeySixmlF
31+
// CHECK: builtin "sizeof"<T>
32+
// CHECK: } // end sil function '$s4test14noConstantSizeySixmlF'
33+
public func noConstantSize<T>(_ t: T.Type) -> Int {
34+
return MemoryLayout<T>.size
35+
}
36+
37+
// Check that there is not constant propagation if optimizations are disabled.
38+
// This is important for the runtime check to make sure that we are comparing
39+
// SIL constant propagated values with IRGen values.
40+
41+
// CHECK-LABEL: sil {{.*}} @$s4test7getSizeSiyF
42+
// CHECK: builtin "sizeof"<S>
43+
// CHECK: } // end sil function '$s4test7getSizeSiyF'
44+
@_optimize(none)
45+
func getSize() -> Int {
46+
return MemoryLayout<S>.size
47+
}
48+
49+
// CHECK-LABEL: sil {{.*}} @$s4test12getAlignmentSiyF
50+
// CHECK: builtin "alignof"<S>
51+
// CHECK: } // end sil function '$s4test12getAlignmentSiyF'
52+
@_optimize(none)
53+
func getAlignment() -> Int {
54+
return MemoryLayout<S>.alignment
55+
}
56+
57+
// CHECK-LABEL: sil {{.*}} @$s4test9getStrideSiyF
58+
// CHECK: builtin "strideof"<S>
59+
// CHECK: } // end sil function '$s4test9getStrideSiyF'
60+
@_optimize(none)
61+
func getStride() -> Int {
62+
return MemoryLayout<S>.stride
63+
}
64+
65+
@inline(never)
66+
func testit() {
67+
// CHECK-OUTPUT: size: true
68+
print("size: \(S.size == getSize())")
69+
70+
// CHECK-OUTPUT: alignment: true
71+
print("alignment: \(S.alignment == getAlignment())")
72+
73+
// CHECK-OUTPUT: stride: true
74+
print("stride: \(S.stride == getStride())")
75+
76+
// CHECK-OUTPUT: doubleSize: true
77+
print("doubleSize: \(S.doubleSize == getSize() * 2)")
78+
}
79+
80+
testit()
81+

0 commit comments

Comments
 (0)