Skip to content

Commit 8e1e666

Browse files
committed
IRGen: Start a type verifier to fuzz static type info against runtime type info.
We've had a rash of bugs due to inconsistencies between how IRGen and the runtime think types are laid out. Add a '-verify-type-layout' mode to the frontend that causes IRGen to emit a bunch of code that compares its static assumptions against what the runtime value witness does. Swift SVN r24918
1 parent f49ab3f commit 8e1e666

13 files changed

+361
-2
lines changed

Diff for: include/swift/AST/IRGenOptions.h

+4-1
Original file line numberDiff line numberDiff line change
@@ -100,14 +100,17 @@ class IRGenOptions {
100100

101101
/// Special codegen for playgrounds.
102102
unsigned Playground : 1;
103+
104+
/// Emit code to verify that static and runtime type layout are consistent.
105+
unsigned VerifyTypeLayout : 1;
103106

104107
IRGenOptions() : OutputKind(IRGenOutputKind::LLVMAssembly), Verify(true),
105108
Optimize(false), DebugInfoKind(IRGenDebugInfoKind::None),
106109
UseJIT(false), EnableDynamicValueTypeLayout(false),
107110
DisableLLVMOptzns(false), DisableLLVMARCOpts(false),
108111
DisableLLVMSLPVectorizer(false),
109112
DisableFPElim(true), HasUnderlyingModule(false),
110-
Playground(false) {}
113+
Playground(false), VerifyTypeLayout(false) {}
111114
};
112115

113116
} // end namespace swift

Diff for: include/swift/Option/FrontendOptions.td

+3
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,9 @@ def disable_func_sig_opts : Flag<["-"], "disable-func-sig-opts">,
250250

251251
def interpret : Flag<["-"], "interpret">, HelpText<"Immediate mode">, ModeOpt;
252252

253+
def verify_type_layout : Flag<["-"], "verify-type-layout">,
254+
HelpText<"Verify compile-time and runtime type layout information">;
255+
253256
def enable_guaranteed_self : Flag<["-"], "enable-guaranteed-self">,
254257
HelpText<"Enable the passing of self as a guaranteed parameter">;
255258

Diff for: lib/Frontend/CompilerInvocation.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -932,6 +932,9 @@ static bool ParseIRGenArgs(IRGenOptions &Opts, ArgList &Args,
932932
if (Args.hasArg(OPT_use_jit))
933933
Opts.UseJIT = true;
934934

935+
if (Args.hasArg(OPT_verify_type_layout))
936+
Opts.VerifyTypeLayout = true;
937+
935938
return false;
936939
}
937940

Diff for: lib/IRGen/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ add_swift_library(swiftIRGen
2727
Linking.cpp
2828
SwiftTargetInfo.cpp
2929
StructLayout.cpp
30+
TypeLayoutVerifier.cpp
3031
UnimplementedTypeInfo.cpp
3132
LINK_LIBRARIES
3233
swiftAST

Diff for: lib/IRGen/GenDecl.cpp

+33
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
#include "GenClass.h"
4444
#include "GenObjC.h"
4545
#include "GenMeta.h"
46+
#include "GenType.h"
4647
#include "IRGenDebugInfo.h"
4748
#include "IRGenFunction.h"
4849
#include "IRGenModule.h"
@@ -787,6 +788,38 @@ void IRGenModule::emitVTableStubs() {
787788
}
788789
}
789790

