Skip to content

Commit

Permalink
Merge pull request swiftlang#59665 from eeckstein/target-constprop
Browse files Browse the repository at this point in the history
SILOptimizer: add a pass to perform target specific constant folding.
  • Loading branch information
eeckstein authored Jun 24, 2022
2 parents 672c106 + f420468 commit ba42be5
Show file tree
Hide file tree
Showing 13 changed files with 275 additions and 23 deletions.
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

0 comments on commit ba42be5

Please sign in to comment.