Skip to content

Commit 7f92d66

Browse files
committed
[ThinLTO] Fix bug when importing writeonly variables
Patch enables import of write-only variables with non-trivial initializers to fix linker errors. Initializers of imported variables are converted to 'zeroinitializer' to avoid promotion of referenced objects. Differential revision: https://reviews.llvm.org/D70006
1 parent caad217 commit 7f92d66

File tree

5 files changed

+80
-8
lines changed

5 files changed

+80
-8
lines changed

llvm/lib/IR/ModuleSummaryIndex.cpp

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,21 @@ void ModuleSummaryIndex::propagateAttributes(
197197
bool ModuleSummaryIndex::canImportGlobalVar(GlobalValueSummary *S,
198198
bool AnalyzeRefs) const {
199199
auto HasRefsPreventingImport = [this](const GlobalVarSummary *GVS) {
200-
return !isReadOnly(GVS) && GVS->refs().size();
200+
// We don't analyze GV references during attribute propagation, so
201+
// GV with non-trivial initializer can be marked either read or
202+
// write-only.
203+
// Importing definiton of readonly GV with non-trivial initializer
204+
// allows us doing some extra optimizations (like converting indirect
205+
// calls to direct).
206+
// Definition of writeonly GV with non-trivial initializer should also
207+
// be imported. Not doing so will result in:
208+
// a) GV internalization in source module (because it's writeonly)
209+
// b) Importing of GV declaration to destination module as a result
210+
// of promotion.
211+
// c) Link error (external declaration with internal definition).
212+
// However we do not promote objects referenced by writeonly GV
213+
// initializer by means of converting it to 'zeroinitializer'
214+
return !isReadOnly(GVS) && !isWriteOnly(GVS) && GVS->refs().size();
201215
};
202216
auto *GVS = cast<GlobalVarSummary>(S->getBaseObject());
203217

llvm/lib/Transforms/IPO/FunctionImport.cpp

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -318,10 +318,14 @@ static void computeImportForReferencedGlobals(
318318
if (ILI.second)
319319
NumImportedGlobalVarsThinLink++;
320320
MarkExported(VI, RefSummary.get());
321-
// Promote referenced functions and variables
322-
for (const auto &VI : RefSummary->refs())
323-
for (const auto &RefFn : VI.getSummaryList())
324-
MarkExported(VI, RefFn.get());
321+
// Promote referenced functions and variables. We don't promote
322+
// objects referenced by writeonly variable initializer, because
323+
// we convert such variables initializers to "zeroinitializer".
324+
// See processGlobalForThinLTO.
325+
if (!Index.isWriteOnly(cast<GlobalVarSummary>(RefSummary.get())))
326+
for (const auto &VI : RefSummary->refs())
327+
for (const auto &RefFn : VI.getSummaryList())
328+
MarkExported(VI, RefFn.get());
325329
break;
326330
}
327331
}

llvm/lib/Transforms/Utils/FunctionImportUtils.cpp

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
//===----------------------------------------------------------------------===//
1313

1414
#include "llvm/Transforms/Utils/FunctionImportUtils.h"
15+
#include "llvm/IR/Constants.h"
1516
#include "llvm/IR/InstIterator.h"
1617
using namespace llvm;
1718

@@ -250,10 +251,20 @@ void FunctionImportGlobalProcessing::processGlobalForThinLTO(GlobalValue &GV) {
250251
// We might have a non-null VI and get here even in that case if the name
251252
// matches one in this module (e.g. weak or appending linkage).
252253
auto* GVS = dyn_cast_or_null<GlobalVarSummary>(
253-
ImportIndex.findSummaryInModule(VI, M.getModuleIdentifier()));
254-
// At this stage "maybe" is "definitely"
255-
if (GVS && (ImportIndex.isReadOnly(GVS) || ImportIndex.isWriteOnly(GVS)))
254+
ImportIndex.findSummaryInModule(VI, M.getModuleIdentifier()));
255+
if (GVS &&
256+
(ImportIndex.isReadOnly(GVS) || ImportIndex.isWriteOnly(GVS))) {
256257
V->addAttribute("thinlto-internalize");
258+
// Objects referenced by writeonly GV initializer should not be
259+
// promoted, because there is no any kind of read access to them
260+
// on behalf of this writeonly GV. To avoid promotion we convert
261+
// GV initializer to 'zeroinitializer'. This effectively drops
262+
// references in IR module (not in combined index), so we can
263+
// ignore them when computing import. We do not export references
264+
// of writeonly object. See computeImportForReferencedGlobals
265+
if (ImportIndex.isWriteOnly(GVS) && GVS->refs().size())
266+
V->setInitializer(Constant::getNullValue(V->getValueType()));
267+
}
257268
}
258269
}
259270

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
; ModuleID = 'foo.o'
2+
source_filename = "foo.cpp"
3+
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
4+
target triple = "x86_64-unknown-linux-gnu"
5+
6+
%struct.S = type { i32 }
7+
%struct.Q = type { %struct.S* }
8+
9+
@_ZL3Obj = internal constant %struct.S { i32 42 }, align 4
10+
@outer = dso_local local_unnamed_addr global %struct.Q { %struct.S* @_ZL3Obj }, align 8
11+
12+
; Function Attrs: nofree norecurse nounwind uwtable writeonly
13+
define dso_local void @_Z3foov() local_unnamed_addr {
14+
entry:
15+
store %struct.S* null, %struct.S** getelementptr inbounds (%struct.Q, %struct.Q* @outer, i64 0, i32 0), align 8
16+
ret void
17+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
; RUN: opt -thinlto-bc %s -o %t1
2+
; RUN: opt -thinlto-bc %p/Inputs/writeonly-with-refs.ll -o %t2
3+
; RUN: llvm-lto2 run -save-temps %t1 %t2 -o %t-out \
4+
; RUN: -r=%t1,main,plx \
5+
; RUN: -r=%t1,_Z3foov,l \
6+
; RUN: -r=%t2,_Z3foov,pl \
7+
; RUN: -r=%t2,outer,pl
8+
9+
; @outer should have been internalized and converted to zeroinitilizer.
10+
; RUN: llvm-dis %t-out.1.5.precodegen.bc -o - | FileCheck %s
11+
; RUN: llvm-dis %t-out.2.5.precodegen.bc -o - | FileCheck %s
12+
13+
; CHECK: @outer = internal unnamed_addr global %struct.Q zeroinitializer
14+
15+
source_filename = "main.cpp"
16+
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
17+
target triple = "x86_64-unknown-linux-gnu"
18+
19+
; Function Attrs: norecurse uwtable
20+
define dso_local i32 @main() local_unnamed_addr {
21+
entry:
22+
tail call void @_Z3foov()
23+
ret i32 0
24+
}
25+
26+
declare dso_local void @_Z3foov() local_unnamed_addr

0 commit comments

Comments
 (0)