791+
void IRGenModule::emitTypeVerifier() {
792+
// Find the entry point.
793+
SILFunction *EntryPoint = SILMod->lookUpFunction(SWIFT_ENTRY_POINT_FUNCTION);
794+
795+
if (!EntryPoint)
796+
return;
797+
798+
llvm::Function *EntryFunction = Module.getFunction(EntryPoint->getName());
799+
if (!EntryFunction)
800+
return;
801+
802+
// Create a new function to contain our logic.
803+
auto fnTy = llvm::FunctionType::get(VoidTy, /*varArg*/ false);
804+
auto VerifierFunction = llvm::Function::Create(fnTy,
805+
llvm::GlobalValue::PrivateLinkage,
806+
"type_verifier",
807+
getModule());
808+
809+
// Insert a call into the entry function.
810+
{
811+
llvm::BasicBlock *EntryBB = &EntryFunction->getEntryBlock();
812+
llvm::BasicBlock::iterator IP = EntryBB->getFirstInsertionPt();
813+
IRBuilder Builder(getLLVMContext());
814+
Builder.llvm::IRBuilderBase::SetInsertPoint(EntryBB, IP);
815+
Builder.CreateCall(VerifierFunction);
816+
}
817+
818+
IRGenFunction VerifierIGF(*this, VerifierFunction);
819+
emitTypeLayoutVerifier(VerifierIGF, TypesToVerify);
820+
VerifierIGF.Builder.CreateRetVoid();
821+
}
822+
790823
/// Get SIL-linkage for something that's not required to be visible
791824
/// and doesn't actually need to be uniqued.
792825
static SILLinkage getNonUniqueSILLinkage(FormalLinkage linkage,

Diff for: lib/IRGen/GenOpaque.cpp

+29
Original file line numberDiff line numberDiff line change
@@ -583,6 +583,16 @@ llvm::Value *irgen::emitLoadOfIsPOD(IRGenFunction &IGF, llvm::Value *wtable) {
583583
wtable->getName() + ".isPOD");
584584
}
585585

586+
/// Load the 'isBitwiseTakable' valueWitness from the given table as an i1.
587+
llvm::Value *irgen::emitLoadOfIsBitwiseTakable(IRGenFunction &IGF,
588+
llvm::Value *wtable) {
589+
auto flags = emitLoadOfValueWitness(IGF, wtable, ValueWitness::Flags);
590+
auto mask = IGF.IGM.getSize(Size(ValueWitnessFlags::IsNonBitwiseTakable));
591+
auto masked = IGF.Builder.CreateAnd(flags, mask);
592+
return IGF.Builder.CreateICmpEQ(masked, IGF.IGM.getSize(Size(0)),
593+
wtable->getName() + ".isBitwiseTakable");
594+
}
595+
586596
/// Load the 'isInline' valueWitness from the given table as an i1.
587597
llvm::Value *irgen::emitLoadOfIsInline(IRGenFunction &IGF,
588598
llvm::Value *wtable) {
@@ -593,7 +603,26 @@ llvm::Value *irgen::emitLoadOfIsInline(IRGenFunction &IGF,
593603
wtable->getName() + ".isInline");
594604
}
595605

606+
/// Load the 'hasExtraInhabitants' valueWitness from the given table as an i1.
607+
llvm::Value *irgen::emitLoadOfHasExtraInhabitants(IRGenFunction &IGF,
608+
llvm::Value *wtable) {
609+
auto flags = emitLoadOfValueWitness(IGF, wtable, ValueWitness::Flags);
610+
auto mask = IGF.IGM.getSize(Size(ValueWitnessFlags::Enum_HasExtraInhabitants));
611+
auto masked = IGF.Builder.CreateAnd(flags, mask);
612+
return IGF.Builder.CreateICmpNE(masked, IGF.IGM.getSize(Size(0)),
613+
wtable->getName() + ".hasExtraInhabitants");
614+
}
615+
596616
/// Load the 'stride' value witness from the given table as a size_t.
597617
llvm::Value *irgen::emitLoadOfStride(IRGenFunction &IGF, llvm::Value *wtable) {
598618
return emitLoadOfValueWitness(IGF, wtable, ValueWitness::Stride);
599619
}
620+
621+
llvm::Value *irgen::emitLoadOfExtraInhabitantCount(IRGenFunction &IGF,
622+
llvm::Value *wtable) {
623+
auto xiFlags = emitLoadOfValueWitness(IGF, wtable,
624+
ValueWitness::ExtraInhabitantFlags);
625+
auto mask = IGF.IGM.getSize(
626+
Size(ExtraInhabitantFlags::NumExtraInhabitantsMask));
627+
return IGF.Builder.CreateAnd(xiFlags, mask);
628+
}

Diff for: lib/IRGen/GenOpaque.h

