Skip to content

SILOptimizer: add a pass to perform target specific constant folding. #59665

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 2 commits into from
Jun 24, 2022
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
12 changes: 8 additions & 4 deletions include/swift/AST/SILGenRequests.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class LangOptions;
class ModuleDecl;
class SILModule;
class SILOptions;
class IRGenOptions;

namespace Lowering {
class TypeConverter;
Expand All @@ -47,6 +48,7 @@ struct ASTLoweringDescriptor {
llvm::PointerUnion<FileUnit *, ModuleDecl *> context;
Lowering::TypeConverter &conv;
const SILOptions &opts;
const IRGenOptions *irgenOptions;

/// A specific set of SILDeclRefs to emit. If set, only these refs will be
/// emitted. Otherwise the entire \c context will be emitted.
Expand Down Expand Up @@ -74,15 +76,17 @@ struct ASTLoweringDescriptor {
public:
static ASTLoweringDescriptor
forFile(FileUnit &sf, Lowering::TypeConverter &conv, const SILOptions &opts,
Optional<SILRefsToEmit> refsToEmit = None) {
return ASTLoweringDescriptor{&sf, conv, opts, refsToEmit};
Optional<SILRefsToEmit> refsToEmit = None,
const IRGenOptions *irgenOptions = nullptr) {
return ASTLoweringDescriptor{&sf, conv, opts, irgenOptions, refsToEmit};
}

static ASTLoweringDescriptor
forWholeModule(ModuleDecl *mod, Lowering::TypeConverter &conv,
const SILOptions &opts,
Optional<SILRefsToEmit> refsToEmit = None) {
return ASTLoweringDescriptor{mod, conv, opts, refsToEmit};
Optional<SILRefsToEmit> refsToEmit = None,
const IRGenOptions *irgenOptions = nullptr) {
return ASTLoweringDescriptor{mod, conv, opts, irgenOptions, refsToEmit};
}

/// Retrieves the files to generate SIL for. If the descriptor is configured
Expand Down
14 changes: 12 additions & 2 deletions include/swift/SIL/SILModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ class AnyFunctionType;
class ASTContext;
class FileUnit;
class FuncDecl;
class IRGenOptions;
class KeyPathPattern;
class ModuleDecl;
class SILUndef;
Expand Down Expand Up @@ -353,6 +354,12 @@ class SILModule {
/// The options passed into this SILModule.
const SILOptions &Options;

/// IRGen options to be used by target specific SIL optimization passes.
///
/// Not null, if the module is created by the compiler itself (and not
/// e.g. by lldb).
const IRGenOptions *irgenOptions;

/// The number of functions created in this module, which will be the index of
/// the next function.
unsigned nextFunctionIndex = 0;
Expand Down Expand Up @@ -382,7 +389,8 @@ class SILModule {
#endif

SILModule(llvm::PointerUnion<FileUnit *, ModuleDecl *> context,
Lowering::TypeConverter &TC, const SILOptions &Options);
Lowering::TypeConverter &TC, const SILOptions &Options,
const IRGenOptions *irgenOptions = nullptr);

SILModule(const SILModule&) = delete;
void operator=(const SILModule&) = delete;
Expand Down Expand Up @@ -537,7 +545,8 @@ class SILModule {
/// single-file mode, and a ModuleDecl in whole-module mode.
static std::unique_ptr<SILModule>
createEmptyModule(llvm::PointerUnion<FileUnit *, ModuleDecl *> context,
Lowering::TypeConverter &TC, const SILOptions &Options);
Lowering::TypeConverter &TC, const SILOptions &Options,
const IRGenOptions *irgenOptions = nullptr);

/// Get the Swift module associated with this SIL module.
ModuleDecl *getSwiftModule() const { return TheSwiftModule; }
Expand Down Expand Up @@ -570,6 +579,7 @@ class SILModule {
bool isOptimizedOnoneSupportModule() const;

const SILOptions &getOptions() const { return Options; }
const IRGenOptions *getIRGenOptionsOrNull() const { return irgenOptions; }

using iterator = FunctionListType::iterator;
using const_iterator = FunctionListType::const_iterator;
Expand Down
2 changes: 2 additions & 0 deletions include/swift/SILOptimizer/PassManager/Passes.def
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,8 @@ PASS(ReleaseHoisting, "release-hoisting",
"SIL release Hoisting")
PASS(LateReleaseHoisting, "late-release-hoisting",
"Late SIL release Hoisting Preserving Epilogues")
PASS(TargetConstantFolding, "target-constant-folding",
"Target specific constant folding")
IRGEN_PASS(LoadableByAddress, "loadable-address",
"SIL Large Loadable type by-address lowering.")
PASS(MandatorySILLinker, "mandatory-linker",
Expand Down
6 changes: 4 additions & 2 deletions include/swift/Subsystems.h
Original file line number Diff line number Diff line change
Expand Up @@ -179,12 +179,14 @@ namespace swift {
/// SIL of all files in the module is present in the SILModule.
std::unique_ptr<SILModule>
performASTLowering(ModuleDecl *M, Lowering::TypeConverter &TC,
const SILOptions &options);
const SILOptions &options,
const IRGenOptions *irgenOptions = nullptr);

/// Turn a source file into SIL IR.
std::unique_ptr<SILModule>
performASTLowering(FileUnit &SF, Lowering::TypeConverter &TC,
const SILOptions &options);
const SILOptions &options,
const IRGenOptions *irgenOptions = nullptr);

using ModuleOrSourceFile = PointerUnion<ModuleDecl *, SourceFile *>;

Expand Down
7 changes: 5 additions & 2 deletions lib/FrontendTool/FrontendTool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -766,7 +766,9 @@ bool swift::performCompileStepsPostSema(CompilerInstance &Instance,
const PrimarySpecificPaths PSPs =
Instance.getPrimarySpecificPathsForWholeModuleOptimizationMode();
SILOptions SILOpts = getSILOptions(PSPs);
auto SM = performASTLowering(mod, Instance.getSILTypes(), SILOpts);
IRGenOptions irgenOpts = Invocation.getIRGenOptions();
auto SM = performASTLowering(mod, Instance.getSILTypes(), SILOpts,
&irgenOpts);
return performCompileStepsPostSILGen(Instance, std::move(SM), mod, PSPs,
ReturnValue, observer);
}
Expand All @@ -779,8 +781,9 @@ bool swift::performCompileStepsPostSema(CompilerInstance &Instance,
const PrimarySpecificPaths PSPs =
Instance.getPrimarySpecificPathsForSourceFile(*PrimaryFile);
SILOptions SILOpts = getSILOptions(PSPs);
IRGenOptions irgenOpts = Invocation.getIRGenOptions();
auto SM = performASTLowering(*PrimaryFile, Instance.getSILTypes(),
SILOpts);
SILOpts, &irgenOpts);
result |= performCompileStepsPostSILGen(Instance, std::move(SM),
PrimaryFile, PSPs, ReturnValue,
observer);
Expand Down
1 change: 1 addition & 0 deletions lib/IRGen/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ add_swift_host_library(swiftIRGen STATIC
Outlining.cpp
StructLayout.cpp
SwiftTargetInfo.cpp
TargetConstantFolding.cpp
TypeLayout.cpp
TypeLayoutDumper.cpp
TypeLayoutVerifier.cpp
Expand Down
2 changes: 1 addition & 1 deletion lib/IRGen/IRGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1059,7 +1059,7 @@ GeneratedModule IRGenRequest::evaluate(Evaluator &evaluator,
auto SILMod = std::unique_ptr<SILModule>(desc.SILMod);
if (!SILMod) {
auto loweringDesc = ASTLoweringDescriptor{
desc.Ctx, desc.Conv, desc.SILOpts,
desc.Ctx, desc.Conv, desc.SILOpts, nullptr,
symsToEmit.map([](const auto &x) { return x.silRefsToEmit; })};
SILMod = llvm::cantFail(Ctx.evaluator(LoweredSILRequest{loweringDesc}));

Expand Down
142 changes: 142 additions & 0 deletions lib/IRGen/TargetConstantFolding.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
//===--- TargetConstantFolding.cpp ----------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
// This pass lowers loadable SILTypes. On completion, the SILType of every
// function argument is an address instead of the type itself.
// This reduces the code size.
// Consequently, this pass is required for IRGen.
// It is a mandatory IRGen preparation pass (not a diagnostic pass).
//
//===----------------------------------------------------------------------===//

#define DEBUG_TYPE "target-constant-folding"
#include "IRGenModule.h"
#include "swift/SIL/SILBuilder.h"
#include "swift/SILOptimizer/PassManager/Transforms.h"
#include "swift/SILOptimizer/Utils/InstructionDeleter.h"
#include "llvm/Support/Debug.h"

using namespace swift;
using namespace swift::irgen;

namespace {

/// Performs constant folding for target-specific values.
///
/// Specifically, this optimization constant folds
/// ```
/// 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).
class TargetConstantFolding : public SILModuleTransform {
private:
/// The entry point to the transformation.
void run() override {
auto *irgenOpts = getModule()->getIRGenOptionsOrNull();
if (!irgenOpts)
return;

// We need an IRGenModule to get the actual sizes from type lowering.
// Creating an IRGenModule involves some effort. Therefore this is a
// module pass rather than a function pass so that this one-time setup
// only needs to be done once and not for all functions in a module.
IRGenerator irgen(*irgenOpts, *getModule());
auto targetMachine = irgen.createTargetMachine();
if (!targetMachine)
return;
IRGenModule IGM(irgen, std::move(targetMachine));

// Scan all instructions in the module for constant foldable instructions.
for (SILFunction &function : *getModule()) {

if (!function.shouldOptimize())
continue;

bool changed = false;
for (SILBasicBlock &block : function) {
InstructionDeleter deleter;

for (SILInstruction *inst : deleter.updatingRange(&block)) {
if (constFold(inst, IGM)) {
deleter.forceDelete(inst);
changed = true;
}
}
deleter.cleanupDeadInstructions();
}
if (changed) {
invalidateAnalysis(&function, SILAnalysis::InvalidationKind::Instructions);
}
}
}

/// Constant fold a single instruction.
///
/// Returns true if `inst` was replaced and can be deleted.
bool constFold(SILInstruction *inst, IRGenModule &IGM) {
auto *bi = dyn_cast<BuiltinInst>(inst);
if (!bi)
return false;

llvm::Constant *c = nullptr;
uint64_t offset = 0;

switch (bi->getBuiltinInfo().ID) {
case BuiltinValueKind::Sizeof:
c = getTypeInfoOfBuiltin(bi, IGM).getStaticSize(IGM);
break;
case BuiltinValueKind::Alignof:
c = getTypeInfoOfBuiltin(bi, IGM).getStaticAlignmentMask(IGM);
// The constant is the alignment _mask_. We get the actual alignment by
// adding 1.
offset = 1;
break;
case BuiltinValueKind::Strideof:
c = getTypeInfoOfBuiltin(bi, IGM).getStaticStride(IGM);
break;
default:
return false;
}
auto *intConst = dyn_cast_or_null<llvm::ConstantInt>(c);
if (!intConst)
return false;

APInt value = intConst->getValue();
value += APInt(value.getBitWidth(), offset);
auto intTy = bi->getType().getAs<BuiltinIntegerType>();
if (!intTy)
return false;

value = value.sextOrTrunc(intTy->getGreatestWidth());

// Replace the builtin by an integer literal.
SILBuilderWithScope builder(bi);
auto *intLit = builder.createIntegerLiteral(bi->getLoc(), bi->getType(),
value);
bi->replaceAllUsesWith(intLit);
return true;
}

const TypeInfo &getTypeInfoOfBuiltin(BuiltinInst *bi, IRGenModule &IGM) {
SubstitutionMap subs = bi->getSubstitutions();
SILType lowered = IGM.getLoweredType(subs.getReplacementTypes()[0]);
return IGM.getTypeInfo(lowered);
}
};

} // end anonymous namespace

SILTransform *swift::createTargetConstantFolding() {
return new TargetConstantFolding();
}
12 changes: 8 additions & 4 deletions lib/SIL/IR/SILModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,11 @@ class SILModule::SerializationCallback final
};

SILModule::SILModule(llvm::PointerUnion<FileUnit *, ModuleDecl *> context,
Lowering::TypeConverter &TC, const SILOptions &Options)
Lowering::TypeConverter &TC, const SILOptions &Options,
const IRGenOptions *irgenOptions)
: Stage(SILStage::Raw), loweredAddresses(!Options.EnableSILOpaqueValues),
indexTrieRoot(new IndexTrieNode()), Options(Options), serialized(false),
indexTrieRoot(new IndexTrieNode()), Options(Options),
irgenOptions(irgenOptions), serialized(false),
regDeserializationNotificationHandlerForNonTransparentFuncOME(false),
regDeserializationNotificationHandlerForAllFuncOME(false),
prespecializedFunctionDeclsImported(false), SerializeSILAction(),
Expand Down Expand Up @@ -203,8 +205,10 @@ void SILModule::checkForLeaksAfterDestruction() {

std::unique_ptr<SILModule> SILModule::createEmptyModule(
llvm::PointerUnion<FileUnit *, ModuleDecl *> context,
Lowering::TypeConverter &TC, const SILOptions &Options) {
return std::unique_ptr<SILModule>(new SILModule(context, TC, Options));
Lowering::TypeConverter &TC, const SILOptions &Options,
const IRGenOptions *irgenOptions) {
return std::unique_ptr<SILModule>(new SILModule(context, TC, Options,
irgenOptions));
}

ASTContext &SILModule::getASTContext() const {
Expand Down
13 changes: 8 additions & 5 deletions lib/SILGen/SILGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2243,7 +2243,7 @@ ASTLoweringRequest::evaluate(Evaluator &evaluator,
SILInstruction::resetInstructionCounts();

auto silMod = SILModule::createEmptyModule(desc.context, desc.conv,
desc.opts);
desc.opts, desc.irgenOptions);

// If all function bodies are being skipped there's no reason to do any
// SIL generation.
Expand Down Expand Up @@ -2285,15 +2285,18 @@ ASTLoweringRequest::evaluate(Evaluator &evaluator,

std::unique_ptr<SILModule>
swift::performASTLowering(ModuleDecl *mod, Lowering::TypeConverter &tc,
const SILOptions &options) {
auto desc = ASTLoweringDescriptor::forWholeModule(mod, tc, options);
const SILOptions &options,
const IRGenOptions *irgenOptions) {
auto desc = ASTLoweringDescriptor::forWholeModule(mod, tc, options,
None, irgenOptions);
return llvm::cantFail(
mod->getASTContext().evaluator(ASTLoweringRequest{desc}));
}

std::unique_ptr<SILModule>
swift::performASTLowering(FileUnit &sf, Lowering::TypeConverter &tc,
const SILOptions &options) {
auto desc = ASTLoweringDescriptor::forFile(sf, tc, options);
const SILOptions &options,
const IRGenOptions *irgenOptions) {
auto desc = ASTLoweringDescriptor::forFile(sf, tc, options, None, irgenOptions);
return llvm::cantFail(sf.getASTContext().evaluator(ASTLoweringRequest{desc}));
}
1 change: 1 addition & 0 deletions lib/SILOptimizer/PassManager/PassPipeline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -666,6 +666,7 @@ static void addMidLevelFunctionPipeline(SILPassPipelinePlan &P) {
static void addClosureSpecializePassPipeline(SILPassPipelinePlan &P) {
P.startPipeline("ClosureSpecialize");
P.addDeadFunctionAndGlobalElimination();
P.addTargetConstantFolding();
P.addDeadStoreElimination();
P.addDeadObjectElimination();

Expand Down
5 changes: 2 additions & 3 deletions test/SILOptimizer/devirt_protocol_method_invocations.swift
Original file line number Diff line number Diff line change
Expand Up @@ -138,9 +138,8 @@ public func test_devirt_protocol_extension_method_invocation_with_self_return_ty
// CHECK: return [[T1]]

// CHECK: sil @$s34devirt_protocol_method_invocations14testExMetatypeSiyF
// CHECK: [[T0:%.*]] = builtin "sizeof"<Int>
// CHECK: [[T1:%.*]] = builtin {{.*}}([[T0]]
// CHECK: [[T2:%.*]] = struct $Int ([[T1]] : {{.*}})
// CHECK: [[T0:%.*]] = integer_literal
// CHECK: [[T2:%.*]] = struct $Int ([[T0]] : {{.*}})
// CHECK: return [[T2]] : $Int

@inline(never)
Expand Down
Loading