Skip to content

Commit b2304c5

Browse files
authored
codegen,gc-lowering: post fixup tbaa information (#32321)
Certain metadata (invariant.load, tbaa_const) allows reordering of memory operations over unknown calls (including safepoint). Before GC lowering this is legal, because the optimizer sinking a load over a safepoint will automatically extend the live range of the heap object (by definition the live range of the heap object before GC lowering matches the live ranges of any reference to it). However, after GC lowering, we have fixed the live ranges of the heap objects, and thus it is no longer legal to sink such memory operations over safepoints (as the update to the object's live range won't be tracked). fix #32215
1 parent 2b08860 commit b2304c5

File tree

2 files changed

+123
-24
lines changed

2 files changed

+123
-24
lines changed

src/llvm-late-gc-lowering.cpp

Lines changed: 79 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -870,26 +870,35 @@ JL_USED_FUNC static void dumpLivenessState(Function &F, State &S) {
870870
}
871871
}
872872

873-
// Check if this is a load from an immutable value. The easiest
874-
// way to do so is to look at the tbaa and see if it derives from
875-
// jtbaa_immut.
876-
static bool isLoadFromImmut(LoadInst *LI)
873+
static bool isTBAA(MDNode *TBAA, std::initializer_list<const char*> const strset)
877874
{
878-
if (LI->getMetadata(LLVMContext::MD_invariant_load))
879-
return true;
880-
MDNode *TBAA = LI->getMetadata(LLVMContext::MD_tbaa);
881875
if (!TBAA)
882876
return false;
883877
while (TBAA->getNumOperands() > 1) {
884878
TBAA = cast<MDNode>(TBAA->getOperand(1).get());
885879
auto str = cast<MDString>(TBAA->getOperand(0))->getString();
886-
if (str == "jtbaa_immut" || str == "jtbaa_const") {
887-
return true;
880+
for (auto str2 : strset) {
881+
if (str == str2) {
882+
return true;
883+
}
888884
}
889885
}
890886
return false;
891887
}
892888

889+
// Check if this is a load from an immutable value. The easiest
890+
// way to do so is to look at the tbaa and see if it derives from
891+
// jtbaa_immut.
892+
static bool isLoadFromImmut(LoadInst *LI)
893+
{
894+
if (LI->getMetadata(LLVMContext::MD_invariant_load))
895+
return true;
896+
MDNode *TBAA = LI->getMetadata(LLVMContext::MD_tbaa);
897+
if (isTBAA(TBAA, {"jtbaa_immut", "jtbaa_const"}))
898+
return true;
899+
return false;
900+
}
901+
893902
// Check if this is a load from an constant global.
894903
static bool isLoadFromConstGV(LoadInst *LI)
895904
{
@@ -898,14 +907,8 @@ static bool isLoadFromConstGV(LoadInst *LI)
898907
if (!isa<GlobalVariable>(LI->getPointerOperand()->stripInBoundsOffsets()))
899908
return false;
900909
MDNode *TBAA = LI->getMetadata(LLVMContext::MD_tbaa);
901-
if (!TBAA)
902-
return false;
903-
while (TBAA->getNumOperands() > 1) {
904-
TBAA = cast<MDNode>(TBAA->getOperand(1).get());
905-
if (cast<MDString>(TBAA->getOperand(0))->getString() == "jtbaa_const") {
906-
return true;
907-
}
908-
}
910+
if (isTBAA(TBAA, {"jtbaa_const"}))
911+
return true;
909912
return false;
910913
}
911914

@@ -1650,6 +1653,40 @@ static inline void UpdatePtrNumbering(Value *From, Value *To, State *S)
16501653
}
16511654
}
16521655

