Index: llvm/include/llvm/IR/ModuleSummaryIndex.h =================================================================== --- llvm/include/llvm/IR/ModuleSummaryIndex.h +++ llvm/include/llvm/IR/ModuleSummaryIndex.h @@ -757,14 +757,29 @@ public: struct GVarFlags { - GVarFlags(bool ReadOnly, bool WriteOnly) - : MaybeReadOnly(ReadOnly), MaybeWriteOnly(WriteOnly) {} - - // In permodule summaries both MaybeReadOnly and MaybeWriteOnly - // bits are set, because attribute propagation occurs later on - // thin link phase. + GVarFlags(bool ReadOnly, bool WriteOnly, bool Constant) + : MaybeReadOnly(ReadOnly), MaybeWriteOnly(WriteOnly), + Constant(Constant) {} + + // If true indicates that this global variable might be accessed + // purely by non-volatile load instructions. This in turn means + // it can be internalized in source and destination modules during + // thin LTO import because it neither modified nor its address + // is taken. unsigned MaybeReadOnly : 1; + // If true indicates that variable is possibly only written to, so + // its value isn't loaded and its address isn't taken anywhere. + // False, when 'Constant' attribute is set. unsigned MaybeWriteOnly : 1; + // Indicates that value is a compile-time constant. Global variable + // can be 'Constant' while not being 'ReadOnly' on several occasions: + // - it is volatile, (e.g mapped device address) + // - its address is taken, meaning that unlike 'ReadOnly' vars we can't + // internalize it. + // Constant variables are always imported thus giving compiler an + // opportunity to make some extra optimizations. Readonly constants + // are also internalized. + unsigned Constant : 1; } VarFlags; GlobalVarSummary(GVFlags Flags, GVarFlags VarFlags, @@ -782,6 +797,7 @@ void setWriteOnly(bool WO) { VarFlags.MaybeWriteOnly = WO; } bool maybeReadOnly() const { return VarFlags.MaybeReadOnly; } bool maybeWriteOnly() const { return VarFlags.MaybeWriteOnly; } + bool isConstant() const { return VarFlags.Constant; } void setVTableFuncs(VTableFuncList Funcs) { assert(!VTableFuncs); Index: llvm/lib/Analysis/ModuleSummaryAnalysis.cpp =================================================================== --- llvm/lib/Analysis/ModuleSummaryAnalysis.cpp +++ llvm/lib/Analysis/ModuleSummaryAnalysis.cpp @@ -599,7 +599,9 @@ bool CanBeInternalized = !V.hasComdat() && !V.hasAppendingLinkage() && !V.isInterposable() && !V.hasAvailableExternallyLinkage() && !V.hasDLLExportStorageClass(); - GlobalVarSummary::GVarFlags VarFlags(CanBeInternalized, CanBeInternalized); + bool Constant = V.isConstant(); + GlobalVarSummary::GVarFlags VarFlags( + CanBeInternalized, Constant ? false : CanBeInternalized, Constant); auto GVarSummary = std::make_unique(Flags, VarFlags, RefEdges.takeVector()); if (NonRenamableLocal) @@ -718,7 +720,9 @@ } else { std::unique_ptr Summary = std::make_unique( - GVFlags, GlobalVarSummary::GVarFlags(false, false), + GVFlags, + GlobalVarSummary::GVarFlags( + false, false, cast(GV)->isConstant()), ArrayRef{}); Index.addGlobalValueSummary(*GV, std::move(Summary)); } Index: llvm/lib/AsmParser/LLParser.cpp =================================================================== --- llvm/lib/AsmParser/LLParser.cpp +++ llvm/lib/AsmParser/LLParser.cpp @@ -8155,7 +8155,8 @@ /*Linkage=*/GlobalValue::ExternalLinkage, /*NotEligibleToImport=*/false, /*Live=*/false, /*IsLocal=*/false, /*CanAutoHide=*/false); GlobalVarSummary::GVarFlags GVarFlags(/*ReadOnly*/ false, - /* WriteOnly */ false); + /* WriteOnly */ false, + /* Constant */ false); std::vector Refs; VTableFuncList VTableFuncs; if (ParseToken(lltok::colon, "expected ':' here") || @@ -8827,7 +8828,8 @@ /// GVarFlags /// ::= 'varFlags' ':' '(' 'readonly' ':' Flag -/// ',' 'writeonly' ':' Flag ')' +/// ',' 'writeonly' ':' Flag +/// ',' 'constant' ':' Flag ')' bool LLParser::ParseGVarFlags(GlobalVarSummary::GVarFlags &GVarFlags) { assert(Lex.getKind() == lltok::kw_varFlags); Lex.Lex(); @@ -8856,6 +8858,11 @@ return true; GVarFlags.MaybeWriteOnly = Flag; break; + case lltok::kw_constant: + if (ParseRest(Flag)) + return true; + GVarFlags.Constant = Flag; + break; default: return Error(Lex.getLoc(), "expected gvar flag type"); } Index: llvm/lib/Bitcode/Reader/BitcodeReader.cpp =================================================================== --- llvm/lib/Bitcode/Reader/BitcodeReader.cpp +++ llvm/lib/Bitcode/Reader/BitcodeReader.cpp @@ -986,7 +986,8 @@ // Decode the flags for GlobalVariable in the summary static GlobalVarSummary::GVarFlags getDecodedGVarFlags(uint64_t RawFlags) { return GlobalVarSummary::GVarFlags((RawFlags & 0x1) ? true : false, - (RawFlags & 0x2) ? true : false); + (RawFlags & 0x2) ? true : false, + (RawFlags & 0x4) ? true : false); } static GlobalValue::VisibilityTypes getDecodedVisibility(unsigned Val) { @@ -5965,7 +5966,8 @@ uint64_t RawFlags = Record[1]; unsigned RefArrayStart = 2; GlobalVarSummary::GVarFlags GVF(/* ReadOnly */ false, - /* WriteOnly */ false); + /* WriteOnly */ false, + /* Constant */ false); auto Flags = getDecodedGVSummaryFlags(RawFlags, Version); if (Version >= 5) { GVF = getDecodedGVarFlags(Record[2]); @@ -6101,7 +6103,8 @@ uint64_t RawFlags = Record[2]; unsigned RefArrayStart = 3; GlobalVarSummary::GVarFlags GVF(/* ReadOnly */ false, - /* WriteOnly */ false); + /* WriteOnly */ false, + /* Constant */ false); auto Flags = getDecodedGVSummaryFlags(RawFlags, Version); if (Version >= 5) { GVF = getDecodedGVarFlags(Record[3]); Index: llvm/lib/Bitcode/Writer/BitcodeWriter.cpp =================================================================== --- llvm/lib/Bitcode/Writer/BitcodeWriter.cpp +++ llvm/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -1028,7 +1028,8 @@ } static uint64_t getEncodedGVarFlags(GlobalVarSummary::GVarFlags Flags) { - uint64_t RawFlags = Flags.MaybeReadOnly | (Flags.MaybeWriteOnly << 1); + uint64_t RawFlags = + Flags.MaybeReadOnly | (Flags.MaybeWriteOnly << 1) | (Flags.Constant << 2); return RawFlags; } Index: llvm/lib/IR/AsmWriter.cpp =================================================================== --- llvm/lib/IR/AsmWriter.cpp +++ llvm/lib/IR/AsmWriter.cpp @@ -2897,7 +2897,8 @@ void AssemblyWriter::printGlobalVarSummary(const GlobalVarSummary *GS) { Out << ", varFlags: (readonly: " << GS->VarFlags.MaybeReadOnly << ", " - << "writeonly: " << GS->VarFlags.MaybeWriteOnly << ")"; + << "writeonly: " << GS->VarFlags.MaybeWriteOnly << ", " + << "constant: " << GS->VarFlags.Constant << ")"; auto VTableFuncs = GS->vTableFuncs(); if (!VTableFuncs.empty()) { Index: llvm/lib/IR/ModuleSummaryIndex.cpp =================================================================== --- llvm/lib/IR/ModuleSummaryIndex.cpp +++ llvm/lib/IR/ModuleSummaryIndex.cpp @@ -221,7 +221,8 @@ // c) Link error (external declaration with internal definition). // However we do not promote objects referenced by writeonly GV // initializer by means of converting it to 'zeroinitializer' - return !isReadOnly(GVS) && !isWriteOnly(GVS) && GVS->refs().size(); + return !GVS->isConstant() && !isReadOnly(GVS) && !isWriteOnly(GVS) && + GVS->refs().size(); }; auto *GVS = cast(S->getBaseObject()); @@ -405,6 +406,12 @@ return false; } +static bool hasConstantFlag(const GlobalValueSummary *S) { + if (auto *GVS = dyn_cast(S)) + return GVS->isConstant(); + return false; +} + void ModuleSummaryIndex::exportToDot( raw_ostream &OS, const DenseSet &GUIDPreservedSymbols) const { @@ -482,6 +489,8 @@ A.addComment("immutable"); if (Flags.Live && hasWriteOnlyFlag(SummaryIt.second)) A.addComment("writeOnly"); + if (Flags.Live && hasConstantFlag(SummaryIt.second)) + A.addComment("constant"); } if (Flags.DSOLocal) A.addComment("dsoLocal"); Index: llvm/test/Assembler/thinlto-summary.ll =================================================================== --- llvm/test/Assembler/thinlto-summary.ll +++ llvm/test/Assembler/thinlto-summary.ll @@ -76,10 +76,10 @@ ; CHECK: ^8 = gv: (guid: 7, summaries: (function: (module: ^0, flags: (linkage: linkonce_odr, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), insts: 1))) ; CHECK: ^9 = gv: (guid: 8, summaries: (function: (module: ^0, flags: (linkage: weak_odr, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 1), insts: 1))) ; CHECK: ^10 = gv: (guid: 9, summaries: (function: (module: ^0, flags: (linkage: weak, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), insts: 1))) -; CHECK: ^11 = gv: (guid: 10, summaries: (variable: (module: ^0, flags: (linkage: common, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), varFlags: (readonly: 0, writeonly: 0)))) -; CHECK: ^12 = gv: (guid: 11, summaries: (variable: (module: ^0, flags: (linkage: appending, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), varFlags: (readonly: 0, writeonly: 0), refs: (^4)))) -; CHECK: ^13 = gv: (guid: 12, summaries: (variable: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), varFlags: (readonly: 1, writeonly: 0)))) -; CHECK: ^14 = gv: (guid: 13, summaries: (variable: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 1, canAutoHide: 0), varFlags: (readonly: 0, writeonly: 0)))) +; CHECK: ^11 = gv: (guid: 10, summaries: (variable: (module: ^0, flags: (linkage: common, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), varFlags: (readonly: 0, writeonly: 0, constant: 0)))) +; CHECK: ^12 = gv: (guid: 11, summaries: (variable: (module: ^0, flags: (linkage: appending, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), varFlags: (readonly: 0, writeonly: 0, constant: 0), refs: (^4)))) +; CHECK: ^13 = gv: (guid: 12, summaries: (variable: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), varFlags: (readonly: 1, writeonly: 0, constant: 0)))) +; CHECK: ^14 = gv: (guid: 13, summaries: (variable: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 1, canAutoHide: 0), varFlags: (readonly: 0, writeonly: 0, constant: 0)))) ; CHECK: ^15 = gv: (guid: 14, summaries: (function: (module: ^1, flags: (linkage: external, notEligibleToImport: 1, live: 1, dsoLocal: 0, canAutoHide: 0), insts: 1, funcFlags: (readNone: 0, readOnly: 0, noRecurse: 0, returnDoesNotAlias: 0, noInline: 1, alwaysInline: 0)))) ; CHECK: ^16 = gv: (guid: 15, summaries: (function: (module: ^1, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), insts: 1, funcFlags: (readNone: 1, readOnly: 0, noRecurse: 1, returnDoesNotAlias: 0, noInline: 0, alwaysInline: 1)))) ; CHECK: ^17 = gv: (guid: 16, summaries: (function: (module: ^1, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), insts: 1, funcFlags: (readNone: 0, readOnly: 1, noRecurse: 0, returnDoesNotAlias: 1, noInline: 0, alwaysInline: 0), calls: ((callee: ^15))))) Index: llvm/test/Assembler/thinlto-vtable-summary.ll =================================================================== --- llvm/test/Assembler/thinlto-vtable-summary.ll +++ llvm/test/Assembler/thinlto-vtable-summary.ll @@ -29,9 +29,9 @@ ^0 = module: (path: "", hash: (0, 0, 0, 0, 0)) ^1 = gv: (name: "_ZN1A1nEi") ; guid = 1621563287929432257 -^2 = gv: (name: "_ZTV1B", summaries: (variable: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), varFlags: (readonly: 0, writeonly: 0), vTableFuncs: ((virtFunc: ^3, offset: 16), (virtFunc: ^1, offset: 24)), refs: (^3, ^1)))) ; guid = 5283576821522790367 +^2 = gv: (name: "_ZTV1B", summaries: (variable: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), varFlags: (readonly: 0, writeonly: 0, constant: 0), vTableFuncs: ((virtFunc: ^3, offset: 16), (virtFunc: ^1, offset: 24)), refs: (^3, ^1)))) ; guid = 5283576821522790367 ^3 = gv: (name: "_ZN1B1fEi") ; guid = 7162046368816414394 -^4 = gv: (name: "_ZTV1C", summaries: (variable: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), varFlags: (readonly: 0, writeonly: 0), vTableFuncs: ((virtFunc: ^5, offset: 16), (virtFunc: ^1, offset: 24)), refs: (^1, ^5)))) ; guid = 13624023785555846296 +^4 = gv: (name: "_ZTV1C", summaries: (variable: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), varFlags: (readonly: 0, writeonly: 0, constant: 0), vTableFuncs: ((virtFunc: ^5, offset: 16), (virtFunc: ^1, offset: 24)), refs: (^1, ^5)))) ; guid = 13624023785555846296 ^5 = gv: (name: "_ZN1C1fEi") ; guid = 14876272565662207556 ^6 = typeidCompatibleVTable: (name: "_ZTS1A", summary: ((offset: 16, ^2), (offset: 16, ^4))) ; guid = 7004155349499253778 ^7 = typeidCompatibleVTable: (name: "_ZTS1B", summary: ((offset: 16, ^2))) ; guid = 6203814149063363976 Index: llvm/test/Bitcode/thinlto-function-summary-refgraph.ll =================================================================== --- llvm/test/Bitcode/thinlto-function-summary-refgraph.ll +++ llvm/test/Bitcode/thinlto-function-summary-refgraph.ll @@ -73,7 +73,7 @@ @bar = global void (...)* bitcast (void ()* @func to void (...)*), align 8 -@globalvar = global i32 0, align 4 +@globalvar = constant i32 0, align 4 declare void @func() #0 declare i32 @func2(...) #1 @@ -154,11 +154,12 @@ ; 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, canAutoHide: 0), varFlags: (readonly: 1, writeonly: 1)))) ; guid = 12887606300320728018 +; Check that default value of writeonly attribute is zero for constant variables +; DIS-DAG: = gv: (name: "globalvar", summaries: (variable: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), varFlags: (readonly: 1, writeonly: 0, constant: 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, canAutoHide: 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, canAutoHide: 0), varFlags: (readonly: 1, writeonly: 1), refs: (^{{.*}})))) ; guid = 16434608426314478903 +; DIS-DAG: = gv: (name: "bar", summaries: (variable: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), varFlags: (readonly: 1, writeonly: 1, constant: 0), 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, canAutoHide: 0), insts: 14, calls: ((callee: ^{{.*}}))))) ; guid = Index: llvm/test/ThinLTO/X86/Inputs/dot-dumper.ll =================================================================== --- llvm/test/ThinLTO/X86/Inputs/dot-dumper.ll +++ llvm/test/ThinLTO/X86/Inputs/dot-dumper.ll @@ -2,7 +2,7 @@ target triple = "x86_64-unknown-linux-gnu" @A = local_unnamed_addr global i32 10, align 4 -@B = local_unnamed_addr global i32 20, align 4 +@B = local_unnamed_addr constant i32 20, align 4 ; Function Attrs: norecurse nounwind readonly uwtable define i32 @foo() local_unnamed_addr #0 { Index: llvm/test/ThinLTO/X86/Inputs/import-constant.ll =================================================================== --- /dev/null +++ llvm/test/ThinLTO/X86/Inputs/import-constant.ll @@ -0,0 +1,15 @@ +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +%struct.S = type { i32, i32, i32* } +%struct.Q = type { %struct.S* } + +@val = dso_local global i32 42, align 4 +@_ZL3Obj = internal constant %struct.S { i32 4, i32 8, i32* @val }, align 8 +@outer = dso_local local_unnamed_addr global %struct.Q { %struct.S* @_ZL3Obj }, align 8 + +define dso_local nonnull %struct.S* @_Z6getObjv() local_unnamed_addr { +entry: + store %struct.S* null, %struct.S** getelementptr inbounds (%struct.Q, %struct.Q* @outer, i64 0, i32 0), align 8 + ret %struct.S* @_ZL3Obj +} Index: llvm/test/ThinLTO/X86/Inputs/import-ro-constant-bar.ll =================================================================== --- /dev/null +++ llvm/test/ThinLTO/X86/Inputs/import-ro-constant-bar.ll @@ -0,0 +1,10 @@ +source_filename = "bar.c" +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +@foo = external dso_local local_unnamed_addr constant i32, align 4 +define dso_local i32 @_Z3barv() local_unnamed_addr { +entry: + %0 = load i32, i32* @foo, align 4 + ret i32 %0 +} Index: llvm/test/ThinLTO/X86/Inputs/import-ro-constant-foo.ll =================================================================== --- /dev/null +++ llvm/test/ThinLTO/X86/Inputs/import-ro-constant-foo.ll @@ -0,0 +1,5 @@ +source_filename = "foo.c" +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +@foo = dso_local local_unnamed_addr constant i32 21, align 4 Index: llvm/test/ThinLTO/X86/dot-dumper.ll =================================================================== --- llvm/test/ThinLTO/X86/dot-dumper.ll +++ llvm/test/ThinLTO/X86/dot-dumper.ll @@ -52,7 +52,7 @@ ; COMBINED-NEXT: node [style=filled,fillcolor=lightblue]; ; COMBINED-NEXT: M1_[[FOO:[0-9]+]] [shape="record",label="foo|extern (inst: 4, ffl: 000010)}"]; // function ; COMBINED-NEXT: M1_[[A:[0-9]+]] [shape="Mrecord",label="A|extern}"]; // variable, immutable -; COMBINED-NEXT: M1_[[B:[0-9]+]] [shape="Mrecord",label="B|extern}"]; // variable, immutable +; COMBINED-NEXT: M1_[[B:[0-9]+]] [shape="Mrecord",label="B|extern}"]; // variable, immutable, constant ; COMBINED-NEXT: M1_{{[0-9]+}} [shape="record",label="bar|extern (inst: 1, ffl: 000000)}",fillcolor="red"]; // function, dead ; COMBINED-NEXT: // Edges: ; COMBINED-NEXT: M1_[[FOO]] -> M1_[[B]] [style=dashed,color=forestgreen]; // const-ref Index: llvm/test/ThinLTO/X86/import-constant.ll =================================================================== --- /dev/null +++ llvm/test/ThinLTO/X86/import-constant.ll @@ -0,0 +1,47 @@ +; Check that we promote constant object in the source module and import it +; even when it is referenced in some other GV initializer and/or is used +; by store instructions. +; RUN: opt -thinlto-bc %s -o %t1.bc +; RUN: opt -thinlto-bc %p/Inputs/import-constant.ll -o %t2.bc +; RUN: llvm-lto2 run -save-temps %t1.bc %t2.bc -o %t-out \ +; RUN: -r=%t1.bc,main,plx \ +; RUN: -r=%t1.bc,_Z6getObjv,l \ +; RUN: -r=%t2.bc,_Z6getObjv,pl \ +; RUN: -r=%t2.bc,val,pl \ +; RUN: -r=%t2.bc,outer,pl +; RUN: llvm-dis %t-out.2.1.promote.bc -o - | FileCheck %s --check-prefix=PROMOTE +; RUN: llvm-dis %t-out.1.3.import.bc -o - | FileCheck %s --check-prefix=IMPORT +; RUN: llvm-dis %t-out.1.4.opt.bc -o - | FileCheck %s --check-prefix=OPT + +; Check that variable has been promoted in the source module +; PROMOTE: @_ZL3Obj.llvm.{{.*}} = hidden constant %struct.S { i32 4, i32 8, i32* @val } + +; @outer is a write-only variable, so it's been converted to zeroinitializer. +; IMPORT: @outer = internal local_unnamed_addr global %struct.Q zeroinitializer +; IMPORT-NEXT: @_ZL3Obj.llvm.{{.*}} = available_externally hidden constant %struct.S { i32 4, i32 8, i32* @val } +; IMPORT-NEXT: @val = external dso_local global i32 + +; OPT: @outer = internal unnamed_addr global %struct.Q zeroinitializer + +; OPT: define dso_local i32 @main() +; OPT-NEXT: entry: +; OPT-NEXT: store %struct.S* null, %struct.S** getelementptr inbounds (%struct.Q, %struct.Q* @outer, i64 0, i32 0) +; OPT-NEXT: ret i32 12 + +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +%struct.S = type { i32, i32, i32* } + +define dso_local i32 @main() local_unnamed_addr { +entry: + %call = tail call %struct.S* @_Z6getObjv() + %d = getelementptr inbounds %struct.S, %struct.S* %call, i64 0, i32 0 + %0 = load i32, i32* %d, align 8 + %v = getelementptr inbounds %struct.S, %struct.S* %call, i64 0, i32 1 + %1 = load i32, i32* %v, align 4 + %add = add nsw i32 %1, %0 + ret i32 %add +} + +declare dso_local %struct.S* @_Z6getObjv() local_unnamed_addr Index: llvm/test/ThinLTO/X86/import-ro-constant.ll =================================================================== --- /dev/null +++ llvm/test/ThinLTO/X86/import-ro-constant.ll @@ -0,0 +1,34 @@ +; Check that we internalize external constant if it is accessed +; purely by non-volatile loads. +; RUN: opt -thinlto-bc %s -o %t-main +; RUN: opt -thinlto-bc %p/Inputs/import-ro-constant-foo.ll -o %t-foo +; RUN: opt -thinlto-bc %p/Inputs/import-ro-constant-bar.ll -o %t-bar +; RUN: llvm-lto2 run -save-temps -o %t-out %t-main %t-foo %t-bar \ +; RUN: -r=%t-foo,foo,pl \ +; RUN: -r=%t-main,main,plx \ +; RUN: -r=%t-main,_Z3barv,l \ +; RUN: -r=%t-main,foo, \ +; RUN: -r=%t-bar,_Z3barv,pl \ +; RUN: -r=%t-bar,foo, +; RUN: llvm-dis %t-out.1.3.import.bc -o - | FileCheck %s --check-prefix=IMPORT +; RUN: llvm-dis %t-out.1.4.opt.bc -o - | FileCheck %s --check-prefix=OPT + +; IMPORT: @foo = internal local_unnamed_addr constant i32 21, align 4 #0 +; IMPORT: attributes #0 = { "thinlto-internalize" } +; OPT: i32 @main() +; OPT-NEXT: entry: +; OPT-NEXT: ret i32 42 + +source_filename = "main.c" +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +@foo = external dso_local local_unnamed_addr constant i32, align 4 +define dso_local i32 @main() local_unnamed_addr { +entry: + %0 = load i32, i32* @foo, align 4 + %call = tail call i32 @_Z3barv() + %add = add nsw i32 %call, %0 + ret i32 %add +} +declare dso_local i32 @_Z3barv() local_unnamed_addr Index: llvm/test/ThinLTO/X86/load-store-caching.ll =================================================================== --- llvm/test/ThinLTO/X86/load-store-caching.ll +++ llvm/test/ThinLTO/X86/load-store-caching.ll @@ -22,5 +22,5 @@ } ; CHECK: ^0 = module: -; CHECK-NEXT: ^1 = gv: (name: "obj", summaries: (variable: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 1, canAutoHide: 0), varFlags: (readonly: 1, writeonly: 1)))) ; guid = +; CHECK-NEXT: ^1 = gv: (name: "obj", summaries: (variable: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 1, canAutoHide: 0), varFlags: (readonly: 1, writeonly: 1, constant: 0)))) ; guid = ; CHECK-NEXT: ^2 = gv: (name: "foo", summaries: (function: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 1, canAutoHide: 0), insts: 3, refs: (^1)))) ; guid = Index: llvm/test/ThinLTO/X86/referenced_by_constant.ll =================================================================== --- llvm/test/ThinLTO/X86/referenced_by_constant.ll +++ llvm/test/ThinLTO/X86/referenced_by_constant.ll @@ -3,14 +3,13 @@ ; RUN: opt -module-summary %p/Inputs/referenced_by_constant.ll -o %t2.bc ; RUN: llvm-lto -thinlto-action=thinlink -o %t3.bc %t.bc %t2.bc -; Check the import side: we currently only import bar() (with a future -; enhancement to identify constants in the summary, we should mark -; @someglobal/@someglobal2 for import as a local copy, which would -; cause @referencedbyglobal and @localreferencedbyglobal to be exported -; and promoted). +; Check the import side: we currently import bar() and also promote/import +; referenced constant objects. There is stll a room for improvement: we +; can make a local copy of someglobal and someglobal2 because they are both +; 'unnamed_addr' constants. This should eventually be done as well. ; RUN: llvm-lto -thinlto-action=import %t.bc -thinlto-index=%t3.bc -o - | llvm-dis -o - | FileCheck %s --check-prefix=IMPORT -; IMPORT: @someglobal.llvm.0 = external hidden unnamed_addr constant -; IMPORT: @someglobal2.llvm.0 = external hidden unnamed_addr constant +; IMPORT: @someglobal.llvm.0 = available_externally hidden unnamed_addr constant i8* bitcast (void ()* @referencedbyglobal to i8*) +; IMPORT: @someglobal2.llvm.0 = available_externally hidden unnamed_addr constant i8* bitcast (void ()* @localreferencedbyglobal.llvm.0 to i8*) ; IMPORT: define available_externally void @bar() ; Check the export side: we currently only export bar(), which causes @@ -19,7 +18,7 @@ ; EXPORT: @someglobal.llvm.0 = hidden unnamed_addr constant ; EXPORT: @someglobal2.llvm.0 = hidden unnamed_addr constant ; EXPORT: define void @referencedbyglobal() -; EXPORT: define internal void @localreferencedbyglobal() +; EXPORT: define hidden void @localreferencedbyglobal.llvm target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-apple-macosx10.11.0"