Index: llvm/trunk/lib/AsmParser/LLLexer.cpp =================================================================== --- llvm/trunk/lib/AsmParser/LLLexer.cpp +++ llvm/trunk/lib/AsmParser/LLLexer.cpp @@ -788,6 +788,7 @@ KEYWORD(info); KEYWORD(byte); KEYWORD(bit); + KEYWORD(varFlags); #undef KEYWORD Index: llvm/trunk/lib/AsmParser/LLParser.h =================================================================== --- llvm/trunk/lib/AsmParser/LLParser.h +++ llvm/trunk/lib/AsmParser/LLParser.h @@ -352,6 +352,7 @@ bool ParseVariableSummary(std::string Name, GlobalValue::GUID, unsigned ID); bool ParseAliasSummary(std::string Name, GlobalValue::GUID, unsigned ID); bool ParseGVFlags(GlobalValueSummary::GVFlags &GVFlags); + bool ParseGVarFlags(GlobalVarSummary::GVarFlags &GVarFlags); bool ParseOptionalFFlags(FunctionSummary::FFlags &FFlags); bool ParseOptionalCalls(std::vector &Calls); bool ParseHotness(CalleeInfo::HotnessType &Hotness); Index: llvm/trunk/lib/AsmParser/LLParser.cpp =================================================================== --- llvm/trunk/lib/AsmParser/LLParser.cpp +++ llvm/trunk/lib/AsmParser/LLParser.cpp @@ -7470,8 +7470,14 @@ return false; } -static ValueInfo EmptyVI = - ValueInfo(false, (GlobalValueSummaryMapTy::value_type *)-8); +auto FwdVIRef = (GlobalValueSummaryMapTy::value_type *)-8; + +static void resolveFwdRef(ValueInfo *Fwd, ValueInfo &Resolved) { + bool ReadOnly = Fwd->isReadOnly(); + *Fwd = Resolved; + if (ReadOnly) + Fwd->setReadOnly(); +} /// Stores the given Name/GUID and associated summary into the Index. /// Also updates any forward references to the associated entry ID. @@ -7507,9 +7513,9 @@ auto FwdRefVIs = ForwardRefValueInfos.find(ID); if (FwdRefVIs != ForwardRefValueInfos.end()) { for (auto VIRef : FwdRefVIs->second) { - assert(*VIRef.first == EmptyVI && + assert(VIRef.first->getRef() == FwdVIRef && "Forward referenced ValueInfo expected to be empty"); - *VIRef.first = VI; + resolveFwdRef(VIRef.first, VI); } ForwardRefValueInfos.erase(FwdRefVIs); } @@ -7699,11 +7705,14 @@ GlobalValueSummary::GVFlags GVFlags = GlobalValueSummary::GVFlags( /*Linkage=*/GlobalValue::ExternalLinkage, /*NotEligibleToImport=*/false, /*Live=*/false, /*IsLocal=*/false); + GlobalVarSummary::GVarFlags GVarFlags(/*ReadOnly*/ false); std::vector Refs; if (ParseToken(lltok::colon, "expected ':' here") || ParseToken(lltok::lparen, "expected '(' here") || ParseModuleReference(ModulePath) || - ParseToken(lltok::comma, "expected ',' here") || ParseGVFlags(GVFlags)) + ParseToken(lltok::comma, "expected ',' here") || ParseGVFlags(GVFlags) || + ParseToken(lltok::comma, "expected ',' here") || + ParseGVarFlags(GVarFlags)) return true; // Parse optional refs field @@ -7715,8 +7724,8 @@ if (ParseToken(lltok::rparen, "expected ')' here")) return true; - auto GS = llvm::make_unique( - GVFlags, GlobalVarSummary::GVarFlags(), std::move(Refs)); + auto GS = + llvm::make_unique(GVFlags, GVarFlags, std::move(Refs)); GS->setModulePath(ModulePath); @@ -7761,7 +7770,7 @@ AS->setModulePath(ModulePath); // Record forward reference if the aliasee is not parsed yet. - if (AliaseeVI == EmptyVI) { + if (AliaseeVI.getRef() == FwdVIRef) { auto FwdRef = ForwardRefAliasees.insert( std::make_pair(GVId, std::vector>())); FwdRef.first->second.push_back(std::make_pair(AS.get(), Loc)); @@ -7883,7 +7892,7 @@ // Keep track of the Call array index needing a forward reference. // We will save the location of the ValueInfo needing an update, but // can only do so once the std::vector is finalized. - if (VI == EmptyVI) + if (VI.getRef() == FwdVIRef) IdToIndexMap[GVId].push_back(std::make_pair(Calls.size(), Loc)); Calls.push_back(FunctionSummary::EdgeTy{VI, CalleeInfo(Hotness, RelBF)}); @@ -7895,7 +7904,7 @@ // of any forward GV references that need updating later. for (auto I : IdToIndexMap) { for (auto P : I.second) { - assert(Calls[P.first].first == EmptyVI && + assert(Calls[P.first].first.getRef() == FwdVIRef && "Forward referenced ValueInfo expected to be empty"); auto FwdRef = ForwardRefValueInfos.insert(std::make_pair( I.first, std::vector>())); @@ -7946,28 +7955,42 @@ ParseToken(lltok::lparen, "expected '(' in refs")) return true; - IdToIndexMapType IdToIndexMap; - // Parse each ref edge - do { + struct ValueContext { ValueInfo VI; - LocTy Loc = Lex.getLoc(); unsigned GVId; - if (ParseGVReference(VI, GVId)) + LocTy Loc; + }; + std::vector VContexts; + // Parse each ref edge + do { + ValueContext VC; + VC.Loc = Lex.getLoc(); + if (ParseGVReference(VC.VI, VC.GVId)) return true; + VContexts.push_back(VC); + } while (EatIfPresent(lltok::comma)); + // Sort value contexts so that ones with readonly ValueInfo are at the end + // of VContexts vector. This is needed to match immutableRefCount() behavior. + llvm::sort(VContexts, [](ValueContext &VC1, ValueContext &VC2) { + return VC1.VI.isReadOnly() < VC2.VI.isReadOnly(); + }); + + IdToIndexMapType IdToIndexMap; + for (auto &VC : VContexts) { // Keep track of the Refs array index needing a forward reference. // We will save the location of the ValueInfo needing an update, but // can only do so once the std::vector is finalized. - if (VI == EmptyVI) - IdToIndexMap[GVId].push_back(std::make_pair(Refs.size(), Loc)); - Refs.push_back(VI); - } while (EatIfPresent(lltok::comma)); + if (VC.VI.getRef() == FwdVIRef) + IdToIndexMap[VC.GVId].push_back(std::make_pair(Refs.size(), VC.Loc)); + Refs.push_back(VC.VI); + } // Now that the Refs vector is finalized, it is safe to save the locations // of any forward GV references that need updating later. for (auto I : IdToIndexMap) { for (auto P : I.second) { - assert(Refs[P.first] == EmptyVI && + assert(Refs[P.first].getRef() == FwdVIRef && "Forward referenced ValueInfo expected to be empty"); auto FwdRef = ForwardRefValueInfos.insert(std::make_pair( I.first, std::vector>())); @@ -8253,6 +8276,27 @@ return false; } +/// GVarFlags +/// ::= 'varFlags' ':' '(' 'readonly' ':' Flag ')' +bool LLParser::ParseGVarFlags(GlobalVarSummary::GVarFlags &GVarFlags) { + assert(Lex.getKind() == lltok::kw_varFlags); + Lex.Lex(); + + unsigned Flag; + if (ParseToken(lltok::colon, "expected ':' here") || + ParseToken(lltok::lparen, "expected '(' here") || + ParseToken(lltok::kw_readonly, "expected 'readonly' here") || + ParseToken(lltok::colon, "expected ':' here")) + return true; + + ParseFlag(Flag); + GVarFlags.ReadOnly = Flag; + + if (ParseToken(lltok::rparen, "expected ')' here")) + return true; + return false; +} + /// ModuleReference /// ::= 'module' ':' UInt bool LLParser::ParseModuleReference(StringRef &ModulePath) { @@ -8273,18 +8317,20 @@ /// GVReference /// ::= SummaryID bool LLParser::ParseGVReference(ValueInfo &VI, unsigned &GVId) { + bool ReadOnly = EatIfPresent(lltok::kw_readonly); if (ParseToken(lltok::SummaryID, "expected GV ID")) return true; GVId = Lex.getUIntVal(); - // Check if we already have a VI for this GV if (GVId < NumberedValueInfos.size()) { - assert(NumberedValueInfos[GVId] != EmptyVI); + assert(NumberedValueInfos[GVId].getRef() != FwdVIRef); VI = NumberedValueInfos[GVId]; } else // We will create a forward reference to the stored location. - VI = EmptyVI; + VI = ValueInfo(false, FwdVIRef); + if (ReadOnly) + VI.setReadOnly(); return false; } Index: llvm/trunk/lib/AsmParser/LLToken.h =================================================================== --- llvm/trunk/lib/AsmParser/LLToken.h +++ llvm/trunk/lib/AsmParser/LLToken.h @@ -418,6 +418,7 @@ kw_info, kw_byte, kw_bit, + kw_varFlags, // Unsigned Valued tokens (UIntVal). GlobalID, // @42 Index: llvm/trunk/lib/IR/AsmWriter.cpp =================================================================== --- llvm/trunk/lib/IR/AsmWriter.cpp +++ llvm/trunk/lib/IR/AsmWriter.cpp @@ -2821,7 +2821,7 @@ } void AssemblyWriter::printGlobalVarSummary(const GlobalVarSummary *GS) { - // Nothing for now + Out << ", varFlags: (readonly: " << GS->VarFlags.ReadOnly << ")"; } static std::string getLinkageName(GlobalValue::LinkageTypes LT) { @@ -3015,6 +3015,8 @@ FieldSeparator FS; for (auto &Ref : RefList) { Out << FS; + if (Ref.isReadOnly()) + Out << "readonly "; Out << "^" << Machine.getGUIDSlot(Ref.getGUID()); } Out << ")"; Index: llvm/trunk/test/Assembler/thinlto-summary.ll =================================================================== --- llvm/trunk/test/Assembler/thinlto-summary.ll +++ llvm/trunk/test/Assembler/thinlto-summary.ll @@ -9,7 +9,7 @@ ; Check a function that makes several calls with various profile hotness, and a ; reference (also tests forward references to function and variables in calls ; and refs). -^2 = gv: (guid: 1, summaries: (function: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 10, calls: ((callee: ^15, hotness: hot), (callee: ^17, hotness: cold), (callee: ^16, hotness: none)), refs: (^13)))) +^2 = gv: (guid: 1, summaries: (function: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 10, calls: ((callee: ^15, hotness: hot), (callee: ^17, hotness: cold), (callee: ^16, hotness: none)), refs: (readonly ^13, ^14)))) ; Function with a call that has relative block frequency instead of profile ; hotness. @@ -24,16 +24,16 @@ ^8 = gv: (guid: 7, summaries: (function: (module: ^0, flags: (linkage: linkonce_odr, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 1))) ^9 = gv: (guid: 8, summaries: (function: (module: ^0, flags: (linkage: weak_odr, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 1))) ^10 = gv: (guid: 9, summaries: (function: (module: ^0, flags: (linkage: weak, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 1))) -^11 = gv: (guid: 10, summaries: (variable: (module: ^0, flags: (linkage: common, notEligibleToImport: 0, live: 0, dsoLocal: 0)))) +^11 = gv: (guid: 10, summaries: (variable: (module: ^0, flags: (linkage: common, notEligibleToImport: 0, live: 0, dsoLocal: 0), varFlags: (readonly: 0)))) ; Test appending globel variable with reference (tests backward reference on ; refs). -^12 = gv: (guid: 11, summaries: (variable: (module: ^0, flags: (linkage: appending, notEligibleToImport: 0, live: 0, dsoLocal: 0), refs: (^4)))) +^12 = gv: (guid: 11, summaries: (variable: (module: ^0, flags: (linkage: appending, notEligibleToImport: 0, live: 0, dsoLocal: 0), varFlags: (readonly: 0), refs: (^4)))) ; Test a referenced global variable. -^13 = gv: (guid: 12, summaries: (variable: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 0)))) +^13 = gv: (guid: 12, summaries: (variable: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 0), varFlags: (readonly: 1)))) ; Test a dsoLocal variable. -^14 = gv: (guid: 13, summaries: (variable: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 1)))) +^14 = gv: (guid: 13, summaries: (variable: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 1), varFlags: (readonly: 0)))) ; Functions with various flag combinations (notEligibleToImport, Live, ; combinations of optional function flags). @@ -67,7 +67,7 @@ ; Make sure we get back from llvm-dis essentially what we put in via llvm-as. ; CHECK: ^0 = module: (path: "thinlto-summary1.o", hash: (1369602428, 2747878711, 259090915, 2507395659, 1141468049)) ; CHECK: ^1 = module: (path: "thinlto-summary2.o", hash: (2998369023, 4283347029, 1195487472, 2757298015, 1852134156)) -; CHECK: ^2 = gv: (guid: 1, summaries: (function: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 10, calls: ((callee: ^15, hotness: hot), (callee: ^17, hotness: cold), (callee: ^16, hotness: none)), refs: (^13)))) +; CHECK: ^2 = gv: (guid: 1, summaries: (function: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 10, calls: ((callee: ^15, hotness: hot), (callee: ^17, hotness: cold), (callee: ^16, hotness: none)), refs: (^14, readonly ^13)))) ; CHECK: ^3 = gv: (guid: 2, summaries: (function: (module: ^1, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 10, calls: ((callee: ^15))))) ; CHECK: ^4 = gv: (guid: 3, summaries: (function: (module: ^0, flags: (linkage: internal, notEligibleToImport: 0, live: 0, dsoLocal: 1), insts: 1))) ; CHECK: ^5 = gv: (guid: 4, summaries: (alias: (module: ^0, flags: (linkage: private, notEligibleToImport: 0, live: 0, dsoLocal: 1), aliasee: ^14))) @@ -76,10 +76,10 @@ ; CHECK: ^8 = gv: (guid: 7, summaries: (function: (module: ^0, flags: (linkage: linkonce_odr, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 1))) ; CHECK: ^9 = gv: (guid: 8, summaries: (function: (module: ^0, flags: (linkage: weak_odr, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 1))) ; CHECK: ^10 = gv: (guid: 9, summaries: (function: (module: ^0, flags: (linkage: weak, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 1))) -; CHECK: ^11 = gv: (guid: 10, summaries: (variable: (module: ^0, flags: (linkage: common, notEligibleToImport: 0, live: 0, dsoLocal: 0)))) -; CHECK: ^12 = gv: (guid: 11, summaries: (variable: (module: ^0, flags: (linkage: appending, notEligibleToImport: 0, live: 0, dsoLocal: 0), refs: (^4)))) -; CHECK: ^13 = gv: (guid: 12, summaries: (variable: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 0)))) -; CHECK: ^14 = gv: (guid: 13, summaries: (variable: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 1)))) +; CHECK: ^11 = gv: (guid: 10, summaries: (variable: (module: ^0, flags: (linkage: common, notEligibleToImport: 0, live: 0, dsoLocal: 0), varFlags: (readonly: 0)))) +; CHECK: ^12 = gv: (guid: 11, summaries: (variable: (module: ^0, flags: (linkage: appending, notEligibleToImport: 0, live: 0, dsoLocal: 0), varFlags: (readonly: 0), refs: (^4)))) +; CHECK: ^13 = gv: (guid: 12, summaries: (variable: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 0), varFlags: (readonly: 1)))) +; CHECK: ^14 = gv: (guid: 13, summaries: (variable: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 1), varFlags: (readonly: 0)))) ; CHECK: ^15 = gv: (guid: 14, summaries: (function: (module: ^1, flags: (linkage: external, notEligibleToImport: 1, live: 1, dsoLocal: 0), insts: 1))) ; CHECK: ^16 = gv: (guid: 15, summaries: (function: (module: ^1, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 1, funcFlags: (readNone: 1, readOnly: 0, noRecurse: 1, returnDoesNotAlias: 0, noInline: 0)))) ; CHECK: ^17 = gv: (guid: 16, summaries: (function: (module: ^1, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 1, funcFlags: (readNone: 0, readOnly: 1, noRecurse: 0, returnDoesNotAlias: 1, noInline: 0), calls: ((callee: ^15))))) Index: llvm/trunk/test/Bitcode/thinlto-function-summary-callgraph-relbf.ll =================================================================== --- llvm/trunk/test/Bitcode/thinlto-function-summary-callgraph-relbf.ll +++ llvm/trunk/test/Bitcode/thinlto-function-summary-callgraph-relbf.ll @@ -38,5 +38,5 @@ ; DIS: ^0 = module: (path: "{{.*}}", hash: (0, 0, 0, 0, 0)) ; DIS: ^1 = gv: (name: "func") ; guid = 7289175272376759421 -; DIS: ^2 = gv: (name: "main", summaries: (function: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 3, calls: ((callee: ^1, relbf: 256)), refs: (^3)))) ; guid = 15822663052811949562 +; DIS: ^2 = gv: (name: "main", summaries: (function: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 3, calls: ((callee: ^1, relbf: 256)), refs: (readonly ^3)))) ; guid = 15822663052811949562 ; DIS: ^3 = gv: (name: "undefinedglob") ; guid = 18036901804029949403 Index: llvm/trunk/test/Bitcode/thinlto-function-summary-refgraph.ll =================================================================== --- llvm/trunk/test/Bitcode/thinlto-function-summary-refgraph.ll +++ llvm/trunk/test/Bitcode/thinlto-function-summary-refgraph.ll @@ -154,11 +154,11 @@ ; DIS-DAG: = gv: (name: "foo") ; guid = 6699318081062747564 ; DIS-DAG: = gv: (name: "func") ; guid = 7289175272376759421 ; DIS-DAG: = gv: (name: "func3") ; guid = 11517462787082255043 -; DIS-DAG: = gv: (name: "globalvar", summaries: (variable: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 0)))) ; guid = 12887606300320728018 +; DIS-DAG: = gv: (name: "globalvar", summaries: (variable: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 0), varFlags: (readonly: 1)))) ; guid = 12887606300320728018 ; DIS-DAG: = gv: (name: "func2") ; guid = 14069196320850861797 ; DIS-DAG: = gv: (name: "llvm.ctpop.i8") ; guid = 15254915475081819833 ; DIS-DAG: = gv: (name: "main", summaries: (function: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 9, calls: ((callee: ^{{.*}})), refs: (^{{.*}})))) ; guid = 15822663052811949562 -; DIS-DAG: = gv: (name: "bar", summaries: (variable: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 0), refs: (^{{.*}})))) ; guid = 16434608426314478903 +; DIS-DAG: = gv: (name: "bar", summaries: (variable: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 0), varFlags: (readonly: 1), refs: (^{{.*}})))) ; guid = 16434608426314478903 ; Don't try to match the exact GUID. Since it is private, the file path ; will get hashed, and that will be test dependent. ; DIS-DAG: = gv: (name: "Y", summaries: (function: (module: ^0, flags: (linkage: private, notEligibleToImport: 0, live: 0, dsoLocal: 1), insts: 14, calls: ((callee: ^{{.*}}))))) ; guid =