Skip to content

Commit bafe690

Browse files
committedDec 15, 2017
[WebAssembly] Implement @llvm.global_ctors and @llvm.global_dtors
Summary: - lowers @llvm.global_dtors by adding @llvm.global_ctors functions which register the destructors with `__cxa_atexit`. - impements @llvm.global_ctors with wasm start functions and linker metadata See [here](WebAssembly/tool-conventions#25) for more background. Subscribers: jfb, dschuff, mgorny, jgravelle-google, aheejin, sunfish Differential Revision: https://reviews.llvm.org/D41211 llvm-svn: 320774
1 parent 476a739 commit bafe690

File tree

9 files changed

+540
-45
lines changed

9 files changed

+540
-45
lines changed
 

‎llvm/include/llvm/CodeGen/TargetLoweringObjectFileImpl.h

+4
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,10 @@ class TargetLoweringObjectFileWasm : public TargetLoweringObjectFile {
182182
const Function &F) const override;
183183

184184
void InitializeWasm();
185+
MCSection *getStaticCtorSection(unsigned Priority,
186+
const MCSymbol *KeySym) const override;
187+
MCSection *getStaticDtorSection(unsigned Priority,
188+
const MCSymbol *KeySym) const override;
185189

186190
const MCExpr *lowerRelativeReference(const GlobalValue *LHS,
187191
const GlobalValue *RHS,

‎llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp

+14-2
Original file line numberDiff line numberDiff line change
@@ -1359,6 +1359,18 @@ const MCExpr *TargetLoweringObjectFileWasm::lowerRelativeReference(
13591359
void TargetLoweringObjectFileWasm::InitializeWasm() {
13601360
StaticCtorSection =
13611361
getContext().getWasmSection(".init_array", SectionKind::getData());
1362-
StaticDtorSection =
1363-
getContext().getWasmSection(".fini_array", SectionKind::getData());
1362+
}
1363+
1364+
MCSection *TargetLoweringObjectFileWasm::getStaticCtorSection(
1365+
unsigned Priority, const MCSymbol *KeySym) const {
1366+
return Priority == UINT16_MAX ?
1367+
StaticCtorSection :
1368+
getContext().getWasmSection(".init_array." + utostr(Priority),
1369+
SectionKind::getData());
1370+
}
1371+
1372+
MCSection *TargetLoweringObjectFileWasm::getStaticDtorSection(
1373+
unsigned Priority, const MCSymbol *KeySym) const {
1374+
llvm_unreachable("@llvm.global_dtors should have been lowered already");
1375+
return nullptr;
13641376
}

‎llvm/lib/MC/WasmObjectWriter.cpp

+75-4
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,8 @@ class WasmObjectWriter : public MCObjectWriter {
284284
void writeDataRelocSection();
285285
void writeLinkingMetaDataSection(
286286
ArrayRef<WasmDataSegment> Segments, uint32_t DataSize,
287-
SmallVector<std::pair<StringRef, uint32_t>, 4> SymbolFlags);
287+
const SmallVector<std::pair<StringRef, uint32_t>, 4> &SymbolFlags,
288+
const SmallVector<std::pair<uint16_t, uint32_t>, 2> &InitFuncs);
288289

289290
uint32_t getProvisionalValue(const WasmRelocationEntry &RelEntry);
290291
void applyRelocations(ArrayRef<WasmRelocationEntry> Relocations,
@@ -366,6 +367,10 @@ void WasmObjectWriter::recordRelocation(MCAssembler &Asm,
366367
uint64_t FixupOffset = Layout.getFragmentOffset(Fragment) + Fixup.getOffset();
367368
MCContext &Ctx = Asm.getContext();
368369

370+
// The .init_array isn't translated as data, so don't do relocations in it.
371+
if (FixupSection.getSectionName().startswith(".init_array"))
372+
return;
373+
369374
if (const MCSymbolRefExpr *RefB = Target.getSymB()) {
370375
assert(RefB->getKind() == MCSymbolRefExpr::VK_None &&
371376
"Should not have constructed this");
@@ -905,7 +910,8 @@ void WasmObjectWriter::writeDataRelocSection() {
905910

906911
void WasmObjectWriter::writeLinkingMetaDataSection(
907912
ArrayRef<WasmDataSegment> Segments, uint32_t DataSize,
908-
SmallVector<std::pair<StringRef, uint32_t>, 4> SymbolFlags) {
913+
const SmallVector<std::pair<StringRef, uint32_t>, 4> &SymbolFlags,
914+
const SmallVector<std::pair<uint16_t, uint32_t>, 2> &InitFuncs) {
909915
SectionBookkeeping Section;
910916
startSection(Section, wasm::WASM_SEC_CUSTOM, "linking");
911917
SectionBookkeeping SubSection;
@@ -937,6 +943,16 @@ void WasmObjectWriter::writeLinkingMetaDataSection(
937943
endSection(SubSection);
938944
}
939945

946+
if (!InitFuncs.empty()) {
947+
startSection(SubSection, wasm::WASM_INIT_FUNCS);
948+
encodeULEB128(InitFuncs.size(), getStream());
949+
for (auto &StartFunc : InitFuncs) {
950+
encodeULEB128(StartFunc.first, getStream()); // priority
951+
encodeULEB128(StartFunc.second, getStream()); // function index
952+
}
953+
endSection(SubSection);
954+
}
955+
940956
endSection(Section);
941957
}
942958

@@ -977,6 +993,7 @@ void WasmObjectWriter::writeObject(MCAssembler &Asm,
977993
SmallVector<WasmImport, 4> Imports;
978994
SmallVector<WasmExport, 4> Exports;
979995
SmallVector<std::pair<StringRef, uint32_t>, 4> SymbolFlags;
996+
SmallVector<std::pair<uint16_t, uint32_t>, 2> InitFuncs;
980997
SmallPtrSet<const MCSymbolWasm *, 4> IsAddressTaken;
981998
unsigned NumFuncImports = 0;
982999
SmallVector<WasmDataSegment, 4> DataSegments;
@@ -1132,6 +1149,10 @@ void WasmObjectWriter::writeObject(MCAssembler &Asm,
11321149
if (!Section.isWasmData())
11331150
continue;
11341151

1152+
// .init_array sections are handled specially elsewhere.
1153+
if (cast<MCSectionWasm>(Sec).getSectionName().startswith(".init_array"))
1154+
continue;
1155+
11351156
DataSize = alignTo(DataSize, Section.getAlignment());
11361157
DataSegments.emplace_back();
11371158
WasmDataSegment &Segment = DataSegments.back();
@@ -1291,6 +1312,56 @@ void WasmObjectWriter::writeObject(MCAssembler &Asm,
12911312
registerFunctionType(*Fixup.Symbol);
12921313
}
12931314

1315+
// Translate .init_array section contents into start functions.
1316+
for (const MCSection &S : Asm) {
1317+
const auto &WS = static_cast<const MCSectionWasm &>(S);
1318+
if (WS.getSectionName().startswith(".fini_array"))
1319+
report_fatal_error(".fini_array sections are unsupported");
1320+
if (!WS.getSectionName().startswith(".init_array"))
1321+
continue;
1322+
if (WS.getFragmentList().empty())
1323+
continue;
1324+
if (WS.getFragmentList().size() != 2)
1325+
report_fatal_error("only one .init_array section fragment supported");
1326+
const MCFragment &AlignFrag = *WS.begin();
1327+
if (AlignFrag.getKind() != MCFragment::FT_Align)
1328+
report_fatal_error(".init_array section should be aligned");
1329+
if (cast<MCAlignFragment>(AlignFrag).getAlignment() != (is64Bit() ? 8 : 4))
1330+
report_fatal_error(".init_array section should be aligned for pointers");
1331+
const MCFragment &Frag = *std::next(WS.begin());
1332+
if (Frag.hasInstructions() || Frag.getKind() != MCFragment::FT_Data)
1333+
report_fatal_error("only data supported in .init_array section");
1334+
uint16_t Priority = UINT16_MAX;
1335+
if (WS.getSectionName().size() != 11) {
1336+
if (WS.getSectionName()[11] != '.')
1337+
report_fatal_error(".init_array section priority should start with '.'");
1338+
if (WS.getSectionName().substr(12).getAsInteger(10, Priority))
1339+
report_fatal_error("invalid .init_array section priority");
1340+
}
1341+
const auto &DataFrag = cast<MCDataFragment>(Frag);
1342+
const SmallVectorImpl<char> &Contents = DataFrag.getContents();
1343+
for (const uint8_t *p = (const uint8_t *)Contents.data(),
1344+
*end = (const uint8_t *)Contents.data() + Contents.size();
1345+
p != end; ++p) {
1346+
if (*p != 0)
1347+
report_fatal_error("non-symbolic data in .init_array section");
1348+
}
1349+
for (const MCFixup &Fixup : DataFrag.getFixups()) {
1350+
assert(Fixup.getKind() == MCFixup::getKindForSize(is64Bit() ? 8 : 4, false));
1351+
const MCExpr *Expr = Fixup.getValue();
1352+
auto *Sym = dyn_cast<MCSymbolRefExpr>(Expr);
1353+
if (!Sym)
1354+
report_fatal_error("fixups in .init_array should be symbol references");
1355+
if (Sym->getKind() != MCSymbolRefExpr::VK_WebAssembly_FUNCTION)
1356+
report_fatal_error("symbols in .init_array should be for functions");
1357+
auto I = SymbolIndices.find(cast<MCSymbolWasm>(&Sym->getSymbol()));
1358+
if (I == SymbolIndices.end())
1359+
report_fatal_error("symbols in .init_array should be defined");
1360+
uint32_t Index = I->second;
1361+
InitFuncs.push_back(std::make_pair(Priority, Index));
1362+
}
1363+
}
1364+
12941365
// Write out the Wasm header.
12951366
writeHeader(Asm);
12961367

@@ -1301,14 +1372,14 @@ void WasmObjectWriter::writeObject(MCAssembler &Asm,
13011372
// Skip the "memory" section; we import the memory instead.
13021373
writeGlobalSection();
13031374
writeExportSection(Exports);
1304-
// TODO: Start Section
13051375
writeElemSection(TableElems);
13061376
writeCodeSection(Asm, Layout, Functions);
13071377
writeDataSection(DataSegments);
13081378
writeNameSection(Functions, Imports, NumFuncImports);
13091379
writeCodeRelocSection();
13101380
writeDataRelocSection();
1311-
writeLinkingMetaDataSection(DataSegments, DataSize, SymbolFlags);
1381+
writeLinkingMetaDataSection(DataSegments, DataSize, SymbolFlags,
1382+
InitFuncs);
13121383

13131384
// TODO: Translate the .comment section to the output.
13141385
// TODO: Translate debug sections to the output.

‎llvm/lib/Target/WebAssembly/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ add_llvm_target(WebAssemblyCodeGen
2525
WebAssemblyInstrInfo.cpp
2626
WebAssemblyLowerBrUnless.cpp
2727
WebAssemblyLowerEmscriptenEHSjLj.cpp
28+
WebAssemblyLowerGlobalDtors.cpp
2829
WebAssemblyMachineFunctionInfo.cpp
2930
WebAssemblyMCInstLower.cpp
3031
WebAssemblyOptimizeLiveIntervals.cpp

‎llvm/lib/Target/WebAssembly/WebAssembly.h

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ class FunctionPass;
2828
// LLVM IR passes.
2929
ModulePass *createWebAssemblyLowerEmscriptenEHSjLj(bool DoEH, bool DoSjLj);
3030
void initializeWebAssemblyLowerEmscriptenEHSjLjPass(PassRegistry &);
31+
ModulePass *createWebAssemblyLowerGlobalDtors();
3132
ModulePass *createWebAssemblyFixFunctionBitcasts();
3233
FunctionPass *createWebAssemblyOptimizeReturned();
3334

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
//===-- WebAssemblyLowerGlobalDtors.cpp - Lower @llvm.global_dtors --------===//
2+
//
3+
// The LLVM Compiler Infrastructure
4+
//
5+
// This file is distributed under the University of Illinois Open Source
6+
// License. See LICENSE.TXT for details.
7+
//
8+
//===----------------------------------------------------------------------===//
9+
///
10+
/// \file
11+
/// \brief Lower @llvm.global_dtors.
12+
///
13+
/// WebAssembly doesn't have a builtin way to invoke static destructors.
14+
/// Implement @llvm.global_dtors by creating wrapper functions that are
15+
/// registered in @llvm.global_ctors and which contain a call to
16+
/// `__cxa_atexit` to register their destructor functions.
17+
///
18+
//===----------------------------------------------------------------------===//
19+
20+
#include "WebAssembly.h"
21+
#include "llvm/IR/Constants.h"
22+
#include "llvm/IR/Instructions.h"
23+
#include "llvm/IR/Intrinsics.h"
24+
#include "llvm/IR/Module.h"
25+
#include "llvm/Transforms/Utils/ModuleUtils.h"
26+
#include "llvm/Pass.h"
27+
#include "llvm/ADT/MapVector.h"
28+
#include "llvm/Support/Debug.h"
29+
#include "llvm/Support/raw_ostream.h"
30+
using namespace llvm;
31+
32+
#define DEBUG_TYPE "wasm-lower-global-dtors"
33+
34+
namespace {
35+
class LowerGlobalDtors final : public ModulePass {
36+
StringRef getPassName() const override {
37+
return "WebAssembly Lower @llvm.global_dtors";
38+
}
39+
40+
void getAnalysisUsage(AnalysisUsage &AU) const override {
41+
AU.setPreservesCFG();
42+
ModulePass::getAnalysisUsage(AU);
43+
}
44+
45+
bool runOnModule(Module &M) override;
46+
47+
public:
48+
static char ID;
49+
LowerGlobalDtors() : ModulePass(ID) {}
50+
};
51+
} // End anonymous namespace
52+
53+
char LowerGlobalDtors::ID = 0;
54+
ModulePass *llvm::createWebAssemblyLowerGlobalDtors() {
55+
return new LowerGlobalDtors();
56+
}
57+
58+
bool LowerGlobalDtors::runOnModule(Module &M) {
59+
GlobalVariable *GV = M.getGlobalVariable("llvm.global_dtors");
60+
if (!GV)
61+
return false;
62+
63+
const ConstantArray *InitList = dyn_cast<ConstantArray>(GV->getInitializer());
64+
if (!InitList)
65+
return false;
66+
67+
// Sanity-check @llvm.global_dtor's type.
68+
StructType *ETy = dyn_cast<StructType>(InitList->getType()->getElementType());
69+
if (!ETy || ETy->getNumElements() != 3 ||
70+
!ETy->getTypeAtIndex(0U)->isIntegerTy() ||
71+
!ETy->getTypeAtIndex(1U)->isPointerTy() ||
72+
!ETy->getTypeAtIndex(2U)->isPointerTy())
73+
return false; // Not (int, ptr, ptr).
74+
75+
// Collect the contents of @llvm.global_dtors, collated by priority and
76+
// associated symbol.
77+
std::map<uint16_t, MapVector<Constant *, std::vector<Constant *> > > DtorFuncs;
78+
for (Value *O : InitList->operands()) {
79+
ConstantStruct *CS = dyn_cast<ConstantStruct>(O);
80+
if (!CS) continue; // Malformed.
81+
82+
ConstantInt *Priority = dyn_cast<ConstantInt>(CS->getOperand(0));
83+
if (!Priority) continue; // Malformed.
84+
uint16_t PriorityValue = Priority->getLimitedValue(UINT16_MAX);
85+
86+
Constant *DtorFunc = CS->getOperand(1);
87+
if (DtorFunc->isNullValue())
88+
break; // Found a null terminator, skip the rest.
89+
90+
Constant *Associated = CS->getOperand(2);
91+
Associated = cast<Constant>(Associated->stripPointerCastsNoFollowAliases());
92+
93+
DtorFuncs[PriorityValue][Associated].push_back(DtorFunc);
94+
}
95+
if (DtorFuncs.empty())
96+
return false;
97+
98+
// extern "C" int __cxa_atexit(void (*f)(void *), void *p, void *d);
99+
LLVMContext &C = M.getContext();
100+
PointerType *VoidStar = Type::getInt8PtrTy(C);
101+
Type *AtExitFuncArgs[] = { VoidStar };
102+
FunctionType *AtExitFuncTy = FunctionType::get(
103+
Type::getVoidTy(C),
104+
AtExitFuncArgs,
105+
/*isVarArg=*/false);
106+
107+
Type *AtExitArgs[] = {
108+
PointerType::get(AtExitFuncTy, 0),
109+
VoidStar,
110+
VoidStar
111+
};
112+
FunctionType *AtExitTy = FunctionType::get(
113+
Type::getInt32Ty(C),
114+
AtExitArgs,
115+
/*isVarArg=*/false);
116+
Constant *AtExit = M.getOrInsertFunction("__cxa_atexit", AtExitTy);
117+
118+
// Declare __dso_local.
119+
Constant *DsoHandle = M.getNamedValue("__dso_handle");
120+
if (!DsoHandle) {
121+
Type *DsoHandleTy = Type::getInt8Ty(C);
122+
GlobalVariable *Handle =
123+
new GlobalVariable(M, DsoHandleTy, /*isConstant=*/true,
124+
GlobalVariable::ExternalWeakLinkage,
125+
nullptr, "__dso_handle");
126+
Handle->setVisibility(GlobalVariable::HiddenVisibility);
127+
DsoHandle = Handle;
128+
}
129+
130+
// For each unique priority level and associated symbol, generate a function
131+
// to call all the destructors at that level, and a function to register the
132+
// first function with __cxa_atexit.
133+
for (auto &PriorityAndMore : DtorFuncs) {
134+
uint16_t Priority = PriorityAndMore.first;
135+
for (auto &AssociatedAndMore : PriorityAndMore.second) {
136+
Constant *Associated = AssociatedAndMore.first;
137+
138+
Function *CallDtors = Function::Create(
139+
AtExitFuncTy, Function::PrivateLinkage,
140+
"call_dtors" +
141+
(Priority != UINT16_MAX ?
142+
(Twine(".") + Twine(Priority)) : Twine()) +
143+
(!Associated->isNullValue() ?
144+
(Twine(".") + Associated->getName()) : Twine()),
145+
&M);
146+
BasicBlock *BB = BasicBlock::Create(C, "body", CallDtors);
147+
148+
for (auto Dtor : AssociatedAndMore.second)
149+
CallInst::Create(Dtor, "", BB);
150+
ReturnInst::Create(C, BB);
151+
152+
FunctionType *VoidVoid = FunctionType::get(Type::getVoidTy(C),
153+
/*isVarArg=*/false);
154+
Function *RegisterCallDtors = Function::Create(
155+
VoidVoid, Function::PrivateLinkage,
156+
"register_call_dtors" +
157+
(Priority != UINT16_MAX ?
158+
(Twine(".") + Twine(Priority)) : Twine()) +
159+
(!Associated->isNullValue() ?
160+
(Twine(".") + Associated->getName()) : Twine()),
161+
&M);
162+
BasicBlock *EntryBB = BasicBlock::Create(C, "entry", RegisterCallDtors);
163+
BasicBlock *FailBB = BasicBlock::Create(C, "fail", RegisterCallDtors);
164+
BasicBlock *RetBB = BasicBlock::Create(C, "return", RegisterCallDtors);
165+
166+
Value *Null = ConstantPointerNull::get(VoidStar);
167+
Value *Args[] = { CallDtors, Null, DsoHandle };
168+
Value *Res = CallInst::Create(AtExit, Args, "call", EntryBB);
169+
Value *Cmp = new ICmpInst(*EntryBB, ICmpInst::ICMP_NE, Res,
170+
Constant::getNullValue(Res->getType()));
171+
BranchInst::Create(FailBB, RetBB, Cmp, EntryBB);
172+
173+
// If `__cxa_atexit` hits out-of-memory, trap, so that we don't misbehave.
174+
// This should be very rare, because if the process is running out of memory
175+
// before main has even started, something is wrong.
176+
CallInst::Create(Intrinsic::getDeclaration(&M, Intrinsic::trap),
177+
"", FailBB);
178+
new UnreachableInst(C, FailBB);
179+
180+
ReturnInst::Create(C, RetBB);
181+
182+
// Now register the registration function with @llvm.global_ctors.
183+
appendToGlobalCtors(M, RegisterCallDtors, Priority, Associated);
184+
}
185+
}
186+
187+
// Now that we've lowered everything, remove @llvm.global_dtors.
188+
GV->eraseFromParent();
189+
190+
return true;
191+
}

‎llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,9 @@ void WebAssemblyPassConfig::addIRPasses() {
175175
// control specifically what gets lowered.
176176
addPass(createAtomicExpandPass());
177177

178+
// Lower .llvm.global_dtors into .llvm_global_ctors with __cxa_atexit calls.
179+
addPass(createWebAssemblyLowerGlobalDtors());
180+
178181
// Fix function bitcasts, as WebAssembly requires caller and callee signatures
179182
// to match.
180183
addPass(createWebAssemblyFixFunctionBitcasts());
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
; RUN: llc < %s -asm-verbose=false | FileCheck --check-prefix=CHECK --check-prefix=FINI --check-prefix=NULL %s
2+
3+
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
4+
target triple = "wasm32-unknown-unknown-wasm"
5+
6+
; Test that @llvm.global_dtors is properly lowered into @llvm.global_ctors,
7+
; grouping dtor calls by priority and associated symbol.
8+
9+
declare void @orig_ctor()
10+
declare void @orig_dtor0()
11+
declare void @orig_dtor1a()
12+
declare void @orig_dtor1b()
13+
declare void @orig_dtor1c0()
14+
declare void @orig_dtor1c1a()
15+
declare void @orig_dtor1c1b()
16+
declare void @orig_dtor65536()
17+
declare void @after_the_null()
18+
19+
@associated1c0 = external global i8
20+
@associated1c1 = external global i8
21+
22+
@llvm.global_ctors = appending global
23+
[1 x { i32, void ()*, i8* }]
24+
[
25+
{ i32, void ()*, i8* } { i32 200, void ()* @orig_ctor, i8* null }
26+
]
27+
28+
@llvm.global_dtors = appending global
29+
[9 x { i32, void ()*, i8* }]
30+
[
31+
{ i32, void ()*, i8* } { i32 0, void ()* @orig_dtor0, i8* null },
32+
{ i32, void ()*, i8* } { i32 1, void ()* @orig_dtor1a, i8* null },
33+
{ i32, void ()*, i8* } { i32 1, void ()* @orig_dtor1b, i8* null },
34+
{ i32, void ()*, i8* } { i32 1, void ()* @orig_dtor1c0, i8* @associated1c0 },
35+
{ i32, void ()*, i8* } { i32 1, void ()* @orig_dtor1c1a, i8* @associated1c1 },
36+
{ i32, void ()*, i8* } { i32 1, void ()* @orig_dtor1c1b, i8* @associated1c1 },
37+
{ i32, void ()*, i8* } { i32 65535, void ()* @orig_dtor65536, i8* null },
38+
{ i32, void ()*, i8* } { i32 65535, void ()* null, i8* null },
39+
{ i32, void ()*, i8* } { i32 65535, void ()* @after_the_null, i8* null }
40+
]
41+
42+
; CHECK-LABEL: .Lcall_dtors.0:
43+
; CHECK-NEXT: .param i32{{$}}
44+
; CHECK-NEXT: call orig_dtor0@FUNCTION{{$}}
45+
46+
; CHECK-LABEL: .Lregister_call_dtors.0:
47+
; CHECK-NEXT: block
48+
; CHECK-NEXT: i32.const $push2=, .Lcall_dtors.0@FUNCTION{{$}}
49+
; CHECK-NEXT: i32.const $push1=, 0
50+
; CHECK-NEXT: i32.const $push0=, __dso_handle
51+
; CHECK-NEXT: i32.call $push3=, __cxa_atexit@FUNCTION, $pop2, $pop1, $pop0{{$}}
52+
; CHECK-NEXT: br_if 0, $pop3
53+
; CHECK-NEXT: return
54+
; CHECK: end_block
55+
; CHECK-NEXT: unreachable
56+
57+
; CHECK-LABEL: .Lcall_dtors.1:
58+
; CHECK-NEXT: .param i32{{$}}
59+
; CHECK-NEXT: call orig_dtor1a@FUNCTION{{$}}
60+
; CHECK-NEXT: call orig_dtor1b@FUNCTION{{$}}
61+
62+
; CHECK-LABEL: .Lregister_call_dtors.1:
63+
; CHECK-NEXT: block
64+
; CHECK-NEXT: i32.const $push2=, .Lcall_dtors.1@FUNCTION{{$}}
65+
; CHECK-NEXT: i32.const $push1=, 0
66+
; CHECK-NEXT: i32.const $push0=, __dso_handle
67+
; CHECK-NEXT: i32.call $push3=, __cxa_atexit@FUNCTION, $pop2, $pop1, $pop0{{$}}
68+
; CHECK-NEXT: br_if 0, $pop3
69+
; CHECK-NEXT: return
70+
; CHECK: end_block
71+
; CHECK-NEXT: unreachable
72+
73+
; CHECK-LABEL: .Lcall_dtors.1.associated1c0:
74+
; CHECK-NEXT: .param i32{{$}}
75+
; CHECK-NEXT: call orig_dtor1c0@FUNCTION{{$}}
76+
77+
; CHECK-LABEL: .Lregister_call_dtors.1.associated1c0:
78+
; CHECK-NEXT: block
79+
; CHECK-NEXT: i32.const $push2=, .Lcall_dtors.1.associated1c0@FUNCTION{{$}}
80+
; CHECK-NEXT: i32.const $push1=, 0
81+
; CHECK-NEXT: i32.const $push0=, __dso_handle
82+
; CHECK-NEXT: i32.call $push3=, __cxa_atexit@FUNCTION, $pop2, $pop1, $pop0{{$}}
83+
; CHECK-NEXT: br_if 0, $pop3
84+
; CHECK-NEXT: return
85+
; CHECK: end_block
86+
; CHECK-NEXT: unreachable
87+
88+
; CHECK-LABEL: .Lcall_dtors.1.associated1c1:
89+
; CHECK-NEXT: .param i32{{$}}
90+
; CHECK-NEXT: call orig_dtor1c1a@FUNCTION{{$}}
91+
; CHECK-NEXT: call orig_dtor1c1b@FUNCTION{{$}}
92+
93+
; CHECK-LABEL: .Lregister_call_dtors.1.associated1c1:
94+
; CHECK-NEXT: block
95+
; CHECK-NEXT: i32.const $push2=, .Lcall_dtors.1.associated1c1@FUNCTION{{$}}
96+
; CHECK-NEXT: i32.const $push1=, 0
97+
; CHECK-NEXT: i32.const $push0=, __dso_handle
98+
; CHECK-NEXT: i32.call $push3=, __cxa_atexit@FUNCTION, $pop2, $pop1, $pop0{{$}}
99+
; CHECK-NEXT: br_if 0, $pop3
100+
; CHECK-NEXT: return
101+
; CHECK: end_block
102+
; CHECK-NEXT: unreachable
103+
104+
; CHECK-LABEL: .Lcall_dtors:
105+
; CHECK-NEXT: .param i32{{$}}
106+
; CHECK-NEXT: call orig_dtor65536@FUNCTION{{$}}
107+
108+
; CHECK-LABEL: .Lregister_call_dtors:
109+
; CHECK-NEXT: block
110+
; CHECK-NEXT: i32.const $push2=, .Lcall_dtors@FUNCTION{{$}}
111+
; CHECK-NEXT: i32.const $push1=, 0
112+
; CHECK-NEXT: i32.const $push0=, __dso_handle
113+
; CHECK-NEXT: i32.call $push3=, __cxa_atexit@FUNCTION, $pop2, $pop1, $pop0{{$}}
114+
; CHECK-NEXT: br_if 0, $pop3
115+
; CHECK-NEXT: return
116+
; CHECK: end_block
117+
; CHECK-NEXT: unreachable
118+
119+
; CHECK-LABEL: .section .init_array.0,"",@
120+
; CHECK: .int32 .Lregister_call_dtors.0@FUNCTION{{$}}
121+
; CHECK-LABEL: .section .init_array.1,"",@
122+
; CHECK: .int32 .Lregister_call_dtors.1@FUNCTION{{$}}
123+
; CHECK-LABEL: .section .init_array.200,"",@
124+
; CHECK: .int32 orig_ctor@FUNCTION{{$}}
125+
; CHECK-LABEL: .section .init_array,"",@
126+
; CHECK: .int32 .Lregister_call_dtors@FUNCTION{{$}}
127+
128+
; CHECK-LABEL: .weak __dso_handle
129+
130+
; CHECK-LABEL: .functype __cxa_atexit, i32, i32, i32, i32{{$}}
131+
132+
; We shouldn't make use of a .fini_array section.
133+
134+
; FINI-NOT: fini_array
135+
136+
; This function is listed after the null terminator, so it should
137+
; be excluded.
138+
139+
; NULL-NOT: after_the_null

‎llvm/test/MC/WebAssembly/init-fini-array.ll

+112-39
Original file line numberDiff line numberDiff line change
@@ -2,37 +2,57 @@
22

33
@global1 = global i32 1025, align 8
44

5+
declare void @func0()
56
declare void @func1()
6-
77
declare void @func2()
8+
declare void @func3()
89

9-
@llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 65535, void ()* @func1, i8* null }]
10+
@llvm.global_ctors = appending global [2 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 65535, void ()* @func0, i8* null }, { i32, void ()*, i8* } { i32 42, void ()* @func1, i8* null }]
1011

11-
@llvm.global_dtors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 65535, void ()* @func2, i8* null }]
12+
@llvm.global_dtors = appending global [2 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 65535, void ()* @func2, i8* null }, { i32, void ()*, i8* } { i32 42, void ()* @func3, i8* null }]
1213

1314

1415
; CHECK: - Type: IMPORT
1516
; CHECK-NEXT: Imports:
1617
; CHECK-NEXT: - Module: env
1718
; CHECK-NEXT: Field: __linear_memory
1819
; CHECK-NEXT: Kind: MEMORY
19-
; CHECK-NEXT: Memory:
20+
; CHECK-NEXT: Memory:
2021
; CHECK-NEXT: Initial: 0x00000001
2122
; CHECK-NEXT: - Module: env
2223
; CHECK-NEXT: Field: __indirect_function_table
2324
; CHECK-NEXT: Kind: TABLE
24-
; CHECK-NEXT: Table:
25+
; CHECK-NEXT: Table:
2526
; CHECK-NEXT: ElemType: ANYFUNC
26-
; CHECK-NEXT: Limits:
27+
; CHECK-NEXT: Limits:
2728
; CHECK-NEXT: Initial: 0x00000002
2829
; CHECK-NEXT: - Module: env
29-
; CHECK-NEXT: Field: func1
30+
; CHECK-NEXT: Field: func3
31+
; CHECK-NEXT: Kind: FUNCTION
32+
; CHECK-NEXT: SigIndex: 1
33+
; CHECK-NEXT: - Module: env
34+
; CHECK-NEXT: Field: __dso_handle
35+
; CHECK-NEXT: Kind: GLOBAL
36+
; CHECK-NEXT: GlobalType: I32
37+
; CHECK-NEXT: GlobalMutable: false
38+
; CHECK-NEXT: - Module: env
39+
; CHECK-NEXT: Field: __cxa_atexit
3040
; CHECK-NEXT: Kind: FUNCTION
31-
; CHECK-NEXT: SigIndex: 0
41+
; CHECK-NEXT: SigIndex: 2
3242
; CHECK-NEXT: - Module: env
3343
; CHECK-NEXT: Field: func2
3444
; CHECK-NEXT: Kind: FUNCTION
35-
; CHECK-NEXT: SigIndex: 0
45+
; CHECK-NEXT: SigIndex: 1
46+
; CHECK-NEXT: - Module: env
47+
; CHECK-NEXT: Field: func1
48+
; CHECK-NEXT: Kind: FUNCTION
49+
; CHECK-NEXT: SigIndex: 1
50+
; CHECK-NEXT: - Module: env
51+
; CHECK-NEXT: Field: func0
52+
; CHECK-NEXT: Kind: FUNCTION
53+
; CHECK-NEXT: SigIndex: 1
54+
; CHECK-NEXT: - Type: FUNCTION
55+
; CHECK-NEXT: FunctionTypes: [ 0, 1, 0, 1 ]
3656
; CHECK-NEXT: - Type: GLOBAL
3757
; CHECK-NEXT: Globals:
3858
; CHECK-NEXT: - Type: I32
@@ -42,64 +62,117 @@ declare void @func2()
4262
; CHECK-NEXT: Value: 0
4363
; CHECK-NEXT: - Type: EXPORT
4464
; CHECK-NEXT: Exports:
65+
; CHECK-NEXT: - Name: .Lcall_dtors.42
66+
; CHECK-NEXT: Kind: FUNCTION
67+
; CHECK-NEXT: Index: 5
68+
; CHECK-NEXT: - Name: .Lregister_call_dtors.42
69+
; CHECK-NEXT: Kind: FUNCTION
70+
; CHECK-NEXT: Index: 6
71+
; CHECK-NEXT: - Name: .Lcall_dtors
72+
; CHECK-NEXT: Kind: FUNCTION
73+
; CHECK-NEXT: Index: 7
74+
; CHECK-NEXT: - Name: .Lregister_call_dtors
75+
; CHECK-NEXT: Kind: FUNCTION
76+
; CHECK-NEXT: Index: 8
4577
; CHECK-NEXT: - Name: global1
4678
; CHECK-NEXT: Kind: GLOBAL
47-
; CHECK-NEXT: Index: 0
79+
; CHECK-NEXT: Index: 1
4880
; CHECK-NEXT: - Type: ELEM
4981
; CHECK-NEXT: Segments:
5082
; CHECK-NEXT: - Offset:
5183
; CHECK-NEXT: Opcode: I32_CONST
5284
; CHECK-NEXT: Value: 0
53-
; CHECK-NEXT: Functions: [ 0, 1 ]
54-
; CHECK-NEXT: - Type: DATA
85+
; CHECK-NEXT: Functions: [ 5, 7 ]
86+
; CHECK-NEXT: - Type: CODE
5587
; CHECK-NEXT: Relocations:
56-
; CHECK-NEXT: - Type: R_WEBASSEMBLY_TABLE_INDEX_I32
88+
; CHECK-NEXT: - Type: R_WEBASSEMBLY_FUNCTION_INDEX_LEB
89+
; CHECK-NEXT: Index: 0
90+
; CHECK-NEXT: Offset: 0x00000004
91+
; CHECK-NEXT: - Type: R_WEBASSEMBLY_TABLE_INDEX_SLEB
5792
; CHECK-NEXT: Index: 0
5893
; CHECK-NEXT: Offset: 0x0000000F
59-
; CHECK-NEXT: - Type: R_WEBASSEMBLY_TABLE_INDEX_I32
94+
; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_SLEB
95+
; CHECK-NEXT: Index: 0
96+
; CHECK-NEXT: Offset: 0x00000017
97+
; CHECK-NEXT: - Type: R_WEBASSEMBLY_FUNCTION_INDEX_LEB
98+
; CHECK-NEXT: Index: 1
99+
; CHECK-NEXT: Offset: 0x0000001D
100+
; CHECK-NEXT: - Type: R_WEBASSEMBLY_FUNCTION_INDEX_LEB
101+
; CHECK-NEXT: Index: 2
102+
; CHECK-NEXT: Offset: 0x0000002C
103+
; CHECK-NEXT: - Type: R_WEBASSEMBLY_TABLE_INDEX_SLEB
60104
; CHECK-NEXT: Index: 1
61-
; CHECK-NEXT: Offset: 0x00000018
105+
; CHECK-NEXT: Offset: 0x00000037
106+
; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_SLEB
107+
; CHECK-NEXT: Index: 0
108+
; CHECK-NEXT: Offset: 0x0000003F
109+
; CHECK-NEXT: - Type: R_WEBASSEMBLY_FUNCTION_INDEX_LEB
110+
; CHECK-NEXT: Index: 1
111+
; CHECK-NEXT: Offset: 0x00000045
112+
; CHECK-NEXT: Functions:
113+
; CHECK-NEXT: - Locals:
114+
; CHECK-NEXT: Body: 1080808080000B
115+
; CHECK-NEXT: - Locals:
116+
; CHECK-NEXT: Body: 0240418080808000410041FFFFFFFF7F1081808080000D000F0B00000B
117+
; CHECK-NEXT: - Locals:
118+
; CHECK-NEXT: Body: 1082808080000B
119+
; CHECK-NEXT: - Locals:
120+
; CHECK-NEXT: Body: 0240418180808000410041FFFFFFFF7F1081808080000D000F0B00000B
121+
; CHECK-NEXT: - Type: DATA
62122
; CHECK-NEXT: Segments:
63123
; CHECK-NEXT: - SectionOffset: 6
64124
; CHECK-NEXT: MemoryIndex: 0
65125
; CHECK-NEXT: Offset:
66126
; CHECK-NEXT: Opcode: I32_CONST
67127
; CHECK-NEXT: Value: 0
68128
; CHECK-NEXT: Content: '01040000'
69-
; CHECK-NEXT: - SectionOffset: 15
70-
; CHECK-NEXT: MemoryIndex: 0
71-
; CHECK-NEXT: Offset:
72-
; CHECK-NEXT: Opcode: I32_CONST
73-
; CHECK-NEXT: Value: 4
74-
; CHECK-NEXT: Content: '00000000'
75-
; CHECK-NEXT: - SectionOffset: 24
76-
; CHECK-NEXT: MemoryIndex: 0
77-
; CHECK-NEXT: Offset:
78-
; CHECK-NEXT: Opcode: I32_CONST
79-
; CHECK-NEXT: Value: 8
80-
; CHECK-NEXT: Content: '01000000'
81129
; CHECK-NEXT: - Type: CUSTOM
82130
; CHECK-NEXT: Name: name
83131
; CHECK-NEXT: FunctionNames:
84132
; CHECK-NEXT: - Index: 0
85-
; CHECK-NEXT: Name: func1
133+
; CHECK-NEXT: Name: func3
86134
; CHECK-NEXT: - Index: 1
135+
; CHECK-NEXT: Name: __cxa_atexit
136+
; CHECK-NEXT: - Index: 2
87137
; CHECK-NEXT: Name: func2
138+
; CHECK-NEXT: - Index: 3
139+
; CHECK-NEXT: Name: func1
140+
; CHECK-NEXT: - Index: 4
141+
; CHECK-NEXT: Name: func0
142+
; CHECK-NEXT: - Index: 5
143+
; CHECK-NEXT: Name: .Lcall_dtors.42
144+
; CHECK-NEXT: - Index: 6
145+
; CHECK-NEXT: Name: .Lregister_call_dtors.42
146+
; CHECK-NEXT: - Index: 7
147+
; CHECK-NEXT: Name: .Lcall_dtors
148+
; CHECK-NEXT: - Index: 8
149+
; CHECK-NEXT: Name: .Lregister_call_dtors
88150
; CHECK-NEXT: - Type: CUSTOM
89151
; CHECK-NEXT: Name: linking
90-
; CHECK-NEXT: DataSize: 12
91-
; CHECK-NEXT: SegmentInfo:
152+
; CHECK-NEXT: DataSize: 4
153+
; CHECK-NEXT: SymbolInfo:
154+
; CHECK-NEXT: - Name: __dso_handle
155+
; CHECK-NEXT: Flags: [ BINDING_WEAK, VISIBILITY_HIDDEN ]
156+
; CHECK-NEXT: - Name: .Lcall_dtors.42
157+
; CHECK-NEXT: Flags: [ BINDING_LOCAL ]
158+
; CHECK-NEXT: - Name: .Lregister_call_dtors.42
159+
; CHECK-NEXT: Flags: [ BINDING_LOCAL ]
160+
; CHECK-NEXT: - Name: .Lcall_dtors
161+
; CHECK-NEXT: Flags: [ BINDING_LOCAL ]
162+
; CHECK-NEXT: - Name: .Lregister_call_dtors
163+
; CHECK-NEXT: Flags: [ BINDING_LOCAL ]
164+
; CHECK-NEXT: SegmentInfo:
92165
; CHECK-NEXT: - Index: 0
93166
; CHECK-NEXT: Name: .data.global1
94167
; CHECK-NEXT: Alignment: 8
95168
; CHECK-NEXT: Flags: [ ]
96-
; CHECK-NEXT: - Index: 1
97-
; CHECK-NEXT: Name: .init_array
98-
; CHECK-NEXT: Alignment: 4
99-
; CHECK-NEXT: Flags: [ ]
100-
; CHECK-NEXT: - Index: 2
101-
; CHECK-NEXT: Name: .fini_array
102-
; CHECK-NEXT: Alignment: 4
103-
; CHECK-NEXT: Flags: [ ]
169+
; CHECK-NEXT: InitFunctions:
170+
; CHECK-NEXT: - Priority: 42
171+
; CHECK-NEXT: FunctionIndex: 3
172+
; CHECK-NEXT: - Priority: 42
173+
; CHECK-NEXT: FunctionIndex: 6
174+
; CHECK-NEXT: - Priority: 65535
175+
; CHECK-NEXT: FunctionIndex: 4
176+
; CHECK-NEXT: - Priority: 65535
177+
; CHECK-NEXT: FunctionIndex: 8
104178
; CHECK-NEXT: ...
105-

0 commit comments

Comments
 (0)
Please sign in to comment.