Skip to content

Commit 23ec578

Browse files
[Bitcode] materialize Functions early when BlockAddress taken
IRLinker builds a work list of functions to materialize, then moves them from a source module to a destination module one at a time. This is a problem for blockaddress Constants, since they need not refer to the function they are used in; IPSCCP is quite good at sinking these constants deep into other functions when passed as arguments. This would lead to curious errors during LTO: ld.lld: error: Never resolved function from blockaddress ... based on the ordering of function definitions in IR. The problem was that IRLinker would basically do: for function f in worklist: materialize f splice f from source module to destination module in one pass, with Functions being lazily added to the running worklist. This confuses BitcodeReader, which cannot disambiguate whether a blockaddress is referring to a function which has not yet been parsed ("materialized") or is simply empty because its body was spliced out. This causes BitcodeReader to insert Functions into its BasicBlockFwdRefs list incorrectly, as it will never re-materialize an already materialized (but spliced out) function. Because of the possibility that blockaddress Constants may appear in Functions other than the ones they reference, this patch adds a new bitcode function code FUNC_CODE_BLOCKADDR_USERS that is a simple list of Functions that contain BlockAddress Constants that refer back to this Function, rather then the Function they are scoped in. We then materialize those functions when materializing `f` from the example loop above. This might over-materialize Functions should the user of BitcodeReader ultimately decide not to link those Functions, but we can at least now we can avoid this ordering related issue with blockaddresses. Fixes: #52787 Fixes: ClangBuiltLinux/linux#1215 Reviewed By: dexonsmith Differential Revision: https://reviews.llvm.org/D120781
1 parent b18e821 commit 23ec578

File tree

6 files changed

+233
-9
lines changed

6 files changed

+233
-9
lines changed

llvm/include/llvm/Bitcode/LLVMBitCodes.h

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -585,14 +585,15 @@ enum FunctionCodes {
585585
52, // CATCHSWITCH: [num,args...] or [num,args...,bb]
586586
// 53 is unused.
587587
// 54 is unused.
588-
FUNC_CODE_OPERAND_BUNDLE = 55, // OPERAND_BUNDLE: [tag#, value...]
589-
FUNC_CODE_INST_UNOP = 56, // UNOP: [opcode, ty, opval]
590-
FUNC_CODE_INST_CALLBR = 57, // CALLBR: [attr, cc, norm, transfs,
591-
// fnty, fnid, args...]
592-
FUNC_CODE_INST_FREEZE = 58, // FREEZE: [opty, opval]
593-
FUNC_CODE_INST_ATOMICRMW = 59, // ATOMICRMW: [ptrty, ptr, valty, val,
594-
// operation, align, vol,
595-
// ordering, synchscope]
588+
FUNC_CODE_OPERAND_BUNDLE = 55, // OPERAND_BUNDLE: [tag#, value...]
589+
FUNC_CODE_INST_UNOP = 56, // UNOP: [opcode, ty, opval]
590+
FUNC_CODE_INST_CALLBR = 57, // CALLBR: [attr, cc, norm, transfs,
591+
// fnty, fnid, args...]
592+
FUNC_CODE_INST_FREEZE = 58, // FREEZE: [opty, opval]
593+
FUNC_CODE_INST_ATOMICRMW = 59, // ATOMICRMW: [ptrty, ptr, valty, val,
594+
// operation, align, vol,
595+
// ordering, synchscope]
596+
FUNC_CODE_BLOCKADDR_USERS = 60, // BLOCKADDR_USERS: [value...]
596597
};
597598