+14
Original file line numberDiff line numberDiff line change
@@ -157,8 +157,22 @@ namespace irgen {
157157
/// Emit a load of the 'isPOD' value witness.
158158
llvm::Value *emitLoadOfIsPOD(IRGenFunction &IGF, llvm::Value *vwtable);
159159

160+
/// Emit a load of the 'isBitwiseTakable' value witness.
161+
llvm::Value *emitLoadOfIsBitwiseTakable(IRGenFunction &IGF,
162+
llvm::Value *vwtable);
163+
160164
/// Emit a load of the 'isInline' value witness.
161165
llvm::Value *emitLoadOfIsInline(IRGenFunction &IGF, llvm::Value *vwtable);
166+
167+
/// Emit a load of the 'hasExtraInhabitants' value witness.
168+
llvm::Value *emitLoadOfHasExtraInhabitants(IRGenFunction &IGF,
169+
llvm::Value *vwtable);
170+
171+
/// Emit a load of the 'extraInhabitantCount' value witness.
172+
/// The type must be dynamically known to have extra inhabitant witnesses.
173+
llvm::Value *emitLoadOfExtraInhabitantCount(IRGenFunction &IGF,
174+
llvm::Value *vwtable);
175+
162176
} // end namespace irgen
163177
} // end namespace swift
164178

Diff for: lib/IRGen/GenType.cpp

+11
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
#include "swift/AST/CanTypeVisitor.h"
1818
#include "swift/AST/Decl.h"
19+
#include "swift/AST/IRGenOptions.h"
1920
#include "swift/AST/PrettyStackTrace.h"
2021
#include "swift/AST/Types.h"
2122
#include "swift/SIL/SILModule.h"
@@ -1181,6 +1182,16 @@ TypeCacheEntry TypeConverter::getTypeEntry(CanType canonicalTy) {
11811182
convertedTI->NextConverted = FirstType;
11821183
FirstType = convertedTI;
11831184
}
1185+
1186+
// Verify fixed-layout, non-dependent, nominal types.
1187+
// TODO: A better mechanism to control what types get verified.
1188+
if (IGM.Opts.VerifyTypeLayout
1189+
&& isa<FixedTypeInfo>(convertedTI)
1190+
&& canonicalTy->getAnyNominal()
1191+
&& !canonicalTy->isDependentType()
1192+
&& !canonicalTy->hasArchetype()) {
1193+
IGM.TypesToVerify.push_back(canonicalTy);
1194+
}
11841195

11851196
return convertedTI;
11861197
}

Diff for: lib/IRGen/GenType.h

+5-1
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ class TypeConverter {
172172

173173
ArchetypeType *getExemplarArchetype(ArchetypeType *t);
174174
CanType getExemplarType(CanType t);
175-
175+
176176
class Types_t {
177177
llvm::DenseMap<TypeBase*, TypeCacheEntry> IndependentCache;
178178
llvm::DenseMap<TypeBase*, TypeCacheEntry> DependentCache;
@@ -218,6 +218,10 @@ class GenericContextScope {
218218
TC.popGenericContext(sig);
219219
}
220220
};
221+
222+
/// Generate code to verify that static type assumptions agree with the runtime.
223+
void emitTypeLayoutVerifier(IRGenFunction &IGF,
224+
ArrayRef<CanType> formalTypes);
221225

222226
} // end namespace irgen
223227
} // end namespace swift

Diff for: lib/IRGen/IRGen.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,10 @@ static std::unique_ptr<llvm::Module> performIRGeneration(IRGenOptions &Opts,
177177
// Emit symbols for eliminated dead methods.
178178
IGM.emitVTableStubs();
179179

180+
// Verify type layout if we were asked to.
181+
if (Opts.VerifyTypeLayout)
182+
IGM.emitTypeVerifier();
183+
180184
// Register our info with the runtime if needed.
181185
if (Opts.UseJIT)
182186
IGM.emitRuntimeRegistration();

Diff for: lib/IRGen/IRGenModule.h

+2
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,7 @@ class IRGenModule {
286286

287287
private:
288288
TypeConverter &Types;
289+
std::vector<CanType> TypesToVerify;
289290
friend class TypeConverter;
290291

291292
const clang::ASTContext *ClangASTContext;
@@ -493,6 +494,7 @@ private: \
493494
void emitRuntimeRegistration();
494495
void emitLazyDefinitions();
495496
void emitVTableStubs();
497+
void emitTypeVerifier();
496498
private:
497499
void emitGlobalDecl(Decl *D);
498500
void emitExternalDefinition(Decl *D);

0 commit comments

Comments
 (0)