| //===--- TypeLayoutVerifier.cpp -------------------------------------------===// |
| // |
| // 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 a generator that produces code to verify that IRGen's |
| // static assumptions about data layout for a Swift type correspond to the |
| // runtime's understanding of data layout. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "llvm/IR/Function.h" |
| #include "llvm/IR/Module.h" |
| #include "swift/AST/ASTContext.h" |
| #include "swift/AST/DiagnosticsIRGen.h" |
| #include "swift/AST/IRGenOptions.h" |
| #include "swift/AST/Types.h" |
| #include "swift/SIL/SILModule.h" |
| #include "EnumPayload.h" |
| #include "IRGenDebugInfo.h" |
| #include "IRGenFunction.h" |
| #include "IRGenModule.h" |
| #include "GenOpaque.h" |
| #include "GenType.h" |
| #include "FixedTypeInfo.h" |
| |
| using namespace swift; |
| using namespace irgen; |
| |
| IRGenTypeVerifierFunction::IRGenTypeVerifierFunction(IRGenModule &IGM, |
| llvm::Function *f) |
| : IRGenFunction(IGM, f), VerifierFn(IGM.getVerifyTypeLayoutAttributeFn()) { |
| // Verifier functions are always artificial. |
| if (IGM.DebugInfo) |
| IGM.DebugInfo->emitArtificialFunction(*this, f); |
| } |
| |
| void |
| IRGenTypeVerifierFunction::emit(ArrayRef<CanType> formalTypes) { |
| auto getSizeConstant = [&](Size sz) -> llvm::Constant * { |
| return llvm::ConstantInt::get(IGM.SizeTy, sz.getValue()); |
| }; |
| auto getAlignmentMaskConstant = [&](Alignment a) -> llvm::Constant * { |
| return llvm::ConstantInt::get(IGM.SizeTy, a.getValue() - 1); |
| }; |
| auto getBoolConstant = [&](bool b) -> llvm::Constant * { |
| return llvm::ConstantInt::get(IGM.Int1Ty, b); |
| }; |
| |
| SmallString<20> numberBuf; |
| |
| for (auto formalType : formalTypes) { |
| // Runtime type metadata always represents the maximal abstraction level of |
| // the type. |
| auto maxAbstraction = AbstractionPattern::getOpaque(); |
| auto layoutType = IGM.getLoweredType(maxAbstraction, formalType); |
| auto &ti = getTypeInfo(layoutType); |
| auto metadata = emitTypeMetadataRef(formalType); |
| |
| // Check type metrics for fixed-layout types. |
| // If there's no fixed type info, we rely on the runtime for type metrics, |
| // so there's no compile-time values to validate against. |
| if (auto *fixedTI = dyn_cast<FixedTypeInfo>(&ti)) { |
| // Check that the fixed layout matches the runtime layout. |
| verifyValues(metadata, |
| emitLoadOfSize(*this, layoutType), |
| getSizeConstant(fixedTI->getFixedSize()), |
| "size"); |
| verifyValues(metadata, |
| emitLoadOfAlignmentMask(*this, layoutType), |
| getAlignmentMaskConstant(fixedTI->getFixedAlignment()), |
| "alignment mask"); |
| verifyValues(metadata, |
| emitLoadOfStride(*this, layoutType), |
| getSizeConstant(fixedTI->getFixedStride()), |
| "stride"); |
| verifyValues(metadata, |
| emitLoadOfIsInline(*this, layoutType), |
| getBoolConstant(fixedTI->getFixedPacking(IGM) |
| == FixedPacking::OffsetZero), |
| "is-inline bit"); |
| verifyValues(metadata, |
| emitLoadOfIsPOD(*this, layoutType), |
| getBoolConstant(fixedTI->isPOD(ResilienceExpansion::Maximal)), |
| "is-POD bit"); |
| verifyValues(metadata, |
| emitLoadOfIsBitwiseTakable(*this, layoutType), |
| getBoolConstant(fixedTI->isBitwiseTakable(ResilienceExpansion::Maximal)), |
| "is-bitwise-takable bit"); |
| unsigned xiCount = fixedTI->getFixedExtraInhabitantCount(IGM); |
| verifyValues(metadata, |
| emitLoadOfHasExtraInhabitants(*this, layoutType), |
| getBoolConstant(xiCount != 0), |
| "has-extra-inhabitants bit"); |
| |
| // Check extra inhabitants. |
| if (xiCount > 0) { |
| verifyValues(metadata, |
| emitLoadOfExtraInhabitantCount(*this, layoutType), |
| getSizeConstant(Size(xiCount)), |
| "extra inhabitant count"); |
| |
| // Verify that the extra inhabitant representations are consistent. |
| |
| // TODO: Update for EnumPayload implementation changes. |
| auto xiBuf = createAlloca(fixedTI->getStorageType(), |
| fixedTI->getFixedAlignment(), |
| "extra-inhabitant"); |
| auto fixedXIBuf = createAlloca(fixedTI->getStorageType(), |
| fixedTI->getFixedAlignment(), |
| "extra-inhabitant"); |
| auto xiOpaque = Builder.CreateBitCast(xiBuf, IGM.OpaquePtrTy); |
| auto fixedXIOpaque = Builder.CreateBitCast(fixedXIBuf, |
| IGM.OpaquePtrTy); |
| auto xiMask = fixedTI->getFixedExtraInhabitantMask(IGM); |
| auto xiSchema = EnumPayloadSchema::withBitSize(xiMask.getBitWidth()); |
| |
| // TODO: Randomize the set of extra inhabitants we check. |
| unsigned bits = fixedTI->getFixedSize().getValueInBits(); |
| for (unsigned i = 0, e = std::min(xiCount, 256u); |
| i < e; ++i) { |
| // Initialize the buffer with junk, to help ensure we're insensitive to |
| // insignificant bits. |
| // TODO: Randomize the filler. |
| Builder.CreateMemSet(xiBuf.getAddress(), |
| llvm::ConstantInt::get(IGM.Int8Ty, 0x5A), |
| fixedTI->getFixedSize().getValue(), |
| fixedTI->getFixedAlignment().getValue()); |
| |
| // Ask the runtime to store an extra inhabitant. |
| auto index = llvm::ConstantInt::get(IGM.Int32Ty, i); |
| emitStoreExtraInhabitantCall(*this, layoutType, index, xiOpaque); |
| |
| // Compare the stored extra inhabitant against the fixed extra |
| // inhabitant pattern. |
| auto fixedXIValue |
| = fixedTI->getFixedExtraInhabitantValue(IGM, bits, i); |
| auto fixedXIPayload = |
| EnumPayload::fromBitPattern(IGM, fixedXIValue, |
| xiSchema); |
| fixedXIPayload.store(*this, fixedXIBuf); |
| |
| auto runtimeXIPayload = EnumPayload::load(*this, xiBuf, xiSchema); |
| runtimeXIPayload.emitApplyAndMask(*this, xiMask); |
| runtimeXIPayload.store(*this, xiBuf); |
| |
| numberBuf.clear(); |
| { |
| llvm::raw_svector_ostream os(numberBuf); |
| os << i; |
| } |
| |
| verifyBuffers(metadata, xiBuf, fixedXIBuf, fixedTI->getFixedSize(), |
| llvm::Twine("stored extra inhabitant ") + numberBuf.str()); |
| |
| // Now ask the runtime to identify the fixed extra inhabitant value. |
| // Mask in junk to make sure the runtime correctly ignores it. |
| // TODO: Randomize the filler. |
| auto xiFill = ~APInt(fixedXIValue.getBitWidth(), 0); |
| xiFill &= ~xiMask; |
| fixedXIValue |= xiFill; |
| |
| auto maskedXIPayload = EnumPayload::fromBitPattern(IGM, |
| fixedXIValue, xiSchema); |
| maskedXIPayload.store(*this, fixedXIBuf); |
| |
| auto runtimeIndex = emitGetExtraInhabitantIndexCall(*this, layoutType, |
| fixedXIOpaque); |
| verifyValues(metadata, |
| runtimeIndex, index, |
| llvm::Twine("extra inhabitant index calculation ") |
| + numberBuf.str()); |
| } |
| } |
| } |
| ti.verify(*this, metadata, layoutType); |
| } |
| |
| Builder.CreateRetVoid(); |
| } |
| |
| void |
| IRGenTypeVerifierFunction::verifyValues(llvm::Value *typeMetadata, |
| llvm::Value *runtimeVal, |
| llvm::Value *staticVal, |
| const llvm::Twine &description) { |
| assert(runtimeVal->getType() == staticVal->getType()); |
| // Get or create buffers for the arguments. |
| VerifierArgumentBuffers bufs; |
| auto foundBufs = VerifierArgBufs.find(runtimeVal->getType()); |
| if (foundBufs != VerifierArgBufs.end()) { |
| bufs = foundBufs->second; |
| } else { |
| Address runtimeBuf = createAlloca(runtimeVal->getType(), |
| IGM.getPointerAlignment(), |
| "runtime"); |
| Address staticBuf = createAlloca(staticVal->getType(), |
| IGM.getPointerAlignment(), |
| "static"); |
| bufs = {runtimeBuf, staticBuf}; |
| VerifierArgBufs[runtimeVal->getType()] = bufs; |
| } |
| |
| Builder.CreateStore(runtimeVal, bufs.runtimeBuf); |
| Builder.CreateStore(staticVal, bufs.staticBuf); |
| |
| auto runtimePtr = Builder.CreateBitCast(bufs.runtimeBuf.getAddress(), |
| IGM.Int8PtrTy); |
| auto staticPtr = Builder.CreateBitCast(bufs.staticBuf.getAddress(), |
| IGM.Int8PtrTy); |
| auto count = llvm::ConstantInt::get(IGM.SizeTy, |
| IGM.DataLayout.getTypeStoreSize(runtimeVal->getType())); |
| auto msg |
| = IGM.getAddrOfGlobalString(description.str()); |
| |
| Builder.CreateCall( |
| VerifierFn, {typeMetadata, runtimePtr, staticPtr, count, msg}); |
| } |
| |
| void |
| IRGenTypeVerifierFunction::verifyBuffers(llvm::Value *typeMetadata, |
| Address runtimeBuf, |
| Address staticBuf, |
| Size size, |
| const llvm::Twine &description) { |
| auto runtimePtr = Builder.CreateBitCast(runtimeBuf.getAddress(), |
| IGM.Int8PtrTy); |
| auto staticPtr = Builder.CreateBitCast(staticBuf.getAddress(), |
| IGM.Int8PtrTy); |
| auto count = llvm::ConstantInt::get(IGM.SizeTy, |
| size.getValue()); |
| auto msg |
| = IGM.getAddrOfGlobalString(description.str()); |
| |
| Builder.CreateCall( |
| VerifierFn, {typeMetadata, runtimePtr, staticPtr, count, msg}); |
| } |
| |
| void IRGenModule::emitTypeVerifier() { |
| // Look up the types to verify. |
| |
| SmallVector<CanType, 4> TypesToVerify; |
| for (auto name : IRGen.Opts.VerifyTypeLayoutNames) { |
| // Look up the name in the module. |
| SmallVector<ValueDecl*, 1> lookup; |
| swift::ModuleDecl *M = getSwiftModule(); |
| M->lookupMember(lookup, M, DeclName(Context.getIdentifier(name)), |
| Identifier()); |
| if (lookup.empty()) { |
| Context.Diags.diagnose(SourceLoc(), diag::type_to_verify_not_found, |
| name); |
| continue; |
| } |
| |
| TypeDecl *typeDecl = nullptr; |
| for (auto decl : lookup) { |
| if (auto td = dyn_cast<TypeDecl>(decl)) { |
| if (typeDecl) { |
| Context.Diags.diagnose(SourceLoc(), diag::type_to_verify_ambiguous, |
| name); |
| goto next; |
| } |
| typeDecl = td; |
| break; |
| } |
| } |
| if (!typeDecl) { |
| Context.Diags.diagnose(SourceLoc(), diag::type_to_verify_not_found, name); |
| continue; |
| } |
| |
| { |
| auto type = typeDecl->getDeclaredInterfaceType(); |
| if (type->hasTypeParameter()) { |
| Context.Diags.diagnose(SourceLoc(), diag::type_to_verify_dependent, |
| name); |
| continue; |
| } |
| |
| TypesToVerify.push_back(type->getCanonicalType()); |
| } |
| next:; |
| } |
| if (TypesToVerify.empty()) |
| return; |
| |
| // Find the entry point. |
| SILFunction *EntryPoint = |
| getSILModule().lookUpFunction(SWIFT_ENTRY_POINT_FUNCTION); |
| |
| if (!EntryPoint) |
| return; |
| |
| llvm::Function *EntryFunction = Module.getFunction(EntryPoint->getName()); |
| if (!EntryFunction) |
| return; |
| |
| // Create a new function to contain our logic. |
| auto fnTy = llvm::FunctionType::get(VoidTy, /*varArg*/ false); |
| auto VerifierFunction = llvm::Function::Create(fnTy, |
| llvm::GlobalValue::PrivateLinkage, |
| "type_verifier", |
| getModule()); |
| VerifierFunction->setAttributes(constructInitialAttributes()); |
| |
| // Insert a call into the entry function. |
| { |
| llvm::BasicBlock *EntryBB = &EntryFunction->getEntryBlock(); |
| llvm::BasicBlock::iterator IP = EntryBB->getFirstInsertionPt(); |
| IRBuilder Builder(getLLVMContext(), DebugInfo != nullptr); |
| Builder.llvm::IRBuilderBase::SetInsertPoint(EntryBB, IP); |
| if (DebugInfo) |
| DebugInfo->setEntryPointLoc(Builder); |
| Builder.CreateCall(VerifierFunction, {}); |
| } |
| |
| IRGenTypeVerifierFunction VerifierIGF(*this, VerifierFunction); |
| VerifierIGF.emit(TypesToVerify); |
| } |