Skip to content

Commit

Permalink
Expand the amount of unchecked_addr_casts that we can handle.
Browse files Browse the repository at this point in the history
This is tested by an assertion in IRGen. After Beta3, this code is going
to go away and be replaced by just always promoting the cast. Then the
IRGen assertion will be replaced by propagating undef. The assertion in
the stdlib will still fire in that case since the assertion is based on
the tops not the given value implying that we will not lose any
correctness.

Swift SVN r19272
  • Loading branch information
gottesmm committed Jun 26, 2014
1 parent ab1e997 commit 256f1e5
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 68 deletions.
3 changes: 3 additions & 0 deletions include/swift/SIL/SILType.h
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,9 @@ class SILType {
/// return the null type.
SILType getOptionalObjectType(SILModule &SILMod) const;

/// Returns true if this is the AnyObject SILType;
bool isAnyObject() const { return getSwiftRValueType()->isAnyObject(); }

//
// Accessors for types used in SIL instructions:
//
Expand Down
87 changes: 49 additions & 38 deletions lib/SIL/SILType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -210,9 +210,10 @@ static bool isLayoutCompatibleWithRawPointer(SILType Ty, SILModule &Mod) {
return false;
}

/// Returns true if scalar type \p LHS is layout compatible with scalar \p RHS.
static bool isLayoutCompatibleAsNonAggregates(SILType LHS, SILType RHS,
SILModule &Mod) {
/// Returns true if scalar type \p LHS is layout compatible with scalar \p
/// RHS. These are all commutative queries.
static bool areLayoutCompatibleNonAggregates(SILType LHS, SILType RHS,
SILModule &Mod) {
// First check canonicalize Builtin.RawPointer on the LHS.
if (RHS.is<BuiltinRawPointerType>())
std::swap(LHS, RHS);
Expand All @@ -238,18 +239,26 @@ static bool isLayoutCompatibleAsNonAggregates(SILType LHS, SILType RHS,
if (RHS.is<BuiltinNativeObjectType>() || RHS.is<BuiltinUnknownObjectType>())
return true;

// As one last final check, if both are classes and have a derived class
// relationship, they are layout compatible.
// If both are classes, we treat them as being layout compatible.
if (LHS.getClassOrBoundGenericClass() && RHS.getClassOrBoundGenericClass())
return LHS.isSuperclassOf(RHS) || RHS.isSuperclassOf(LHS);
return true;

// Canonicalize AnyObject on the left side.
if (RHS.isAnyObject())
std::swap(LHS, RHS);

// If LHS is an AnyObject and RHS is a class, we are going to say that they
// are layout compatible.
if (LHS.isAnyObject() && RHS.getClassOrBoundGenericClass())
return true;

// Otherwise conservatively assume that the two types are not layout
// compatible.
return false;
}

static SILType getFirstPayloadOfEnum(EnumDecl *E, SILType TyIter,
SILModule &Mod) {
static SILType getFirstPayloadTypeOfEnum(EnumDecl *E, SILType TyIter,
SILModule &Mod) {
for (EnumElementDecl *Elt : E->getAllElements())
if (Elt->hasArgumentType())
return TyIter.getEnumElementType(Elt, Mod);
Expand Down Expand Up @@ -288,16 +297,17 @@ static bool isLayoutCompatibleWithAggregate(SILType LHS, SILType RHS,
SILType TyIter = RHS;

while (true) {
// If this type is the type we are searching for (Ty1), we have succeeded,
// return.
if (TyIter == LHS)
// If TyIter is equal to Ty1 or is layout compatible with Ty1 as a non
// aggregate, return true.
if (TyIter == LHS || areLayoutCompatibleNonAggregates(TyIter, LHS, Mod) ||
areLayoutCompatibleNonAggregates(LHS, TyIter, Mod))
return true;

// Then if we have an enum...
if (EnumDecl *E = TyIter.getEnumOrBoundGenericEnum()) {
// Attempt to compute the first payloaded case of the enum. If we find it,
// continue.
if ((TyIter = getFirstPayloadOfEnum(E, TyIter, Mod)))
if ((TyIter = getFirstPayloadTypeOfEnum(E, TyIter, Mod)))
continue;

// Otherwise this enum has no payloads implying that it can not be layout
Expand All @@ -318,8 +328,8 @@ static bool isLayoutCompatibleWithAggregate(SILType LHS, SILType RHS,

// If we reached this point, then this type has no subrecords we are
// interested in. Check if LHS/RHS are layout compatible as scalars.
return isLayoutCompatibleAsNonAggregates(TyIter, LHS, Mod) ||
isLayoutCompatibleAsNonAggregates(LHS, TyIter, Mod);
return areLayoutCompatibleNonAggregates(TyIter, LHS, Mod) ||
areLayoutCompatibleNonAggregates(LHS, TyIter, Mod);
}
}

Expand All @@ -331,34 +341,37 @@ bool SILType::isLayoutCompatibleWith(SILType RHS, SILModule &Mod) const {
// Create a reference to ourselves so we can canonicalize.
SILType LHS = *this;

// If we are equal to Ty, we must be layout compatible with it.
if (LHS == RHS)
return true;

// If either LHS or RHS has archetypes, bail since we don't know what the
// archetypes are. This could probably be improved.
if (LHS.hasArchetype() || RHS.hasArchetype())
return false;

// First check if RHS is an enum...
if (RHS.getEnumOrBoundGenericEnum()) {
// If LHS is an enum as well...
if (LHS.getEnumOrBoundGenericEnum()) {
// If both are optional types with layout compatible types, they are
// layout compatible.
SILType LHSOptType = LHS.getOptionalObjectType(Mod);
SILType RHSOptType = RHS.getOptionalObjectType(Mod);
if (LHSOptType && RHSOptType &&
LHSOptType.isLayoutCompatibleWith(RHSOptType, Mod))
return true;
}
// If LHS is equal to RHS, or they are layout compatible as non aggregates,
// return true.
if (LHS == RHS || areLayoutCompatibleNonAggregates(LHS, RHS, Mod) ||
areLayoutCompatibleNonAggregates(RHS, LHS, Mod))
return true;

// If LHS is an enum type...
if (EnumDecl *E = LHS.getEnumOrBoundGenericEnum()) {
// LHS can only be layout compatible with RHS if RHS is the layout
// compatible with the first payload type of LHS. Thus grab the first
// payload type from LHS and redo the query with it instead. We are trusting
// the stdlib not to attempt to perform this operation on invalid types.
LHS = getFirstPayloadTypeOfEnum(E, LHS, Mod);

if (LHS.isLayoutCompatibleWith(RHS, Mod))
return true;
}

// If RHS is an enum...
if (EnumDecl *E = RHS.getEnumOrBoundGenericEnum()) {
// Otherwise if we have a generic enum, try to prove that the LHS/RHS are
// layout compatible by using the fact the enum is an aggregate to see if
// LHS is the payload of RHS. We do not handle the case where LHS is an
// enum since enums are not commutatively layout compatible with their first
// payload.
if (isLayoutCompatibleWithAggregate(LHS, RHS, Mod))
// LHS is the payload of RHS.
RHS = getFirstPayloadTypeOfEnum(E, RHS, Mod);

if (RHS.isLayoutCompatibleWith(LHS, Mod))
return true;
}

Expand All @@ -373,10 +386,8 @@ bool SILType::isLayoutCompatibleWith(SILType RHS, SILModule &Mod) const {
if (isLayoutCompatibleWithAggregate(RHS, LHS, Mod))
return true;

// Otherwise attempt to prove that the LHS, RHS are layout compatible as
// non-aggregate types.
return isLayoutCompatibleAsNonAggregates(LHS, RHS, Mod) ||
isLayoutCompatibleAsNonAggregates(RHS, LHS, Mod);
// Otherwise be conservative and assume that they are not layout compatible.
return false;
}

SILType SILType::getOptionalObjectType(SILModule &M) const {
Expand Down
72 changes: 47 additions & 25 deletions lib/SILPasses/LoadStoreOpts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,12 @@ STATISTIC(NumForwardedLoads, "Number of loads forwarded");
/// insts and return the last such inst.
static SILValue
findExtractPathFromAddressValueToLoad(SILValue Address, SILValue StoredValue,
LoadInst *Load) {
SILInstruction *Inst, SILValue InstOp) {
// Attempt to find the projection path from Address -> Load->getOperand().
SILValue LIOp = Load->getOperand();
llvm::SmallVector<Projection, 4> ProjectionPath;

// If we failed to find the path, return an empty value early.
if (!findAddressProjectionPathBetweenValues(Address, LIOp, ProjectionPath))
if (!findAddressProjectionPathBetweenValues(Address, InstOp, ProjectionPath))
return SILValue();

// If we found a projection path, but there are no projections, then the two
Expand All @@ -57,12 +56,12 @@ findExtractPathFromAddressValueToLoad(SILValue Address, SILValue StoredValue,
// Ok, at this point we know that we can construct our aggregate projections
// from our list of address projections.
SILValue LastExtract = StoredValue;
SILBuilder Builder(Load);
SILBuilder Builder(Inst);
while (!ProjectionPath.empty()) {
auto P = ProjectionPath.pop_back_val();
if (ValueDecl *D = P.getDecl()) {
if (P.getNominalType() == Projection::NominalType::Struct) {
LastExtract = Builder.createStructExtract(Load->getLoc(), LastExtract,
LastExtract = Builder.createStructExtract(Inst->getLoc(), LastExtract,
cast<VarDecl>(D),
P.getType().getObjectType());
assert(cast<StructExtractInst>(*LastExtract).getStructDecl() &&
Expand All @@ -72,13 +71,13 @@ findExtractPathFromAddressValueToLoad(SILValue Address, SILValue StoredValue,
"Expected an enum decl here. Only other possibility is a "
"class which we do not support");
LastExtract = Builder.createUncheckedEnumData(
Load->getLoc(), LastExtract, cast<EnumElementDecl>(D),
Inst->getLoc(), LastExtract, cast<EnumElementDecl>(D),
P.getType().getObjectType());
assert(cast<UncheckedEnumDataInst>(*LastExtract).getElement() &&
"Instruction must have an enum element decl!");
}
} else {
LastExtract = Builder.createTupleExtract(Load->getLoc(), LastExtract,
LastExtract = Builder.createTupleExtract(Inst->getLoc(), LastExtract,
P.getIndex(),
P.getType().getObjectType());
assert(cast<TupleExtractInst>(*LastExtract).getTupleType() &&
Expand Down Expand Up @@ -239,17 +238,28 @@ void LSBBForwarder::tryToEliminateDeadStores(StoreInst *SI) {
Stores.insert(SI);
}

/// Given an unchecked_addr_cast with various address projections using it,
/// rewrite the forwarding stored value to a bitcast + the relevant extract
/// operations.
static SILValue
tryToForwardAddressValueToUncheckedAddrToLoad(SILValue Address,
SILValue StoredValue,
LoadInst *LI,
UncheckedAddrCastInst *UADCI) {
assert(LI->getOperand().stripAddressProjections() == UADCI &&
"We assume that the UADCI is the load's address stripped of "
"address projections.");

// First grab the address operand of our UADCI.
SILValue UADCIOp = UADCI->getOperand();
if (Address != UADCIOp)

// Make sure that this is equal to our address. If not, bail.
if (UADCIOp != Address)
return SILValue();

// Construct the relevant bitcast.
SILModule &Mod = UADCI->getModule();
SILType InputTy = UADCIOp.getType();
SILType InputTy = UADCI->getOperand().getType();
SILType OutputTy = UADCI->getType();

// If OutputTy is not layout compatible with InputTy, there is nothing we can
Expand All @@ -260,27 +270,37 @@ tryToForwardAddressValueToUncheckedAddrToLoad(SILValue Address,
bool InputIsTrivial = InputTy.isTrivial(Mod);
bool OutputIsTrivial = OutputTy.isTrivial(Mod);

// If one is trivial and the other is not, bail.
if (InputIsTrivial != OutputIsTrivial)
return SILValue();

// If either are generic, bail.
if (InputTy.hasArchetype() || OutputTy.hasArchetype())
return SILValue();

SILBuilder B(LI);

// If both input and output are trivial types, return an
// unchecked_trivial_bit_cast.
if (InputIsTrivial && OutputIsTrivial) {
return B.createUncheckedTrivialBitCast(UADCI->getLoc(), StoredValue,
OutputTy.getObjectType());
SILValue CastValue;

// If the output is trivial, we have a trivial bit cast.
if (InputIsTrivial || OutputIsTrivial) {
CastValue = B.createUncheckedTrivialBitCast(UADCI->getLoc(), StoredValue,
OutputTy.getObjectType());
} else {
// Otherwise, both must have some sort of reference counts on them. Insert
// the ref count cast.
CastValue = B.createUncheckedRefBitCast(UADCI->getLoc(), StoredValue,
OutputTy.getObjectType());
}

// Otherwise, both must have some sort of reference counts on them. Insert the
// ref count cast.
return B.createUncheckedRefBitCast(UADCI->getLoc(), StoredValue,
OutputTy.getObjectType());
// Then try to construct an extract path from the UADCI to the Address.
SILValue ExtractPath =
findExtractPathFromAddressValueToLoad(UADCI, CastValue,
LI, LI->getOperand());

// If we can not construct the extract path, bail.
if (!ExtractPath)
return SILValue();

assert(ExtractPath.getType() == UADCIOp.getType().getObjectType() &&
"Must have same types here.");

return ExtractPath;
}

/// Given an address \p Address and a value \p Value stored there that is then
Expand All @@ -291,13 +311,15 @@ static SILValue tryToForwardAddressValueToLoad(SILValue Address,
LoadInst *LI) {
// First if we have a store + unchecked_addr_cast + load, try to forward the
// value the store using a bitcast.
if (auto *UADCI = dyn_cast<UncheckedAddrCastInst>(LI->getOperand()))
SILValue LIOpWithoutProjs = LI->getOperand().stripAddressProjections();
if (auto *UADCI = dyn_cast<UncheckedAddrCastInst>(LIOpWithoutProjs))
return tryToForwardAddressValueToUncheckedAddrToLoad(Address, StoredValue,
LI, UADCI);

// Next, try to promote partial loads from stores. If this fails, it will
// return SILValue(), which is also our failure condition.
return findExtractPathFromAddressValueToLoad(Address, StoredValue, LI);
return findExtractPathFromAddressValueToLoad(Address, StoredValue, LI,
LI->getOperand());
}

void LSBBForwarder::tryToForwardLoad(LoadInst *LI) {
Expand Down
7 changes: 2 additions & 5 deletions lib/SILPasses/SILCombine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1338,13 +1338,10 @@ SILCombiner::visitUncheckedAddrCastInst(UncheckedAddrCastInst *UADCI) {
if (!OutputTy.isLayoutCompatibleWith(InputTy, Mod))
return nullptr;

// If our types have mismatching trivial properties, bail.
// TODO: Can we handle this case?
// If our input is trivial and our output type is not, do not do anything.
bool InputIsTrivial = InputTy.isTrivial(Mod);
bool OutputIsTrivial = OutputTy.isTrivial(Mod);
if (InputIsTrivial != OutputIsTrivial)
return nullptr;
bool IsTrivialCast = InputIsTrivial;
bool IsTrivialCast = InputIsTrivial || OutputIsTrivial;

// For each user U of the unchecked_addr_cast...
for (auto U : UADCI->getUses())
Expand Down

0 comments on commit 256f1e5

Please sign in to comment.