Refactor cast representation in AST and SIL, and implement 'is'.

Improve our representations of casts in the AST and SIL so that 'as!' and 'is' (and eventually 'as?') can share almost all of the same type-checking, SILGen, and IRGen code.

In the AST, we now represent 'as!' and 'is' as UnconditionalCheckedCastExpr and IsaExpr, respectively, with the semantic variations of cast (downcast, super-to-archetype, archetype-to-concrete, etc.) discriminated by an enum field. This keeps the user-visible syntactic and type behavior differences of the two forms cleanly separated for AST consumers.

At the SIL level, we transpose the representation so that the different cast semantics get their own instructions and the conditional/unconditional cast behavior is indicated by an enum, making it easy for IRGen to discriminate the different code paths for the different semantics. We also add an 'IsNonnull' instruction to cover the conditional-cast-result-to-boolean conversion common to all the forms of 'is'.

The upshot of all this is that 'x is T' now works for all the new archetype and existential cast forms supported by 'as!'.

Swift SVN r5737
This commit is contained in:
Joe Groff
2013-06-21 05:54:03 +00:00
parent be0f7b4c48
commit f072c48e45
28 changed files with 587 additions and 636 deletions

View File

@@ -20,6 +20,7 @@
#include "swift/AST/Pattern.h"
#include "swift/AST/Types.h"
#include "swift/SIL/SILType.h"
#include "swift/SIL/SILInstruction.h"
#include "swift/Basic/Optional.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/Function.h"
@@ -39,8 +40,8 @@ using namespace swift;
using namespace irgen;
/// Emit a checked unconditional downcast.
llvm::Value *IRGenFunction::emitUnconditionalDowncast(llvm::Value *from,
SILType toType) {
llvm::Value *IRGenFunction::emitDowncast(llvm::Value *from, SILType toType,
CheckedCastMode mode) {
// Emit the value we're casting from.
if (from->getType() != IGM.Int8PtrTy)
from = Builder.CreateBitCast(from, IGM.Int8PtrTy);
@@ -50,16 +51,30 @@ llvm::Value *IRGenFunction::emitUnconditionalDowncast(llvm::Value *from,
llvm::Value *metadataRef;
llvm::Constant *castFn;
if (isClass) {
// If the dest type is a concrete class, get the class metadata
// If the dest type is a concrete class, get the full class metadata
// and call dynamicCastClass directly.
metadataRef
= IGM.getAddrOfTypeMetadata(toType.getSwiftRValueType(), false, false);
castFn = IGM.getDynamicCastClassUnconditionalFn();
switch (mode) {
case CheckedCastMode::Unconditional:
castFn = IGM.getDynamicCastClassUnconditionalFn();
break;
case CheckedCastMode::Conditional:
castFn = IGM.getDynamicCastClassFn();
break;
}
} else {
// Otherwise, get the type metadata, which may be local, and go through
// the more general dynamicCast entry point.
metadataRef = emitTypeMetadataRef(toType);
castFn = IGM.getDynamicCastUnconditionalFn();
switch (mode) {
case CheckedCastMode::Unconditional:
castFn = IGM.getDynamicCastUnconditionalFn();
break;
case CheckedCastMode::Conditional:
castFn = IGM.getDynamicCastFn();
break;
}
}
if (metadataRef->getType() != IGM.Int8PtrTy)
@@ -76,7 +91,8 @@ llvm::Value *IRGenFunction::emitUnconditionalDowncast(llvm::Value *from,
}
void IRGenFunction::emitFakeExplosion(const TypeInfo &type, Explosion &explosion) {
void IRGenFunction::emitFakeExplosion(const TypeInfo &type,
Explosion &explosion) {
ExplosionSchema schema(explosion.getKind());
type.getSchema(schema);
for (auto &element : schema) {

View File

@@ -17,6 +17,7 @@
#include "swift/AST/ASTContext.h"
#include "swift/AST/Types.h"
#include "swift/AST/Decl.h"
#include "swift/SIL/SILInstruction.h"
#include "swift/SIL/SILType.h"
#include "llvm/IR/DerivedTypes.h"
@@ -684,7 +685,8 @@ void irgen::reemitAsSubstituted(IRGenFunction &IGF,
llvm::Value *
IRGenFunction::emitSuperToClassArchetypeConversion(llvm::Value *super,
SILType destType) {
SILType destType,
CheckedCastMode mode) {
assert(destType.is<ArchetypeType>() && "expected archetype type");
assert(destType.castTo<ArchetypeType>()->requiresClass()
&& "expected class archetype type");
@@ -698,9 +700,18 @@ IRGenFunction::emitSuperToClassArchetypeConversion(llvm::Value *super,
metadataRef = Builder.CreateBitCast(metadataRef, IGM.Int8PtrTy);
// Call the (unconditional) dynamic cast.
llvm::Value *castFn;
switch (mode) {
case CheckedCastMode::Unconditional:
castFn = IGM.getDynamicCastUnconditionalFn();
break;
case CheckedCastMode::Conditional:
castFn = IGM.getDynamicCastFn();
break;
}
auto call
= Builder.CreateCall2(IGM.getDynamicCastUnconditionalFn(),
super, metadataRef);
= Builder.CreateCall2(castFn, super, metadataRef);
// FIXME: Eventually, we may want to throw.
call->setDoesNotThrow();

View File

@@ -3897,10 +3897,11 @@ llvm::Value *irgen::emitClassExistentialProjection(IRGenFunction &IGF,
}
static Address
emitUnconditionalOpaqueDowncast(IRGenFunction &IGF,
Address value,
llvm::Value *srcMetadata,
SILType destType) {
emitOpaqueDowncast(IRGenFunction &IGF,
Address value,
llvm::Value *srcMetadata,
SILType destType,
CheckedCastMode mode) {
llvm::Value *addr = IGF.Builder.CreateBitCast(value.getAddress(),
IGF.IGM.OpaquePtrTy);
@@ -3908,9 +3909,17 @@ emitUnconditionalOpaqueDowncast(IRGenFunction &IGF,
llvm::Value *destMetadata = IGF.emitTypeMetadataRef(destType);
destMetadata = IGF.Builder.CreateBitCast(destMetadata, IGF.IGM.Int8PtrTy);
auto *call
= IGF.Builder.CreateCall3(IGF.IGM.getDynamicCastIndirectUnconditionalFn(),
addr, srcMetadata, destMetadata);
llvm::Value *castFn;
switch (mode) {
case CheckedCastMode::Unconditional:
castFn = IGF.IGM.getDynamicCastIndirectUnconditionalFn();
break;
case CheckedCastMode::Conditional:
castFn = IGF.IGM.getDynamicCastIndirectFn();
break;
}
auto *call = IGF.Builder.CreateCall3(castFn, addr, srcMetadata, destMetadata);
// FIXME: Eventually, we may want to throw.
call->setDoesNotThrow();
@@ -3921,24 +3930,26 @@ emitUnconditionalOpaqueDowncast(IRGenFunction &IGF,
return destTI.getAddressForPointer(ptr);
}
/// Emit a checked unconditional cast of an opaque archetype.
Address irgen::emitUnconditionalOpaqueArchetypeDowncast(IRGenFunction &IGF,
Address value,
SILType srcType,
SILType destType) {
/// Emit a checked cast of an opaque archetype.
Address irgen::emitOpaqueArchetypeDowncast(IRGenFunction &IGF,
Address value,
SILType srcType,
SILType destType,
CheckedCastMode mode) {
assert(srcType.is<ArchetypeType>());
assert(!srcType.castTo<ArchetypeType>()->requiresClass());
llvm::Value *srcMetadata = IGF.emitTypeMetadataRef(srcType);
return emitUnconditionalOpaqueDowncast(IGF, value, srcMetadata, destType);
return emitOpaqueDowncast(IGF, value, srcMetadata, destType, mode);
}
/// Emit a checked unconditional cast of an opaque existential container's
/// contained value.
Address irgen::emitUnconditionalOpaqueExistentialDowncast(IRGenFunction &IGF,
Address container,
SILType srcType,
SILType destType) {
Address irgen::emitOpaqueExistentialDowncast(IRGenFunction &IGF,
Address container,
SILType srcType,
SILType destType,
CheckedCastMode mode) {
assert(srcType.isExistentialType());
assert(!srcType.isClassExistentialType());
@@ -3949,5 +3960,5 @@ Address irgen::emitUnconditionalOpaqueExistentialDowncast(IRGenFunction &IGF,
std::tie(value, srcMetadata)
= emitOpaqueExistentialProjectionWithMetadata(IGF, container, srcType);
return emitUnconditionalOpaqueDowncast(IGF, value, srcMetadata, destType);
return emitOpaqueDowncast(IGF, value, srcMetadata, destType, mode);
}

View File

@@ -34,6 +34,7 @@ namespace swift {
class ProtocolConformance;
struct SILConstant;
class SILType;
enum class CheckedCastMode : unsigned char;
namespace irgen {
class AbstractCallee;
@@ -173,18 +174,20 @@ namespace irgen {
Explosion &value,
CanType type);
/// Emit a checked unconditional cast of an opaque archetype.
Address emitUnconditionalOpaqueArchetypeDowncast(IRGenFunction &IGF,
Address value,
SILType srcType,
SILType destType);
/// Emit a checked cast of an opaque archetype.
Address emitOpaqueArchetypeDowncast(IRGenFunction &IGF,
Address value,
SILType srcType,
SILType destType,
CheckedCastMode mode);
/// Emit a checked unconditional cast of an opaque existential container's
/// Emit a checked cast of an opaque existential container's
/// contained value.
Address emitUnconditionalOpaqueExistentialDowncast(IRGenFunction &IGF,
Address value,
SILType srcType,
SILType destType);
Address emitOpaqueExistentialDowncast(IRGenFunction &IGF,
Address value,
SILType srcType,
SILType destType,
CheckedCastMode mode);
} // end namespace irgen
} // end namespace swift

View File

@@ -51,6 +51,7 @@ namespace swift {
class TranslationUnit;
class ValueDecl;
class VarDecl;
enum class CheckedCastMode : unsigned char;
namespace Mangle {
enum class ExplosionKind : unsigned;
@@ -181,12 +182,14 @@ public:
/// \brief Convert the given explosion to the given destination archetype,
/// using a runtime-checked cast.
llvm::Value *emitSuperToClassArchetypeConversion(llvm::Value *super,
SILType destType);
SILType destType,
CheckedCastMode mode);
/// \brief Convert the given value to the given destination type, using a
/// runtime-checked cast.
llvm::Value *emitUnconditionalDowncast(llvm::Value *from,
SILType toType);
llvm::Value *emitDowncast(llvm::Value *from,
SILType toType,
CheckedCastMode mode);
//--- Declaration emission -----------------------------------------------------

View File

@@ -1291,7 +1291,7 @@ void IRGenSILFunction::visitSuperToArchetypeRefInst(
llvm::Value *in = super.claimNext();
Explosion out(CurExplosionLevel);
llvm::Value *cast
= emitSuperToClassArchetypeConversion(in, i->getType());
= emitSuperToClassArchetypeConversion(in, i->getType(), i->getMode());
out.add(cast);
newLoweredExplosion(SILValue(i, 0), out);
}
@@ -1300,7 +1300,7 @@ void IRGenSILFunction::visitDowncastArchetypeRefInst(
swift::DowncastArchetypeRefInst *i) {
Explosion archetype = getLoweredExplosion(i->getOperand());
llvm::Value *fromValue = archetype.claimNext();
llvm::Value *toValue = emitUnconditionalDowncast(fromValue, i->getType());
llvm::Value *toValue = emitDowncast(fromValue, i->getType(), i->getMode());
Explosion to(archetype.getKind());
to.add(toValue);
newLoweredExplosion(SILValue(i,0), to);
@@ -1313,7 +1313,7 @@ void IRGenSILFunction::visitDowncastExistentialRefInst(
= emitClassExistentialProjection(*this, existential,
i->getOperand().getType());
llvm::Value *toValue = emitUnconditionalDowncast(instance, i->getType());
llvm::Value *toValue = emitDowncast(instance, i->getType(), i->getMode());
Explosion to(existential.getKind());
to.add(toValue);
newLoweredExplosion(SILValue(i,0), to);
@@ -1322,42 +1322,39 @@ void IRGenSILFunction::visitDowncastExistentialRefInst(
void IRGenSILFunction::visitDowncastArchetypeAddrInst(
swift::DowncastArchetypeAddrInst *i) {
Address archetype = getLoweredAddress(i->getOperand());
Address cast = emitUnconditionalOpaqueArchetypeDowncast(*this, archetype,
i->getOperand().getType(),
i->getType());
Address cast = emitOpaqueArchetypeDowncast(*this, archetype,
i->getOperand().getType(),
i->getType(),
i->getMode());
newLoweredAddress(SILValue(i,0), cast);
}
void IRGenSILFunction::visitProjectDowncastExistentialAddrInst(
swift::ProjectDowncastExistentialAddrInst *i) {
Address existential = getLoweredAddress(i->getOperand());
Address cast = emitUnconditionalOpaqueExistentialDowncast(*this, existential,
i->getOperand().getType(),
i->getType());
Address cast = emitOpaqueExistentialDowncast(*this, existential,
i->getOperand().getType(),
i->getType(),
i->getMode());
newLoweredAddress(SILValue(i,0), cast);
}
void IRGenSILFunction::visitIsaInst(swift::IsaInst *i) {
// Emit the value we're testing.
Explosion from = getLoweredExplosion(i->getOperand());
llvm::Value *fromValue = from.claimNext();
fromValue = Builder.CreateBitCast(fromValue, IGM.Int8PtrTy);
// Emit the metadata of the type we're testing against.
CanType toType = i->getTestType().getSwiftRValueType();
Explosion metadata(ExplosionKind::Minimal);
emitMetaTypeRef(*this, toType, metadata);
llvm::Value *metadataValue = metadata.claimNext();
metadataValue = Builder.CreateBitCast(metadataValue, IGM.Int8PtrTy);
// Perform a checked cast.
auto call = Builder.CreateCall2(IGM.getDynamicCastClassFn(),
fromValue, metadataValue);
call->setDoesNotThrow();
void IRGenSILFunction::visitIsNonnullInst(swift::IsNonnullInst *i) {
// Get the value we're testing, which may be an address or an instance
// pointer.
llvm::Value *val;
LoweredValue const &lv = getLoweredValue(i->getOperand());
if (lv.isAddress()) {
val = lv.getAddress().getAddress();
} else {
Explosion values = lv.getExplosion(*this);
val = values.claimNext();
}
// Check that the result isn't null.
auto *valTy = cast<llvm::PointerType>(val->getType());
llvm::Value *result = Builder.CreateICmp(llvm::CmpInst::ICMP_NE,
call, llvm::ConstantPointerNull::get(IGM.Int8PtrTy));
val, llvm::ConstantPointerNull::get(valTy));
Explosion out(CurExplosionLevel);
out.add(result);
@@ -1383,9 +1380,7 @@ void IRGenSILFunction::visitDowncastInst(swift::DowncastInst *i) {
Explosion from = getLoweredExplosion(i->getOperand());
Explosion to(from.getKind());
llvm::Value *fromValue = from.claimNext();
llvm::Value *castValue = emitUnconditionalDowncast(
fromValue,
i->getType());
llvm::Value *castValue = emitDowncast(fromValue, i->getType(), i->getMode());
to.add(castValue);
newLoweredExplosion(SILValue(i, 0), to);
}

View File

@@ -539,7 +539,7 @@ public:
void visitProjectDowncastExistentialAddrInst(
ProjectDowncastExistentialAddrInst *i);
void visitIsaInst(IsaInst *i);
void visitIsNonnullInst(IsNonnullInst *i);
void visitIndexAddrInst(IndexAddrInst *i);
void visitIndexRawPointerInst(IndexRawPointerInst *i);