598599
enum UseListCodes {

llvm/lib/Bitcode/Reader/BitcodeAnalyzer.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,7 @@ static Optional<const char *> GetCodeName(unsigned CodeID, unsigned BlockID,
267267
STRINGIFY_CODE(FUNC_CODE, INST_STOREATOMIC)
268268
STRINGIFY_CODE(FUNC_CODE, INST_CMPXCHG)
269269
STRINGIFY_CODE(FUNC_CODE, INST_CALLBR)
270+
STRINGIFY_CODE(FUNC_CODE, BLOCKADDR_USERS)
270271
}
271272
case bitc::VALUE_SYMTAB_BLOCK_ID:
272273
switch (CodeID) {

llvm/lib/Bitcode/Reader/BitcodeReader.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -558,6 +558,13 @@ class BitcodeReader : public BitcodeReaderBase, public GVMaterializer {
558558
DenseMap<Function *, std::vector<BasicBlock *>> BasicBlockFwdRefs;
559559
std::deque<Function *> BasicBlockFwdRefQueue;
560560

561+
/// These are Functions that contain BlockAddresses which refer a different
562+
/// Function. When parsing the different Function, queue Functions that refer
563+
/// to the different Function. Those Functions must be materialized in order
564+
/// to resolve their BlockAddress constants before the different Function
565+
/// gets moved into another Module.
566+
std::vector<Function *> BackwardRefFunctions;
567+
561568
/// Indicates that we are using a new encoding for instruction operands where
562569
/// most operands in the current FUNCTION_BLOCK are encoded relative to the
563570
/// instruction number, for a more compact encoding. Some instruction
@@ -881,6 +888,11 @@ Error BitcodeReader::materializeForwardReferencedFunctions() {
881888
}
882889
assert(BasicBlockFwdRefs.empty() && "Function missing from queue");
883890

891+
for (Function *F : BackwardRefFunctions)
892+
if (Error Err = materialize(F))
893+
return Err;
894+
BackwardRefFunctions.clear();
895+
884896
// Reset state.
885897
WillMaterializeAllForwardRefs = false;
886898
return Error::success();
@@ -4317,6 +4329,31 @@ Error BitcodeReader::parseFunctionBody(Function *F) {
43174329
continue;
43184330
}
43194331

4332+
case bitc::FUNC_CODE_BLOCKADDR_USERS: // BLOCKADDR_USERS: [vals...]
4333+
// The record should not be emitted if it's an empty list.
4334+
if (Record.empty())
4335+
return error("Invalid record");
4336+
// When we have the RARE case of a BlockAddress Constant that is not
4337+
// scoped to the Function it refers to, we need to conservatively
4338+
// materialize the referred to Function, regardless of whether or not
4339+
// that Function will ultimately be linked, otherwise users of
4340+
// BitcodeReader might start splicing out Function bodies such that we
4341+
// might no longer be able to materialize the BlockAddress since the
4342+
// BasicBlock (and entire body of the Function) the BlockAddress refers
4343+
// to may have been moved. In the case that the user of BitcodeReader
4344+
// decides ultimately not to link the Function body, materializing here
4345+
// could be considered wasteful, but it's better than a deserialization
4346+
// failure as described. This keeps BitcodeReader unaware of complex
4347+
// linkage policy decisions such as those use by LTO, leaving those
4348+
// decisions "one layer up."
4349+
for (uint64_t ValID : Record)
4350+
if (auto *F = dyn_cast<Function>(ValueList[ValID]))
4351+
BackwardRefFunctions.push_back(F);
4352+
else
4353+
return error("Invalid record");
4354+
4355+
continue;
4356+
43204357
case bitc::FUNC_CODE_DEBUG_LOC_AGAIN: // DEBUG_LOC_AGAIN
43214358
// This record indicates that the last instruction is at the same
43224359
// location as the previous instruction with a location.

llvm/lib/Bitcode/Writer/BitcodeWriter.cpp

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "llvm/ADT/None.h"
2020
#include "llvm/ADT/Optional.h"
2121
#include "llvm/ADT/STLExtras.h"
22+
#include "llvm/ADT/SmallPtrSet.h"
2223
#include "llvm/ADT/SmallString.h"
2324
#include "llvm/ADT/SmallVector.h"
2425
#include "llvm/ADT/StringMap.h"
@@ -3359,8 +3360,10 @@ void ModuleBitcodeWriter::writeFunction(
33593360
bool NeedsMetadataAttachment = F.hasMetadata();
33603361

33613362
DILocation *LastDL = nullptr;
3363+
SmallPtrSet<Function *, 4> BlockAddressUsers;
3364+
33623365
// Finally, emit all the instructions, in order.
3363-
for (const BasicBlock &BB : F)
3366+
for (const BasicBlock &BB : F) {
33643367
for (const Instruction &I : BB) {
33653368
writeInstruction(I, InstID, Vals);
33663369

@@ -3392,6 +3395,25 @@ void ModuleBitcodeWriter::writeFunction(
33923395
LastDL = DL;
33933396
}
33943397

3398+
if (BlockAddress *BA = BlockAddress::lookup(&BB)) {
3399+
for (User *U : BA->users()) {
3400+
if (auto *I = dyn_cast<Instruction>(U)) {
3401+
Function *P = I->getParent()->getParent();
3402+
if (P != &F)
3403+
BlockAddressUsers.insert(P);
3404+
}
3405+
}
3406+
}
3407+
}
3408+
3409+
if (!BlockAddressUsers.empty()) {
3410+
SmallVector<uint64_t, 4> Record;
3411+
Record.reserve(BlockAddressUsers.size());
3412+
for (Function *F : BlockAddressUsers)
3413+
Record.push_back(VE.getValueID(F));
3414+
Stream.EmitRecord(bitc::FUNC_CODE_BLOCKADDR_USERS, Record);
3415+
}
3416+
33953417
// Emit names for all the instructions etc.
33963418
if (auto *Symtab = F.getValueSymbolTable())
33973419
writeFunctionLevelValueSymbolTable(*Symtab);
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
; RUN: llvm-as %s -o %t.bc
2+
; RUN: llvm-bcanalyzer -dump %t.bc | FileCheck %s
3+
; RUN: llvm-dis %t.bc
4+
5+
; There's a curious case where blockaddress constants may refer to functions
6+
; outside of the function they're used in. There's a special bitcode function
7+
; code, FUNC_CODE_BLOCKADDR_USERS, used to signify that this is the case.
8+
9+
; The intent of this test is two-fold:
10+
; 1. Ensure we produce BLOCKADDR_USERS bitcode function code on the first fn,
11+
; @repro, since @fun and @fun2 both refer to @repro via blockaddress
12+
; constants.
13+
; 2. Ensure we can round-trip serializing+desearlizing such case.
14+
15+
; CHECK: <FUNCTION_BLOCK
16+
; CHECK: <BLOCKADDR_USERS op0=2 op1=1
17+
; CHECK: <FUNCTION_BLOCK
18+
; CHECK: <FUNCTION_BLOCK
19+
20+
define void @repro() {
21+
br label %label
22+
23+
label:
24+
call void @fun()
25+
ret void
26+
}
27+
28+
define void @fun() noinline {
29+
call void @f(i8* blockaddress(@repro, %label))
30+
ret void
31+
}
32+
33+
define void @fun2() noinline {
34+
call void @f(i8* blockaddress(@repro, %label))
35+
ret void
36+
}
37+
38+
declare void @f(i8*)

llvm/test/Linker/blockaddress.ll

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
; RUN: llvm-as %s -o %t.bc
2+
; RUN: llvm-link %t.bc -S | FileCheck %s
3+
4+
declare void @f(i8*)
5+
6+
; Test that a blockaddress in @y referring to %label in @x can be moved when @y
7+
; appears after @x.
8+
define void @x() {
9+
br label %label
10+
11+
label:
12+
call void @y()
13+
ret void
14+
}
15+
16+
define void @y() {
17+
; CHECK: define void @y() {
18+
; CHECK-NEXT: call void @f(i8* blockaddress(@x, %label))
19+
call void @f(i8* blockaddress(@x, %label))
20+
ret void
21+
}
22+
23+
; Test that a blockaddress in @a referring to %label in @b can be moved when @a
24+
; appears before @b.
25+
define void @a() {
26+
; CHECK: define void @a() {
27+
; CHECK-NEXT: call void @f(i8* blockaddress(@b, %label))
28+
call void @f(i8* blockaddress(@b, %label))
29+
ret void
30+
}
31+
32+
define void @b() {
33+
br label %label
34+
35+
label:
36+
call void @a()
37+
ret void
38+
}
39+
40+
; Test that @c and @d can both have blockaddress Constants that refer to one
41+
; another.
42+
43+
define void @c() {
44+
; CHECK: define void @c() {
45+
; CHECK-NEXT: br label %label
46+
; CHECK-EMPTY:
47+
; CHECK-NEXT: label:
48+
; CHECK-NEXT: call void @f(i8* blockaddress(@d, %label))
49+
br label %label
50+
51+
label:
52+
call void @f(i8* blockaddress(@d, %label))
53+
ret void
54+
}
55+
56+
define void @d() {
57+
; CHECK: define void @d() {
58+
; CHECK-NEXT: br label %label
59+
; CHECK-EMPTY:
60+
; CHECK-NEXT: label:
61+
; CHECK-NEXT: call void @f(i8* blockaddress(@c, %label))
62+
br label %label
63+
64+
label:
65+
call void @f(i8* blockaddress(@c, %label))
66+
ret void
67+
}
68+
69+
; Test that Functions added to IRLinker's Worklist member lazily (linkonce_odr)
70+
; aren't susceptible to the the same issues as @x/@y above.
71+
define void @parsed() {
72+
br label %label
73+
74+
label:
75+
ret void
76+
}
77+
78+
define linkonce_odr void @lazy() {
79+
; CHECK: define linkonce_odr void @lazy() {
80+
; CHECK-NEXT: br label %label
81+
; CHECK-EMPTY:
82+
; CHECK-NEXT: label:
83+
; CHECK-NEXT: call void @f(i8* blockaddress(@parsed, %label))
84+
br label %label
85+
86+
label:
87+
call void @f(i8* blockaddress(@parsed, %label))
88+
ret void
89+
}
90+
91+
define void @parsed2() {
92+
call void @lazy()
93+
ret void
94+
}
95+
96+
; Same test as @lazy, just with one more level of lazy parsed functions.
97+
define void @parsed3() {
98+
br label %label
99+
100+
label:
101+
ret void
102+
}
103+
104+
define linkonce_odr void @lazy1() {
105+
; CHECK: define linkonce_odr void @lazy1() {
106+
; CHECK-NEXT: br label %label
107+
; CHECK-EMPTY:
108+
; CHECK-NEXT: label:
109+
; CHECK-NEXT: call void @f(i8* blockaddress(@parsed3, %label))
110+
br label %label
111+
112+
label:
113+
call void @f(i8* blockaddress(@parsed3, %label))
114+
ret void
115+
}
116+
117+
define linkonce_odr void @lazy2() {
118+
call void @lazy1()
119+
ret void
120+
}
121+
122+
define void @parsed4() {
123+
call void @lazy2()
124+
ret void
125+
}

0 commit comments

Comments
 (0)