[sil-code-motion] Teach SIL code motion how to forward a load of an aggregate type + struct_extracts for a load of a gep + load of field of aggregate type.

Swift SVN r13651
This commit is contained in:
Michael Gottesman
2014-02-07 21:38:43 +00:00
parent fb512b48f4
commit 1905d85aab
2 changed files with 142 additions and 12 deletions

View File

@@ -16,6 +16,7 @@
#include "swift/SIL/SILModule.h"
#include "swift/SIL/SILType.h"
#include "swift/SIL/SILValue.h"
#include "swift/SIL/SILBuilder.h"
#include "swift/SIL/SILVisitor.h"
#include "swift/SILPasses/Utils/Local.h"
#include "swift/SILPasses/Transforms.h"
@@ -47,6 +48,107 @@ static bool isWriteMemBehavior(SILInstruction::MemoryBehavior B) {
}
}
namespace {
/// An abstract representation of a SIL Projection that allows one to refer to
/// either nominal fields or tuple indices.
class Projection {
SILType Type;
VarDecl *Decl;
unsigned Index;
public:
Projection(SILType T, VarDecl *D) : Type(T), Decl(D), Index(-1) { }
Projection(SILType T, unsigned I) : Type(T), Decl(nullptr), Index(I) { }
SILType getType() const { return Type; }
VarDecl *getDecl() const { return Decl; }
unsigned getIndex() const { return Index; }
bool operator==(Projection &Other) const {
if (Decl)
return Decl == Other.getDecl();
else
return !Other.getDecl() && Index == Other.getIndex();
}
bool operator<(Projection Other) const {
// If Proj1 is a decl...
if (Decl) {
// It should be sorted before Proj2 is Proj2 is not a decl. Otherwise
// compare the pointers.
if (auto OtherDecl = Other.getDecl())
return uintptr_t(Decl) < uintptr_t(OtherDecl);
return true;
}
// If Proj1 is not a decl, then if Proj2 is a decl, Proj1 is not before
// Proj2. If Proj2 is not a decl, compare the indices.
return !Other.getDecl() && (Index < Other.Index);
}
};
} // end anonymous namespace.
static bool isAddressProjection(SILValue V) {
switch (V->getKind()) {
case ValueKind::StructElementAddrInst:
case ValueKind::TupleElementAddrInst:
return true;
default:
return false;
}
}
// Given an already emitted load PrevLd, see if we can
static SILValue findExtractPathBetweenValues(LoadInst *PrevLI, LoadInst *LI) {
SILValue PrevLIOp = PrevLI->getOperand();
SILValue LIOp = LI->getOperand();
// If they are equal, just return PrevLI.
if (PrevLIOp == LIOp)
return PrevLI;
// Otherwise see if LI can be projection extracted from PrevLI. First see if
// LI is a projection at all.
llvm::SmallVector<Projection, 4> Projections;
auto Iter = LIOp;
while (isAddressProjection(Iter) && PrevLIOp != Iter) {
if (auto *SEA = dyn_cast<StructElementAddrInst>(Iter.getDef()))
Projections.push_back(Projection(Iter.getType(), SEA->getField()));
else
Projections.push_back(
Projection(Iter.getType(),
cast<TupleElementAddrInst>(*Iter).getFieldNo()));
Iter = cast<SILInstruction>(*Iter).getOperand(0);
}
// We could not find an extract path in between the two values.
if (Projections.empty() || PrevLIOp != Iter)
return SILValue();
// Use the projection list we created to create the relevant extracts
SILValue LastExtract = PrevLI;
SILBuilder Builder(LI);
while (!Projections.empty()) {
auto P = Projections.pop_back_val();
if (auto *D = P.getDecl()) {
LastExtract = Builder.createStructExtract(LI->getLoc(), LastExtract,
D,
P.getType().getObjectType());
cast<StructExtractInst>(*LastExtract).getStructDecl();
} else {
LastExtract = Builder.createTupleExtract(LI->getLoc(), LastExtract,
P.getIndex(),
P.getType().getObjectType());
cast<TupleExtractInst>(*LastExtract).getTupleType();
}
}
// Return the last extract we created.
return LastExtract;
}
/// \brief Promote stored values to loads, remove dead stores and merge
/// duplicated loads.
bool promoteMemoryOperationsInBlock(SILBasicBlock *BB, AliasAnalysis *AA) {
@@ -101,18 +203,21 @@ bool promoteMemoryOperationsInBlock(SILBasicBlock *BB, AliasAnalysis *AA) {
// Search the previous loads and replace the current load with one of the
// previous loads.
for (auto PrevLd : Loads) {
if (PrevLd->getOperand() == LI->getOperand()) {
for (auto PrevLI : Loads) {
SILValue ForwardingExtract =
findExtractPathBetweenValues(PrevLI, LI);
if (!ForwardingExtract)
continue;
DEBUG(llvm::dbgs() << " Replacing with previous load: "
<< *PrevLd);
SILValue(LI, 0).replaceAllUsesWith(PrevLd);
<< *ForwardingExtract);
SILValue(LI, 0).replaceAllUsesWith(ForwardingExtract);
recursivelyDeleteTriviallyDeadInstructions(LI, true);
Changed = true;
LI = 0;
NumDupLoads++;
break;
}
}
if (LI)
Loads.insert(LI);
@@ -316,6 +421,7 @@ class SILCodeMotion : public SILFunctionTransform {
/// The entry point to the transformation.
void run() {
SILFunction &F = *getFunction();
DEBUG(llvm::dbgs() << "***** CodeMotion on function: " << F.getName() <<
" *****\n");

View File

@@ -215,3 +215,27 @@ bb0(%0 : $Builtin.Int64):
return %4 : $Builtin.Int64
}
struct Agg2 {
var t : (Builtin.Int64, Builtin.Int32)
}
struct Agg1 {
var a : Agg2
}
// CHECK-LABEL: sil @load_store_forwarding_from_aggregate_to_field
// CHECK: bb0([[INPUT_PTR:%[0-9]+]]
// CHECK-NEXT: load [[INPUT_PTR]]
// CHECK-NEXT: struct_extract
// CHECK-NEXT: struct_extract
// CHECK-NEXT: tuple_extract
// CHECK-NEXT: return
sil @load_store_forwarding_from_aggregate_to_field : $@thin (@inout Agg1) -> (Builtin.Int32) {
bb0(%0 : $*Agg1):
%1 = load %0 : $*Agg1
%2 = struct_element_addr %0 : $*Agg1, #a
%3 = struct_element_addr %2 : $*Agg2, #t
%4 = tuple_element_addr %3 : $*(Builtin.Int64, Builtin.Int32), 1
%5 = load %4 : $*Builtin.Int32
return %5 : $Builtin.Int32
}