10b57cec5SDimitry Andric //===--- InterfaceStubFunctionsConsumer.cpp -------------------------------===// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric 90b57cec5SDimitry Andric #include "clang/AST/Mangle.h" 100b57cec5SDimitry Andric #include "clang/AST/RecursiveASTVisitor.h" 115ffd83dbSDimitry Andric #include "clang/Basic/TargetInfo.h" 120b57cec5SDimitry Andric #include "clang/Frontend/CompilerInstance.h" 130b57cec5SDimitry Andric #include "clang/Frontend/FrontendActions.h" 140b57cec5SDimitry Andric #include "clang/Sema/TemplateInstCallback.h" 150b57cec5SDimitry Andric #include "llvm/BinaryFormat/ELF.h" 160b57cec5SDimitry Andric 170b57cec5SDimitry Andric using namespace clang; 180b57cec5SDimitry Andric 19a7dea167SDimitry Andric namespace { 200b57cec5SDimitry Andric class InterfaceStubFunctionsConsumer : public ASTConsumer { 210b57cec5SDimitry Andric CompilerInstance &Instance; 220b57cec5SDimitry Andric StringRef InFile; 230b57cec5SDimitry Andric StringRef Format; 240b57cec5SDimitry Andric std::set<std::string> ParsedTemplates; 250b57cec5SDimitry Andric 260b57cec5SDimitry Andric enum RootDeclOrigin { TopLevel = 0, FromTU = 1, IsLate = 2 }; 270b57cec5SDimitry Andric struct MangledSymbol { 280b57cec5SDimitry Andric std::string ParentName; 290b57cec5SDimitry Andric uint8_t Type; 300b57cec5SDimitry Andric uint8_t Binding; 310b57cec5SDimitry Andric std::vector<std::string> Names; 320b57cec5SDimitry Andric MangledSymbol() = delete; 330b57cec5SDimitry Andric 340b57cec5SDimitry Andric MangledSymbol(const std::string &ParentName, uint8_t Type, uint8_t Binding, 350b57cec5SDimitry Andric std::vector<std::string> Names) 36*0fca6ea1SDimitry Andric : ParentName(ParentName), Type(Type), Binding(Binding), 37*0fca6ea1SDimitry Andric Names(std::move(Names)) {} 380b57cec5SDimitry Andric }; 390b57cec5SDimitry Andric using MangledSymbols = std::map<const NamedDecl *, MangledSymbol>; 400b57cec5SDimitry Andric 410b57cec5SDimitry Andric bool WriteNamedDecl(const NamedDecl *ND, MangledSymbols &Symbols, int RDO) { 420b57cec5SDimitry Andric // Here we filter out anything that's not set to DefaultVisibility. 430b57cec5SDimitry Andric // DefaultVisibility is set on a decl when -fvisibility is not specified on 440b57cec5SDimitry Andric // the command line (or specified as default) and the decl does not have 450b57cec5SDimitry Andric // __attribute__((visibility("hidden"))) set or when the command line 460b57cec5SDimitry Andric // argument is set to hidden but the decl explicitly has 470b57cec5SDimitry Andric // __attribute__((visibility ("default"))) set. We do this so that the user 480b57cec5SDimitry Andric // can have fine grain control of what they want to expose in the stub. 490b57cec5SDimitry Andric auto isVisible = [](const NamedDecl *ND) -> bool { 500b57cec5SDimitry Andric return ND->getVisibility() == DefaultVisibility; 510b57cec5SDimitry Andric }; 520b57cec5SDimitry Andric 530b57cec5SDimitry Andric auto ignoreDecl = [this, isVisible](const NamedDecl *ND) -> bool { 540b57cec5SDimitry Andric if (!isVisible(ND)) 550b57cec5SDimitry Andric return true; 560b57cec5SDimitry Andric 57480093f4SDimitry Andric if (const VarDecl *VD = dyn_cast<VarDecl>(ND)) { 58480093f4SDimitry Andric if (const auto *Parent = VD->getParentFunctionOrMethod()) 59480093f4SDimitry Andric if (isa<BlockDecl>(Parent) || isa<CXXMethodDecl>(Parent)) 60480093f4SDimitry Andric return true; 61480093f4SDimitry Andric 620b57cec5SDimitry Andric if ((VD->getStorageClass() == StorageClass::SC_Extern) || 630b57cec5SDimitry Andric (VD->getStorageClass() == StorageClass::SC_Static && 640b57cec5SDimitry Andric VD->getParentFunctionOrMethod() == nullptr)) 650b57cec5SDimitry Andric return true; 66480093f4SDimitry Andric } 670b57cec5SDimitry Andric 680b57cec5SDimitry Andric if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(ND)) { 690b57cec5SDimitry Andric if (FD->isInlined() && !isa<CXXMethodDecl>(FD) && 700b57cec5SDimitry Andric !Instance.getLangOpts().GNUInline) 710b57cec5SDimitry Andric return true; 720b57cec5SDimitry Andric if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(FD)) { 730b57cec5SDimitry Andric if (const auto *RC = dyn_cast<CXXRecordDecl>(MD->getParent())) 740b57cec5SDimitry Andric if (isa<ClassTemplateDecl>(RC->getParent()) || !isVisible(RC)) 750b57cec5SDimitry Andric return true; 760b57cec5SDimitry Andric if (MD->isDependentContext() || !MD->hasBody()) 770b57cec5SDimitry Andric return true; 780b57cec5SDimitry Andric } 790b57cec5SDimitry Andric if (FD->getStorageClass() == StorageClass::SC_Static) 800b57cec5SDimitry Andric return true; 810b57cec5SDimitry Andric } 820b57cec5SDimitry Andric return false; 830b57cec5SDimitry Andric }; 840b57cec5SDimitry Andric 850b57cec5SDimitry Andric auto getParentFunctionDecl = [](const NamedDecl *ND) -> const NamedDecl * { 860b57cec5SDimitry Andric if (const VarDecl *VD = dyn_cast<VarDecl>(ND)) 870b57cec5SDimitry Andric if (const auto *FD = 880b57cec5SDimitry Andric dyn_cast_or_null<FunctionDecl>(VD->getParentFunctionOrMethod())) 890b57cec5SDimitry Andric return FD; 900b57cec5SDimitry Andric return nullptr; 910b57cec5SDimitry Andric }; 920b57cec5SDimitry Andric 930b57cec5SDimitry Andric auto getMangledNames = [](const NamedDecl *ND) -> std::vector<std::string> { 940b57cec5SDimitry Andric if (!ND) 950b57cec5SDimitry Andric return {""}; 960b57cec5SDimitry Andric ASTNameGenerator NameGen(ND->getASTContext()); 970b57cec5SDimitry Andric std::vector<std::string> MangledNames = NameGen.getAllManglings(ND); 980b57cec5SDimitry Andric if (isa<CXXConstructorDecl>(ND) || isa<CXXDestructorDecl>(ND)) 990b57cec5SDimitry Andric return MangledNames; 1000b57cec5SDimitry Andric #ifdef EXPENSIVE_CHECKS 1010b57cec5SDimitry Andric assert(MangledNames.size() <= 1 && "Expected only one name mangling."); 1020b57cec5SDimitry Andric #endif 1030b57cec5SDimitry Andric return {NameGen.getName(ND)}; 1040b57cec5SDimitry Andric }; 1050b57cec5SDimitry Andric 1060b57cec5SDimitry Andric if (!(RDO & FromTU)) 1070b57cec5SDimitry Andric return true; 1080b57cec5SDimitry Andric if (Symbols.find(ND) != Symbols.end()) 1090b57cec5SDimitry Andric return true; 1100b57cec5SDimitry Andric // - Currently have not figured out how to produce the names for FieldDecls. 1110b57cec5SDimitry Andric // - Do not want to produce symbols for function paremeters. 1120b57cec5SDimitry Andric if (isa<FieldDecl>(ND) || isa<ParmVarDecl>(ND)) 1130b57cec5SDimitry Andric return true; 1140b57cec5SDimitry Andric 1150b57cec5SDimitry Andric const NamedDecl *ParentDecl = getParentFunctionDecl(ND); 1160b57cec5SDimitry Andric if ((ParentDecl && ignoreDecl(ParentDecl)) || ignoreDecl(ND)) 1170b57cec5SDimitry Andric return true; 1180b57cec5SDimitry Andric 1190b57cec5SDimitry Andric if (RDO & IsLate) { 1200b57cec5SDimitry Andric Instance.getDiagnostics().Report(diag::err_asm_invalid_type_in_input) 1210b57cec5SDimitry Andric << "Generating Interface Stubs is not supported with " 1220b57cec5SDimitry Andric "delayed template parsing."; 1230b57cec5SDimitry Andric } else { 1240b57cec5SDimitry Andric if (const auto *FD = dyn_cast<FunctionDecl>(ND)) 1250b57cec5SDimitry Andric if (FD->isDependentContext()) 1260b57cec5SDimitry Andric return true; 1270b57cec5SDimitry Andric 1280b57cec5SDimitry Andric const bool IsWeak = (ND->hasAttr<WeakAttr>() || 1290b57cec5SDimitry Andric ND->hasAttr<WeakRefAttr>() || ND->isWeakImported()); 1300b57cec5SDimitry Andric 1310b57cec5SDimitry Andric Symbols.insert(std::make_pair( 1320b57cec5SDimitry Andric ND, 1330b57cec5SDimitry Andric MangledSymbol(getMangledNames(ParentDecl).front(), 1340b57cec5SDimitry Andric // Type: 1350b57cec5SDimitry Andric isa<VarDecl>(ND) ? llvm::ELF::STT_OBJECT 1360b57cec5SDimitry Andric : llvm::ELF::STT_FUNC, 1370b57cec5SDimitry Andric // Binding: 1380b57cec5SDimitry Andric IsWeak ? llvm::ELF::STB_WEAK : llvm::ELF::STB_GLOBAL, 1390b57cec5SDimitry Andric getMangledNames(ND)))); 1400b57cec5SDimitry Andric } 1410b57cec5SDimitry Andric return true; 1420b57cec5SDimitry Andric } 1430b57cec5SDimitry Andric 1440b57cec5SDimitry Andric void 1450b57cec5SDimitry Andric HandleDecls(const llvm::iterator_range<DeclContext::decl_iterator> &Decls, 1460b57cec5SDimitry Andric MangledSymbols &Symbols, int RDO) { 1470b57cec5SDimitry Andric for (const auto *D : Decls) 1480b57cec5SDimitry Andric HandleNamedDecl(dyn_cast<NamedDecl>(D), Symbols, RDO); 1490b57cec5SDimitry Andric } 1500b57cec5SDimitry Andric 1510b57cec5SDimitry Andric void HandleTemplateSpecializations(const FunctionTemplateDecl &FTD, 1520b57cec5SDimitry Andric MangledSymbols &Symbols, int RDO) { 1530b57cec5SDimitry Andric for (const auto *D : FTD.specializations()) 1540b57cec5SDimitry Andric HandleNamedDecl(dyn_cast<NamedDecl>(D), Symbols, RDO); 1550b57cec5SDimitry Andric } 1560b57cec5SDimitry Andric 1570b57cec5SDimitry Andric void HandleTemplateSpecializations(const ClassTemplateDecl &CTD, 1580b57cec5SDimitry Andric MangledSymbols &Symbols, int RDO) { 1590b57cec5SDimitry Andric for (const auto *D : CTD.specializations()) 1600b57cec5SDimitry Andric HandleNamedDecl(dyn_cast<NamedDecl>(D), Symbols, RDO); 1610b57cec5SDimitry Andric } 1620b57cec5SDimitry Andric 1630b57cec5SDimitry Andric bool HandleNamedDecl(const NamedDecl *ND, MangledSymbols &Symbols, int RDO) { 1640b57cec5SDimitry Andric if (!ND) 1650b57cec5SDimitry Andric return false; 1660b57cec5SDimitry Andric 1670b57cec5SDimitry Andric switch (ND->getKind()) { 1680b57cec5SDimitry Andric default: 1690b57cec5SDimitry Andric break; 1700b57cec5SDimitry Andric case Decl::Kind::Namespace: 1710b57cec5SDimitry Andric HandleDecls(cast<NamespaceDecl>(ND)->decls(), Symbols, RDO); 1720b57cec5SDimitry Andric return true; 1730b57cec5SDimitry Andric case Decl::Kind::CXXRecord: 1740b57cec5SDimitry Andric HandleDecls(cast<CXXRecordDecl>(ND)->decls(), Symbols, RDO); 1750b57cec5SDimitry Andric return true; 1760b57cec5SDimitry Andric case Decl::Kind::ClassTemplateSpecialization: 1770b57cec5SDimitry Andric HandleDecls(cast<ClassTemplateSpecializationDecl>(ND)->decls(), Symbols, 1780b57cec5SDimitry Andric RDO); 1790b57cec5SDimitry Andric return true; 1800b57cec5SDimitry Andric case Decl::Kind::ClassTemplate: 1810b57cec5SDimitry Andric HandleTemplateSpecializations(*cast<ClassTemplateDecl>(ND), Symbols, RDO); 1820b57cec5SDimitry Andric return true; 1830b57cec5SDimitry Andric case Decl::Kind::FunctionTemplate: 1840b57cec5SDimitry Andric HandleTemplateSpecializations(*cast<FunctionTemplateDecl>(ND), Symbols, 1850b57cec5SDimitry Andric RDO); 1860b57cec5SDimitry Andric return true; 187a7dea167SDimitry Andric case Decl::Kind::Record: 188a7dea167SDimitry Andric case Decl::Kind::Typedef: 189a7dea167SDimitry Andric case Decl::Kind::Enum: 190a7dea167SDimitry Andric case Decl::Kind::EnumConstant: 1910b57cec5SDimitry Andric case Decl::Kind::TemplateTypeParm: 192480093f4SDimitry Andric case Decl::Kind::NonTypeTemplateParm: 193480093f4SDimitry Andric case Decl::Kind::CXXConversion: 194480093f4SDimitry Andric case Decl::Kind::UnresolvedUsingValue: 195480093f4SDimitry Andric case Decl::Kind::Using: 196480093f4SDimitry Andric case Decl::Kind::UsingShadow: 197480093f4SDimitry Andric case Decl::Kind::TypeAliasTemplate: 198480093f4SDimitry Andric case Decl::Kind::TypeAlias: 199480093f4SDimitry Andric case Decl::Kind::VarTemplate: 200480093f4SDimitry Andric case Decl::Kind::VarTemplateSpecialization: 201480093f4SDimitry Andric case Decl::Kind::UsingDirective: 202480093f4SDimitry Andric case Decl::Kind::TemplateTemplateParm: 203480093f4SDimitry Andric case Decl::Kind::ClassTemplatePartialSpecialization: 204480093f4SDimitry Andric case Decl::Kind::IndirectField: 205480093f4SDimitry Andric case Decl::Kind::ConstructorUsingShadow: 206480093f4SDimitry Andric case Decl::Kind::CXXDeductionGuide: 207480093f4SDimitry Andric case Decl::Kind::NamespaceAlias: 208480093f4SDimitry Andric case Decl::Kind::UnresolvedUsingTypename: 2090b57cec5SDimitry Andric return true; 210480093f4SDimitry Andric case Decl::Kind::Var: { 211480093f4SDimitry Andric // Bail on any VarDecl that either has no named symbol. 212480093f4SDimitry Andric if (!ND->getIdentifier()) 213480093f4SDimitry Andric return true; 214480093f4SDimitry Andric const auto *VD = cast<VarDecl>(ND); 215480093f4SDimitry Andric // Bail on any VarDecl that is a dependent or templated type. 216480093f4SDimitry Andric if (VD->isTemplated() || VD->getType()->isDependentType()) 217480093f4SDimitry Andric return true; 218480093f4SDimitry Andric if (WriteNamedDecl(ND, Symbols, RDO)) 219480093f4SDimitry Andric return true; 220480093f4SDimitry Andric break; 221480093f4SDimitry Andric } 2220b57cec5SDimitry Andric case Decl::Kind::ParmVar: 2230b57cec5SDimitry Andric case Decl::Kind::CXXMethod: 2240b57cec5SDimitry Andric case Decl::Kind::CXXConstructor: 2250b57cec5SDimitry Andric case Decl::Kind::CXXDestructor: 2260b57cec5SDimitry Andric case Decl::Kind::Function: 2270b57cec5SDimitry Andric case Decl::Kind::Field: 2280b57cec5SDimitry Andric if (WriteNamedDecl(ND, Symbols, RDO)) 2290b57cec5SDimitry Andric return true; 2300b57cec5SDimitry Andric } 2310b57cec5SDimitry Andric 2320b57cec5SDimitry Andric // While interface stubs are in the development stage, it's probably best to 2330b57cec5SDimitry Andric // catch anything that's not a VarDecl or Template/FunctionDecl. 2340b57cec5SDimitry Andric Instance.getDiagnostics().Report(diag::err_asm_invalid_type_in_input) 2350b57cec5SDimitry Andric << "Expected a function or function template decl."; 2360b57cec5SDimitry Andric return false; 2370b57cec5SDimitry Andric } 2380b57cec5SDimitry Andric 2390b57cec5SDimitry Andric public: 2400b57cec5SDimitry Andric InterfaceStubFunctionsConsumer(CompilerInstance &Instance, StringRef InFile, 2410b57cec5SDimitry Andric StringRef Format) 2420b57cec5SDimitry Andric : Instance(Instance), InFile(InFile), Format(Format) {} 2430b57cec5SDimitry Andric 2440b57cec5SDimitry Andric void HandleTranslationUnit(ASTContext &context) override { 2450b57cec5SDimitry Andric struct Visitor : public RecursiveASTVisitor<Visitor> { 2460b57cec5SDimitry Andric bool VisitNamedDecl(NamedDecl *ND) { 2470b57cec5SDimitry Andric if (const auto *FD = dyn_cast<FunctionDecl>(ND)) 2480b57cec5SDimitry Andric if (FD->isLateTemplateParsed()) { 2490b57cec5SDimitry Andric LateParsedDecls.insert(FD); 2500b57cec5SDimitry Andric return true; 2510b57cec5SDimitry Andric } 2520b57cec5SDimitry Andric 2530b57cec5SDimitry Andric if (const auto *VD = dyn_cast<ValueDecl>(ND)) { 2540b57cec5SDimitry Andric ValueDecls.insert(VD); 2550b57cec5SDimitry Andric return true; 2560b57cec5SDimitry Andric } 2570b57cec5SDimitry Andric 2580b57cec5SDimitry Andric NamedDecls.insert(ND); 2590b57cec5SDimitry Andric return true; 2600b57cec5SDimitry Andric } 2610b57cec5SDimitry Andric 2620b57cec5SDimitry Andric std::set<const NamedDecl *> LateParsedDecls; 2630b57cec5SDimitry Andric std::set<NamedDecl *> NamedDecls; 2640b57cec5SDimitry Andric std::set<const ValueDecl *> ValueDecls; 2650b57cec5SDimitry Andric } v; 2660b57cec5SDimitry Andric 2670b57cec5SDimitry Andric v.TraverseDecl(context.getTranslationUnitDecl()); 2680b57cec5SDimitry Andric 2690b57cec5SDimitry Andric MangledSymbols Symbols; 2700b57cec5SDimitry Andric auto OS = Instance.createDefaultOutputFile(/*Binary=*/false, InFile, "ifs"); 2710b57cec5SDimitry Andric if (!OS) 2720b57cec5SDimitry Andric return; 2730b57cec5SDimitry Andric 2740b57cec5SDimitry Andric if (Instance.getLangOpts().DelayedTemplateParsing) { 2750b57cec5SDimitry Andric clang::Sema &S = Instance.getSema(); 2760b57cec5SDimitry Andric for (const auto *FD : v.LateParsedDecls) { 2770b57cec5SDimitry Andric clang::LateParsedTemplate &LPT = 2780b57cec5SDimitry Andric *S.LateParsedTemplateMap.find(cast<FunctionDecl>(FD))->second; 2790b57cec5SDimitry Andric S.LateTemplateParser(S.OpaqueParser, LPT); 2800b57cec5SDimitry Andric HandleNamedDecl(FD, Symbols, (FromTU | IsLate)); 2810b57cec5SDimitry Andric } 2820b57cec5SDimitry Andric } 2830b57cec5SDimitry Andric 2840b57cec5SDimitry Andric for (const NamedDecl *ND : v.ValueDecls) 2850b57cec5SDimitry Andric HandleNamedDecl(ND, Symbols, FromTU); 2860b57cec5SDimitry Andric for (const NamedDecl *ND : v.NamedDecls) 2870b57cec5SDimitry Andric HandleNamedDecl(ND, Symbols, FromTU); 2880b57cec5SDimitry Andric 289480093f4SDimitry Andric auto writeIfsV1 = [this](const llvm::Triple &T, 290480093f4SDimitry Andric const MangledSymbols &Symbols, 2910b57cec5SDimitry Andric const ASTContext &context, StringRef Format, 2920b57cec5SDimitry Andric raw_ostream &OS) -> void { 2930b57cec5SDimitry Andric OS << "--- !" << Format << "\n"; 294fe6060f1SDimitry Andric OS << "IfsVersion: 3.0\n"; 295fe6060f1SDimitry Andric OS << "Target: " << T.str() << "\n"; 2960b57cec5SDimitry Andric OS << "Symbols:\n"; 2970b57cec5SDimitry Andric for (const auto &E : Symbols) { 2980b57cec5SDimitry Andric const MangledSymbol &Symbol = E.second; 299*0fca6ea1SDimitry Andric for (const auto &Name : Symbol.Names) { 3005ffd83dbSDimitry Andric OS << " - { Name: \"" 3010b57cec5SDimitry Andric << (Symbol.ParentName.empty() || Instance.getLangOpts().CPlusPlus 3020b57cec5SDimitry Andric ? "" 3030b57cec5SDimitry Andric : (Symbol.ParentName + ".")) 3045ffd83dbSDimitry Andric << Name << "\", Type: "; 3050b57cec5SDimitry Andric switch (Symbol.Type) { 3060b57cec5SDimitry Andric default: 3070b57cec5SDimitry Andric llvm_unreachable( 308a7dea167SDimitry Andric "clang -emit-interface-stubs: Unexpected symbol type."); 3090b57cec5SDimitry Andric case llvm::ELF::STT_NOTYPE: 3100b57cec5SDimitry Andric OS << "NoType"; 3110b57cec5SDimitry Andric break; 3120b57cec5SDimitry Andric case llvm::ELF::STT_OBJECT: { 3130b57cec5SDimitry Andric auto VD = cast<ValueDecl>(E.first)->getType(); 3140b57cec5SDimitry Andric OS << "Object, Size: " 3150b57cec5SDimitry Andric << context.getTypeSizeInChars(VD).getQuantity(); 3160b57cec5SDimitry Andric break; 3170b57cec5SDimitry Andric } 3180b57cec5SDimitry Andric case llvm::ELF::STT_FUNC: 3190b57cec5SDimitry Andric OS << "Func"; 3200b57cec5SDimitry Andric break; 3210b57cec5SDimitry Andric } 3220b57cec5SDimitry Andric if (Symbol.Binding == llvm::ELF::STB_WEAK) 3230b57cec5SDimitry Andric OS << ", Weak: true"; 3240b57cec5SDimitry Andric OS << " }\n"; 3250b57cec5SDimitry Andric } 3260b57cec5SDimitry Andric } 3270b57cec5SDimitry Andric OS << "...\n"; 3280b57cec5SDimitry Andric OS.flush(); 3290b57cec5SDimitry Andric }; 3300b57cec5SDimitry Andric 331fe6060f1SDimitry Andric assert(Format == "ifs-v1" && "Unexpected IFS Format."); 332a7dea167SDimitry Andric writeIfsV1(Instance.getTarget().getTriple(), Symbols, context, Format, *OS); 3330b57cec5SDimitry Andric } 3340b57cec5SDimitry Andric }; 335a7dea167SDimitry Andric } // namespace 3360b57cec5SDimitry Andric 3370b57cec5SDimitry Andric std::unique_ptr<ASTConsumer> 3385ffd83dbSDimitry Andric GenerateInterfaceStubsAction::CreateASTConsumer(CompilerInstance &CI, 3390b57cec5SDimitry Andric StringRef InFile) { 340fe6060f1SDimitry Andric return std::make_unique<InterfaceStubFunctionsConsumer>(CI, InFile, "ifs-v1"); 3410b57cec5SDimitry Andric } 342