1656+
#if JL_LLVM_VERSION < 80000
1657+
MDNode *createMutableTBAAAccessTag(MDNode *Tag) {
1658+
MDNode *BaseType = cast<MDNode>(Tag->getOperand(0));
1659+
MDNode *AccessType = cast<MDNode>(Tag->getOperand(1));
1660+
Metadata *OffsetNode = Tag->getOperand(2);
1661+
uint64_t Offset = mdconst::extract<ConstantInt>(OffsetNode)->getZExtValue();
1662+
1663+
bool NewFormat = isa<MDNode>(AccessType->getOperand(0));
1664+
1665+
// See if the tag is already mutable.
1666+
unsigned ImmutabilityFlagOp = NewFormat ? 4 : 3;
1667+
if (Tag->getNumOperands() <= ImmutabilityFlagOp)
1668+
return Tag;
1669+
1670+
// If Tag is already mutable then return it.
1671+
Metadata *ImmutabilityFlagNode = Tag->getOperand(ImmutabilityFlagOp);
1672+
if (!mdconst::extract<ConstantInt>(ImmutabilityFlagNode)->getValue())
1673+
return Tag;
1674+
1675+
// Otherwise, create another node.
1676+
if (!NewFormat)
1677+
return MDBuilder(Tag->getContext()).createTBAAStructTagNode(BaseType, AccessType, Offset);
1678+
1679+
Metadata *SizeNode = Tag->getOperand(3);
1680+
uint64_t Size = mdconst::extract<ConstantInt>(SizeNode)->getZExtValue();
1681+
return MDBuilder(Tag->getContext()).createTBAAAccessTag(BaseType, AccessType, Offset, Size);
1682+
}
1683+
#else
1684+
MDNode *createMutableTBAAAccessTag(MDNode *Tag) {
1685+
return MDBuilder(Tag->getContext()).createMutableTBAAAccessTag(TBAA);
1686+
}
1687+
#endif
1688+
1689+
16531690
bool LateLowerGCFrame::CleanupIR(Function &F, State *S) {
16541691
bool ChangesMade = false;
16551692
// We create one alloca for all the jlcall frames that haven't been processed
@@ -1667,6 +1704,24 @@ bool LateLowerGCFrame::CleanupIR(Function &F, State *S) {
16671704
SmallVector<CallInst*, 16> write_barriers;
16681705
for (BasicBlock &BB : F) {
16691706
for (auto it = BB.begin(); it != BB.end();) {
1707+
Instruction *I = &*it;
1708+
if (isa<LoadInst>(I) || isa<StoreInst>(I)) {
1709+
// strip all constant alias information, as it might depend on the gc having
1710+
// preserved a gc root, which stops being true after this pass (#32215)
1711+
// we'd like to call RewriteStatepointsForGC::stripNonValidData here, but
1712+
// that function asserts that the GC strategy must be named either "statepoint-example" or "coreclr",
1713+
// while we don't give a name to our GC in the IR, and C++ scope rules prohibit us from using it,
1714+
// so instead we reimplement it here badly
1715+
if (I->getMetadata(LLVMContext::MD_invariant_load))
1716+
I->setMetadata(LLVMContext::MD_invariant_load, NULL);
1717+
if (MDNode *TBAA = I->getMetadata(LLVMContext::MD_tbaa)) {
1718+
if (TBAA->getNumOperands() == 4 && isTBAA(TBAA, {"jtbaa_const"})) {
1719+
MDNode *MutableTBAA = createMutableTBAAAccessTag(TBAA);
1720+
if (MutableTBAA != TBAA)
1721+
I->setMetadata(LLVMContext::MD_tbaa, MutableTBAA);
1722+
}
1723+
}
1724+
}
16701725
auto *CI = dyn_cast<CallInst>(&*it);
16711726
if (!CI) {
16721727
++it;
@@ -1745,18 +1800,18 @@ bool LateLowerGCFrame::CleanupIR(Function &F, State *S) {
17451800
else if (CC == JLCALL_F2_CC)
17461801
nframeargs -= 2;
17471802
SmallVector<Value*, 4> ReplacementArgs;
1748-
auto it = CI->arg_begin();
1749-
assert(it != CI->arg_end());
1750-
ReplacementArgs.push_back(*(it++));
1803+
auto arg_it = CI->arg_begin();
1804+
assert(arg_it != CI->arg_end());
1805+
ReplacementArgs.push_back(*(arg_it++));
17511806
if (CC != JLCALL_F_CC) {
1752-
assert(it != CI->arg_end());
1753-
ReplacementArgs.push_back(*(it++));
1807+
assert(arg_it != CI->arg_end());
1808+
ReplacementArgs.push_back(*(arg_it++));
17541809
}
17551810
maxframeargs = std::max(maxframeargs, nframeargs);
17561811
int slot = 0;
17571812
IRBuilder<> Builder (CI);
1758-
for (; it != CI->arg_end(); ++it) {
1759-
Builder.CreateStore(*it, Builder.CreateGEP(T_prjlvalue, Frame,
1813+
for (; arg_it != CI->arg_end(); ++arg_it) {
1814+
Builder.CreateStore(*arg_it, Builder.CreateGEP(T_prjlvalue, Frame,
17601815
ConstantInt::get(T_int32, slot++)));
17611816
}
17621817
ReplacementArgs.push_back(nframeargs == 0 ?

test/llvmpasses/late-lower-gc.ll

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,47 @@ top:
4747
; CHECK-NEXT: ret %jl_value_t addrspace(10)* %v
4848
ret %jl_value_t addrspace(10)* %v
4949
}
50+
51+
; Confirm that loadedval instruction does not contain invariant.load metadata
52+
; after the gc placement pass, but still contains the range metadata.
53+
; Since loadedval is marked invariant, passes are allowed to move the use.
54+
; But after the placement pass, must ensure it won't be relocated after our
55+
; last gc-root use
56+
define void @gc_drop_aliasing() {
57+
top:
58+
; CHECK-LABEL: @gc_drop_aliasing
59+
%ptls = call %jl_value_t*** @julia.ptls_states()
60+
%ptls_i8 = bitcast %jl_value_t*** %ptls to i8*
61+
; CHECK: %v = call %jl_value_t addrspace(10)* @julia.gc_alloc_bytes(i8* %ptls_i8, [[SIZE_T:i.[0-9]+]] 8)
62+
; CHECK-NEXT: [[V2:%.*]] = bitcast %jl_value_t addrspace(10)* %v to %jl_value_t addrspace(10)* addrspace(10)*
63+
; CHECK-NEXT: [[V_HEADROOM:%.*]] = getelementptr %jl_value_t addrspace(10)*, %jl_value_t addrspace(10)* addrspace(10)* [[V2]], i64 -1
64+
; CHECK-NEXT: store %jl_value_t addrspace(10)* @tag, %jl_value_t addrspace(10)* addrspace(10)* [[V_HEADROOM]], !tbaa !0
65+
%v = call noalias %jl_value_t addrspace(10)* @julia.gc_alloc_obj(i8* %ptls_i8, i64 8, %jl_value_t addrspace(10)* @tag)
66+
; CHECK-NEXT: %v64 = bitcast %jl_value_t addrspace(10)* %v to i64 addrspace(10)*
67+
%v64 = bitcast %jl_value_t addrspace(10)* %v to i64 addrspace(10)*
68+
; CHECK-NEXT: %loadedval = load i64, i64 addrspace(10)* %v64, align 8, !range !4
69+
%loadedval = load i64, i64 addrspace(10)* %v64, align 8, !range !0, !invariant.load !1
70+
; CHECK-NEXT: store i64 %loadedval, i64 addrspace(10)* %v64, align 8, !noalias !5
71+
store i64 %loadedval, i64 addrspace(10)* %v64, align 8, !noalias !2
72+
; CHECK-NEXT: %lv2 = load i64, i64 addrspace(10)* %v64, align 8, !tbaa !6, !range !4
73+
%lv2 = load i64, i64 addrspace(10)* %v64, align 8, !range !0, !tbaa !4
74+
; CHECK-NEXT: ret void
75+
ret void
76+
}
77+
78+
!0 = !{i64 0, i64 23}
79+
!1 = !{}
80+
!2 = distinct !{!2}
81+
!3 = !{!4, !4, i64 0, i64 1}
82+
!4 = !{!"jtbaa_const", !5}
83+
!5 = !{!"jtbaa"}
84+
85+
; CHECK: !0 = !{!1, !1, i64 0}
86+
; CHECK-NEXT: !1 = !{!"jtbaa_tag", !2, i64 0}
87+
; CHECK-NEXT: !2 = !{!"jtbaa_data", !3, i64 0}
88+
; CHECK-NEXT: !3 = !{!"jtbaa"}
89+
; CHECK-NEXT: !4 = !{i64 0, i64 23}
90+
; CHECK-NEXT: !5 = distinct !{!5}
91+
; CHECK-NEXT: !6 = !{!7, !7, i64 0}
92+
; CHECK-NEXT: !7 = !{!"jtbaa_const", !8}
93+
; CHECK-NEXT: !8 = !{!"jtbaa"}

0 commit comments

Comments
 (0)