Skip to content

Commit

Permalink
[WebAssembly] Fix the order of destructors in the LowerGlobalDtors pass.
Browse files Browse the repository at this point in the history
Fix the LowerGlobalDtors pass to run destructors in the same order as the
regular LLVM destructor lowering -- in reverse order. Adjacent
destructors with the same associated object are grouped, but destructors
are not reordered based on associated objects.

Differential Revision: https://reviews.llvm.org/D70685
  • Loading branch information
sunfishcode committed Mar 26, 2020
1 parent 5db37f3 commit d865437
Show file tree
Hide file tree
Showing 2 changed files with 115 additions and 35 deletions.
30 changes: 24 additions & 6 deletions llvm/lib/Target/WebAssembly/WebAssemblyLowerGlobalDtors.cpp
Expand Up @@ -76,9 +76,13 @@ bool LowerGlobalDtors::runOnModule(Module &M) {
!ETy->getTypeAtIndex(2U)->isPointerTy())
return false; // Not (int, ptr, ptr).

// Collect the contents of @llvm.global_dtors, collated by priority and
// associated symbol.
std::map<uint16_t, MapVector<Constant *, std::vector<Constant *>>> DtorFuncs;
// Collect the contents of @llvm.global_dtors, ordered by priority. Within a
// priority, sequences of destructors with the same associated object are
// recorded so that we can register them as a group.
std::map<
uint16_t,
std::vector<std::pair<Constant *, std::vector<Constant *>>>
> DtorFuncs;
for (Value *O : InitList->operands()) {
auto *CS = dyn_cast<ConstantStruct>(O);
if (!CS)
Expand All @@ -96,7 +100,14 @@ bool LowerGlobalDtors::runOnModule(Module &M) {
Constant *Associated = CS->getOperand(2);
Associated = cast<Constant>(Associated->stripPointerCasts());

DtorFuncs[PriorityValue][Associated].push_back(DtorFunc);
auto &AtThisPriority = DtorFuncs[PriorityValue];
if (AtThisPriority.empty() || AtThisPriority.back().first != Associated) {
std::vector<Constant *> NewList;
NewList.push_back(DtorFunc);
AtThisPriority.push_back(std::make_pair(Associated, NewList));
} else {
AtThisPriority.back().second.push_back(DtorFunc);
}
}
if (DtorFuncs.empty())
return false;
Expand Down Expand Up @@ -131,22 +142,27 @@ bool LowerGlobalDtors::runOnModule(Module &M) {
// first function with __cxa_atexit.
for (auto &PriorityAndMore : DtorFuncs) {
uint16_t Priority = PriorityAndMore.first;
for (auto &AssociatedAndMore : PriorityAndMore.second) {
uint64_t Id = 0;
auto &AtThisPriority = PriorityAndMore.second;
for (auto &AssociatedAndMore : AtThisPriority) {
Constant *Associated = AssociatedAndMore.first;
auto ThisId = Id++;

Function *CallDtors = Function::Create(
AtExitFuncTy, Function::PrivateLinkage,
"call_dtors" +
(Priority != UINT16_MAX ? (Twine(".") + Twine(Priority))
: Twine()) +
(AtThisPriority.size() > 1 ? Twine("$") + Twine(ThisId)
: Twine()) +
(!Associated->isNullValue() ? (Twine(".") + Associated->getName())
: Twine()),
&M);
BasicBlock *BB = BasicBlock::Create(C, "body", CallDtors);
FunctionType *VoidVoid = FunctionType::get(Type::getVoidTy(C),
/*isVarArg=*/false);

for (auto Dtor : AssociatedAndMore.second)
for (auto Dtor : reverse(AssociatedAndMore.second))
CallInst::Create(VoidVoid, Dtor, "", BB);
ReturnInst::Create(C, BB);

Expand All @@ -155,6 +171,8 @@ bool LowerGlobalDtors::runOnModule(Module &M) {
"register_call_dtors" +
(Priority != UINT16_MAX ? (Twine(".") + Twine(Priority))
: Twine()) +
(AtThisPriority.size() > 1 ? Twine("$") + Twine(ThisId)
: Twine()) +
(!Associated->isNullValue() ? (Twine(".") + Associated->getName())
: Twine()),
&M);
Expand Down
120 changes: 91 additions & 29 deletions llvm/test/CodeGen/WebAssembly/lower-global-dtors.ll
Expand Up @@ -13,11 +13,18 @@ declare void @orig_dtor1b()
declare void @orig_dtor1c0()
declare void @orig_dtor1c1a()
declare void @orig_dtor1c1b()
declare void @orig_dtor65536()
declare void @orig_dtor1c2a()
declare void @orig_dtor1c2b()
declare void @orig_dtor1c3()
declare void @orig_dtor1d()
declare void @orig_dtor65535()
declare void @orig_dtor65535c0()
declare void @after_the_null()

@associated1c0 = external global i8
@associated1c1 = external global i8
@associatedc0 = external global i8
@associatedc1 = external global i8
@associatedc2 = global i8 42
@associatedc3 = global i8 84

@llvm.global_ctors = appending global
[1 x { i32, void ()*, i8* }]
Expand All @@ -26,15 +33,20 @@ declare void @after_the_null()
]

@llvm.global_dtors = appending global
[9 x { i32, void ()*, i8* }]
[14 x { i32, void ()*, i8* }]
[
{ i32, void ()*, i8* } { i32 0, void ()* @orig_dtor0, i8* null },
{ i32, void ()*, i8* } { i32 1, void ()* @orig_dtor1a, i8* null },
{ i32, void ()*, i8* } { i32 1, void ()* @orig_dtor1b, i8* null },
{ i32, void ()*, i8* } { i32 1, void ()* @orig_dtor1c0, i8* @associated1c0 },
{ i32, void ()*, i8* } { i32 1, void ()* @orig_dtor1c1a, i8* @associated1c1 },
{ i32, void ()*, i8* } { i32 1, void ()* @orig_dtor1c1b, i8* @associated1c1 },
{ i32, void ()*, i8* } { i32 65535, void ()* @orig_dtor65536, i8* null },
{ i32, void ()*, i8* } { i32 1, void ()* @orig_dtor1c0, i8* @associatedc0 },
{ i32, void ()*, i8* } { i32 1, void ()* @orig_dtor1c1a, i8* @associatedc1 },
{ i32, void ()*, i8* } { i32 1, void ()* @orig_dtor1c1b, i8* @associatedc1 },
{ i32, void ()*, i8* } { i32 1, void ()* @orig_dtor1c2a, i8* @associatedc2 },
{ i32, void ()*, i8* } { i32 1, void ()* @orig_dtor1c2b, i8* @associatedc2 },
{ i32, void ()*, i8* } { i32 1, void ()* @orig_dtor1c3, i8* @associatedc3 },
{ i32, void ()*, i8* } { i32 1, void ()* @orig_dtor1d, i8* null },
{ i32, void ()*, i8* } { i32 65535, void ()* @orig_dtor65535c0, i8* @associatedc0 },
{ i32, void ()*, i8* } { i32 65535, void ()* @orig_dtor65535, i8* null },
{ i32, void ()*, i8* } { i32 65535, void ()* null, i8* null },
{ i32, void ()*, i8* } { i32 65535, void ()* @after_the_null, i8* null }
]
Expand All @@ -54,14 +66,14 @@ declare void @after_the_null()
; CHECK-NEXT: unreachable
; CHECK: end_block

; CHECK-LABEL: .Lcall_dtors.1:
; CHECK-NEXT: .functype .Lcall_dtors.1 (i32) -> (){{$}}
; CHECK-NEXT: call orig_dtor1a{{$}}
; CHECK-LABEL: .Lcall_dtors.1$0:
; CHECK-NEXT: .functype .Lcall_dtors.1$0 (i32) -> (){{$}}
; CHECK-NEXT: call orig_dtor1b{{$}}
; CHECK-NEXT: call orig_dtor1a{{$}}

; CHECK-LABEL: .Lregister_call_dtors.1:
; CHECK-LABEL: .Lregister_call_dtors.1$0:
; CHECK: block
; CHECK-NEXT: i32.const $push2=, .Lcall_dtors.1{{$}}
; CHECK-NEXT: i32.const $push2=, .Lcall_dtors.1$0{{$}}
; CHECK-NEXT: i32.const $push1=, 0
; CHECK-NEXT: i32.const $push0=, __dso_handle
; CHECK-NEXT: call $push3=, __cxa_atexit, $pop2, $pop1, $pop0{{$}}
Expand All @@ -70,42 +82,89 @@ declare void @after_the_null()
; CHECK-NEXT: unreachable
; CHECK: end_block

; CHECK-LABEL: .Lcall_dtors.1.associated1c0:
; CHECK-NEXT: .functype .Lcall_dtors.1.associated1c0 (i32) -> (){{$}}
; CHECK-LABEL: .Lcall_dtors.1$1.associatedc0:
; CHECK-NEXT: .functype .Lcall_dtors.1$1.associatedc0 (i32) -> (){{$}}
; CHECK-NEXT: call orig_dtor1c0{{$}}

; CHECK-LABEL: .Lregister_call_dtors.1.associated1c0:
; CHECK-LABEL: .Lregister_call_dtors.1$1.associatedc0:
; CHECK: block
; CHECK-NEXT: i32.const $push2=, .Lcall_dtors.1.associated1c0{{$}}
; CHECK-NEXT: i32.const $push2=, .Lcall_dtors.1$1.associatedc0{{$}}
; CHECK-NEXT: i32.const $push1=, 0
; CHECK-NEXT: i32.const $push0=, __dso_handle
; CHECK-NEXT: call $push3=, __cxa_atexit, $pop2, $pop1, $pop0{{$}}
; CHECK-NEXT: i32.eqz $push4=, $pop3
; CHECK-NEXT: br_if 0, $pop4
; CHECK-NEXT: unreachable

; CHECK-LABEL: .Lcall_dtors.1.associated1c1:
; CHECK-NEXT: .functype .Lcall_dtors.1.associated1c1 (i32) -> (){{$}}
; CHECK-NEXT: call orig_dtor1c1a{{$}}
; CHECK-LABEL: .Lcall_dtors.1$2.associatedc1:
; CHECK-NEXT: .functype .Lcall_dtors.1$2.associatedc1 (i32) -> (){{$}}
; CHECK-NEXT: call orig_dtor1c1b{{$}}
; CHECK-NEXT: call orig_dtor1c1a{{$}}

; CHECK-LABEL: .Lregister_call_dtors.1$2.associatedc1:
; CHECK: block
; CHECK-NEXT: i32.const $push2=, .Lcall_dtors.1$2.associatedc1{{$}}
; CHECK-NEXT: i32.const $push1=, 0
; CHECK-NEXT: i32.const $push0=, __dso_handle
; CHECK-NEXT: call $push3=, __cxa_atexit, $pop2, $pop1, $pop0{{$}}
; CHECK-NEXT: i32.eqz $push4=, $pop3
; CHECK-NEXT: br_if 0, $pop4
; CHECK-NEXT: unreachable

; CHECK-LABEL: .Lcall_dtors.1$3.associatedc2:
; CHECK-NEXT: .functype .Lcall_dtors.1$3.associatedc2 (i32) -> (){{$}}
; CHECK-NEXT: call orig_dtor1c2b{{$}}
; CHECK-NEXT: call orig_dtor1c2a{{$}}

; CHECK-LABEL: .Lregister_call_dtors.1$3.associatedc2:
; CHECK: block
; CHECK-NEXT: i32.const $push2=, .Lcall_dtors.1$3.associatedc2{{$}}
; CHECK-NEXT: i32.const $push1=, 0
; CHECK-NEXT: i32.const $push0=, __dso_handle
; CHECK-NEXT: call $push3=, __cxa_atexit, $pop2, $pop1, $pop0{{$}}
; CHECK-NEXT: i32.eqz $push4=, $pop3
; CHECK-NEXT: br_if 0, $pop4
; CHECK-NEXT: unreachable

; CHECK-LABEL: .Lcall_dtors.1$4.associatedc3:
; CHECK-NEXT: .functype .Lcall_dtors.1$4.associatedc3 (i32) -> (){{$}}
; CHECK-NEXT: call orig_dtor1c3{{$}}

; CHECK-LABEL: .Lregister_call_dtors.1$4.associatedc3:
; CHECK: block
; CHECK-NEXT: i32.const $push2=, .Lcall_dtors.1$4.associatedc3{{$}}
; CHECK-NEXT: i32.const $push1=, 0
; CHECK-NEXT: i32.const $push0=, __dso_handle
; CHECK-NEXT: call $push3=, __cxa_atexit, $pop2, $pop1, $pop0{{$}}
; CHECK-NEXT: i32.eqz $push4=, $pop3
; CHECK-NEXT: br_if 0, $pop4
; CHECK-NEXT: unreachable

; CHECK-LABEL: .Lregister_call_dtors.1.associated1c1:
; CHECK-LABEL: .Lcall_dtors.1$5:
; CHECK-NEXT: .functype .Lcall_dtors.1$5 (i32) -> (){{$}}
; CHECK-NEXT: call orig_dtor1d{{$}}

; CHECK-LABEL: .Lregister_call_dtors.1$5:
; CHECK: block
; CHECK-NEXT: i32.const $push2=, .Lcall_dtors.1.associated1c1{{$}}
; CHECK-NEXT: i32.const $push2=, .Lcall_dtors.1$5{{$}}
; CHECK-NEXT: i32.const $push1=, 0
; CHECK-NEXT: i32.const $push0=, __dso_handle
; CHECK-NEXT: call $push3=, __cxa_atexit, $pop2, $pop1, $pop0{{$}}
; CHECK-NEXT: i32.eqz $push4=, $pop3
; CHECK-NEXT: br_if 0, $pop4
; CHECK-NEXT: unreachable

; CHECK-LABEL: .Lcall_dtors:
; CHECK-NEXT: .functype .Lcall_dtors (i32) -> (){{$}}
; CHECK-NEXT: call orig_dtor65536{{$}}
; CHECK-LABEL: .Lcall_dtors$0.associatedc0:
; CHECK-NEXT: .functype .Lcall_dtors$0.associatedc0 (i32) -> (){{$}}
; CHECK-NEXT: call orig_dtor65535c0

; CHECK-LABEL: .Lcall_dtors$1:
; CHECK-NEXT: .functype .Lcall_dtors$1 (i32) -> (){{$}}
; CHECK-NEXT: call orig_dtor65535{{$}}

; CHECK-LABEL: .Lregister_call_dtors:
; CHECK-LABEL: .Lregister_call_dtors$1:
; CHECK: block
; CHECK-NEXT: i32.const $push2=, .Lcall_dtors{{$}}
; CHECK-NEXT: i32.const $push2=, .Lcall_dtors$1{{$}}
; CHECK-NEXT: i32.const $push1=, 0
; CHECK-NEXT: i32.const $push0=, __dso_handle
; CHECK-NEXT: call $push3=, __cxa_atexit, $pop2, $pop1, $pop0{{$}}
Expand All @@ -116,11 +175,14 @@ declare void @after_the_null()
; CHECK-LABEL: .section .init_array.0,"",@
; CHECK: .int32 .Lregister_call_dtors.0{{$}}
; CHECK-LABEL: .section .init_array.1,"",@
; CHECK: .int32 .Lregister_call_dtors.1{{$}}
; CHECK: .int32 .Lregister_call_dtors.1$0{{$}}
; CHECK-NEXT: .int32 .Lregister_call_dtors.1$3.associatedc2{{$}}
; CHECK-NEXT: .int32 .Lregister_call_dtors.1$4.associatedc3{{$}}
; CHECK-NEXT: .int32 .Lregister_call_dtors.1$5{{$}}
; CHECK-LABEL: .section .init_array.200,"",@
; CHECK: .int32 orig_ctor{{$}}
; CHECK-LABEL: .section .init_array,"",@
; CHECK: .int32 .Lregister_call_dtors{{$}}
; CHECK: .int32 .Lregister_call_dtors$1{{$}}

; CHECK-LABEL: .weak __dso_handle

Expand Down

0 comments on commit d865437

Please sign in to comment.