blob: 177d531a9998066f93045cdd56e51bb0316dc4c6 [file] [log] [blame]
//===--- LoadStoreOptUtils.h ------------------------------------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 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 file defines LSBase, a class containing a SILValue base
/// and a ProjectionPath. It is used as the base class for LSLocation and
/// LSValue.
///
/// In the case of LSLocation, the base represents the base of the allocated
/// objects and the ProjectionPath tells which field in the object the
/// LSLocation represents.
///
/// In the case of LSValue, the base represents the root of loaded or stored
/// value it represents. And the ProjectionPath represents the field in the
/// loaded/store value the LSValue represents.
///
//===----------------------------------------------------------------------===//
#ifndef SWIFT_SIL_LSBASE_H
#define SWIFT_SIL_LSBASE_H
#include "swift/SIL/InstructionUtils.h"
#include "swift/SIL/Projection.h"
#include "swift/SILOptimizer/Analysis/AliasAnalysis.h"
#include "swift/SILOptimizer/Analysis/EscapeAnalysis.h"
#include "swift/SILOptimizer/Analysis/TypeExpansionAnalysis.h"
#include "swift/SILOptimizer/Analysis/ValueTracking.h"
#include "swift/SILOptimizer/Utils/Local.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Debug.h"
#include <utility>
namespace swift {
class LSBase;
class LSLocation;
class LSValue;
//===----------------------------------------------------------------------===//
// Load Store Base
//===----------------------------------------------------------------------===//
class LSBase {
public:
enum KeyKind : uint8_t { Empty = 0, Tombstone, Normal };
protected:
/// The base of the object.
SILValue Base;
/// Empty key, tombstone key or normal key.
KeyKind Kind;
/// The path to reach the accessed field of the object.
Optional<ProjectionPath> Path;
public:
/// Constructors.
LSBase() : Base(), Kind(Normal) {}
LSBase(KeyKind Kind) : Base(), Kind(Kind) {}
LSBase(SILValue B) : Base(B), Kind(Normal) {}
LSBase(SILValue B, const Optional<ProjectionPath> &P, KeyKind Kind = Normal)
: Base(B), Kind(Kind), Path(P) {}
/// Virtual destructor.
virtual ~LSBase() {}
/// Copy constructor.
LSBase(const LSBase &RHS) {
Base = RHS.Base;
Kind = RHS.Kind;
Path = RHS.Path;
}
/// Assignment operator.
LSBase &operator=(const LSBase &RHS) {
Base = RHS.Base;
Kind = RHS.Kind;
Path = RHS.Path;
return *this;
}
/// Getters for LSBase.
KeyKind getKind() const { return Kind; }
SILValue getBase() const { return Base; }
const Optional<ProjectionPath> &getPath() const { return Path; }
/// Reset the LSBase, i.e. clear base and path.
void reset() {
Base = SILValue();
Kind = Normal;
Path.reset();
}
/// Returns whether the LSBase has been initialized properly.
virtual bool isValid() const { return Base && Path.hasValue(); }
/// Returns true if the LSBase has a non-empty projection path.
bool hasEmptyProjectionPath() const { return !Path.getValue().size(); }
/// return true if that the two objects have the same base but access different
/// fields of the base object.
bool hasNonEmptySymmetricPathDifference(const LSBase &RHS) const {
const ProjectionPath &P = RHS.Path.getValue();
return Path.getValue().hasNonEmptySymmetricDifference(P);
}
/// Subtract the given path from the ProjectionPath.
void removePathPrefix(Optional<ProjectionPath> &P) {
if (!P.hasValue())
return;
// Remove prefix does not modify the Path in-place.
Path = ProjectionPath::removePrefix(Path.getValue(), P.getValue());
}
/// Return true if the RHS have identical projection paths.
///
/// If both LSBase have empty paths, they are treated as having
/// identical projection path.
bool hasIdenticalProjectionPath(const LSBase &RHS) const {
// If both Paths have no value, then the 2 LSBases are
// different.
if (!Path.hasValue() && !RHS.Path.hasValue())
return false;
// If 1 Path has value while the other does not, then the 2
// LSBases are different.
if (Path.hasValue() != RHS.Path.hasValue())
return false;
// If both Paths are empty, then the 2 LSBases are the same.
if (Path.getValue().empty() && RHS.Path.getValue().empty())
return true;
// If both Paths have different values, then the 2 LSBases are
// different.
if (Path.getValue() != RHS.Path.getValue())
return false;
// Otherwise, the 2 LSBases are the same.
return true;
}
/// Comparisons.
bool operator!=(const LSBase &RHS) const {
return !(*this == RHS);
}
bool operator==(const LSBase &RHS) const {
// If type is not the same, then LSBases different.
if (Kind != RHS.Kind)
return false;
// Return true if this is a Tombstone or Empty.
if (Kind == Empty || Kind == Tombstone)
return true;
// If Base is different, then LSBases different.
if (Base != RHS.Base)
return false;
// If the projection paths are different, then LSBases are
// different.
if (!hasIdenticalProjectionPath(RHS))
return false;
// These LSBases represent the same memory location.
return true;
}
/// Print the LSBase.
virtual void print(llvm::raw_ostream &os, SILModule *Mod) {
os << Base;
Path.getValue().print(os, *Mod);
}
virtual void dump(SILModule *Mod) { print(llvm::dbgs(), Mod); }
};
static inline llvm::hash_code hash_value(const LSBase &S) {
const SILValue Base = S.getBase();
const Optional<ProjectionPath> &Path = S.getPath();
llvm::hash_code HC = llvm::hash_combine(Base.getOpaqueValue());
if (!Path.hasValue())
return HC;
return llvm::hash_combine(HC, hash_value(Path.getValue()));
}
//===----------------------------------------------------------------------===//
// Load Store Value
//===----------------------------------------------------------------------===//
using LSLocationValueMap = llvm::DenseMap<LSLocation, LSValue>;
using LSValueList = llvm::SmallVector<LSValue, 8>;
using LSValueIndexMap = llvm::SmallDenseMap<LSValue, unsigned, 32>;
using ValueTableMap = llvm::SmallMapVector<unsigned, unsigned, 8>;
/// A LSValue is an abstraction of an object field value in program. It
/// consists of a base that is the tracked SILValue, and a projection path to
/// the represented field.
class LSValue : public LSBase {
/// If this is a covering value, we need to go to each predecessor to
/// materialize the value.
bool CoveringValue;
public:
/// Constructors.
LSValue() : LSBase(), CoveringValue(false) {}
LSValue(KeyKind Kind) : LSBase(Kind), CoveringValue(false) {}
LSValue(bool CSVal) : LSBase(Normal), CoveringValue(CSVal) {}
LSValue(SILValue B, const ProjectionPath &P)
: LSBase(B, P), CoveringValue(false) {}
/// Copy constructor.
LSValue(const LSValue &RHS) : LSBase(RHS) {
CoveringValue = RHS.CoveringValue;
}
/// Assignment operator.
LSValue &operator=(const LSValue &RHS) {
LSBase::operator=(RHS);
CoveringValue = RHS.CoveringValue;
return *this;
}
/// Comparisons.
bool operator!=(const LSValue &RHS) const { return !(*this == RHS); }
bool operator==(const LSValue &RHS) const {
if (CoveringValue && RHS.isCoveringValue())
return true;
if (CoveringValue != RHS.isCoveringValue())
return false;
return LSBase::operator==(RHS);
}
/// Returns whether the LSValue has been initialized properly.
bool isValid() const {
if (CoveringValue)
return true;
return LSBase::isValid();
}
/// Take the last level projection off. Return the modified LSValue.
LSValue &stripLastLevelProjection() {
Path.getValue().pop_back();
return *this;
}
/// Returns true if this LSValue is a covering value.
bool isCoveringValue() const { return CoveringValue; }
/// Materialize the SILValue that this LSValue represents.
///
/// In the case where we have a single value this can be materialized by
/// applying Path to the Base.
SILValue materialize(SILInstruction *Inst) {
if (CoveringValue)
return SILValue();
return Path.getValue().createExtract(Base, Inst, true);
}
void print(llvm::raw_ostream &os, SILModule *Mod) {
if (CoveringValue) {
os << "Covering Value";
return;
}
LSBase::print(os, Mod);
}
/// Expand this SILValue to all individual fields it contains.
static void expand(SILValue Base, SILModule *Mod, LSValueList &Vals,
TypeExpansionAnalysis *TE);
/// Given a memory location and a map between the expansions of the location
/// and their corresponding values, try to come up with a single SILValue this
/// location holds. This may involve extracting and aggregating available
/// values.
static void reduceInner(LSLocation &B, SILModule *M, LSLocationValueMap &Vals,
SILInstruction *InsertPt);
static SILValue reduce(LSLocation &B, SILModule *M, LSLocationValueMap &Vals,
SILInstruction *InsertPt);
};
static inline llvm::hash_code hash_value(const LSValue &V) {
llvm::hash_code HC = llvm::hash_combine(V.isCoveringValue());
if (V.isCoveringValue())
return HC;
return llvm::hash_combine(HC, hash_value((LSBase)V));
}
//===----------------------------------------------------------------------===//
// Load Store Location
//===----------------------------------------------------------------------===//
using LSLocationSet = llvm::DenseSet<LSLocation>;
using LSLocationList = llvm::SmallVector<LSLocation, 8>;
using LSLocationIndexMap = llvm::SmallDenseMap<LSLocation, unsigned, 32>;
using LSLocationBaseMap = llvm::DenseMap<SILValue, LSLocation>;
/// This class represents a field in an allocated object. It consists of a
/// base that is the tracked SILValue, and a projection path to the
/// represented field.
class LSLocation : public LSBase {
public:
/// Constructors.
LSLocation() {}
LSLocation(SILValue B, const Optional<ProjectionPath> &P, KeyKind K = Normal)
: LSBase(B, P, K) {}
LSLocation(KeyKind Kind) : LSBase(Kind) {}
/// Use the concatenation of the 2 ProjectionPaths as the Path.
LSLocation(SILValue B, const ProjectionPath &BP, const ProjectionPath &AP)
: LSBase(B) {
ProjectionPath P((*Base).getType());
P.append(BP);
P.append(AP);
Path = P;
}
/// Initialize a location with a new set of base, projectionpath and kind.
void init(SILValue B, const Optional<ProjectionPath> &P, KeyKind K= Normal) {
Base = B;
Path = P;
Kind = K;
}
/// Copy constructor.
LSLocation(const LSLocation &RHS) : LSBase(RHS) {}
/// Assignment operator.
LSLocation &operator=(const LSLocation &RHS) {
LSBase::operator=(RHS);
return *this;
}
/// Returns the type of the object the LSLocation represents.
SILType getType(SILModule *M) {
return Path.getValue().getMostDerivedType(*M);
}
/// Get the first level locations based on this location's first level
/// projection.
void getNextLevelLSLocations(LSLocationList &Locs, SILModule *Mod);
/// Check whether the 2 LSLocations may alias each other or not.
bool isMayAliasLSLocation(const LSLocation &RHS, AliasAnalysis *AA);
/// Check whether the 2 LSLocations must alias each other or not.
bool isMustAliasLSLocation(const LSLocation &RHS, AliasAnalysis *AA);
/// Check whether the LSLocation can escape the current function.
bool isNonEscapingLocalLSLocation(SILFunction *Fn, EscapeAnalysis *EA);
/// Expand this location to all individual fields it contains.
///
/// In SIL, we can have a store to an aggregate and loads from its individual
/// fields. Therefore, we expand all the operations on aggregates onto
/// individual fields and process them separately.
static void expand(LSLocation Base, SILModule *Mod, LSLocationList &Locs,
TypeExpansionAnalysis *TE);
/// Given a set of locations derived from the same base, try to merge/reduce
/// them into smallest number of LSLocations possible.
static bool reduce(LSLocation Base, SILModule *Mod, LSLocationSet &Locs);
/// Enumerate the given Mem LSLocation.
static void enumerateLSLocation(SILModule *M, SILValue Mem,
std::vector<LSLocation> &LSLocationVault,
LSLocationIndexMap &LocToBit,
LSLocationBaseMap &BaseToLoc,
TypeExpansionAnalysis *TE);
/// Enumerate all the locations in the function.
static void enumerateLSLocations(SILFunction &F,
std::vector<LSLocation> &LSLocationVault,
LSLocationIndexMap &LocToBit,
LSLocationBaseMap &BaseToLoc,
TypeExpansionAnalysis *TE,
std::pair<int, int> &LSCount);
};
static inline llvm::hash_code hash_value(const LSLocation &L) {
return llvm::hash_combine(hash_value((LSBase)L));
}
} // end swift namespace
/// LSLocation and LSValue are used in DenseMap.
namespace llvm {
using swift::LSBase;
using swift::LSLocation;
using swift::LSValue;
template <> struct DenseMapInfo<LSValue> {
static inline LSValue getEmptyKey() {
return LSValue(LSBase::Empty);
}
static inline LSValue getTombstoneKey() {
return LSValue(LSBase::Tombstone);
}
static inline unsigned getHashValue(const LSValue &Val) {
return hash_value(Val);
}
static bool isEqual(const LSValue &LHS, const LSValue &RHS) {
return LHS == RHS;
}
};
template <> struct DenseMapInfo<LSLocation> {
static inline LSLocation getEmptyKey() {
return LSLocation(LSBase::Empty);
}
static inline LSLocation getTombstoneKey() {
return LSLocation(LSBase::Tombstone);
}
static inline unsigned getHashValue(const LSLocation &Loc) {
return hash_value(Loc);
}
static bool isEqual(const LSLocation &LHS, const LSLocation &RHS) {
return LHS == RHS;
}
};
} // namespace llvm
#endif // SWIFT_SIL_LSBASE_H