llvm.org GIT mirror llvm / 5334180
[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](https://github.com/WebAssembly/tool-conventions/issues/25) for more background. Subscribers: jfb, dschuff, mgorny, jgravelle-google, aheejin, sunfish Differential Revision: https://reviews.llvm.org/D41211 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@320774 91177308-0d34-0410-b5e6-96231b3b80d8 Sam Clegg 2 years ago
9 changed file(s) with 543 addition(s) and 48 deletion(s). Raw diff Collapse all Expand all
181181 const Function &F) const override;
182182
183183 void InitializeWasm();
184 MCSection *getStaticCtorSection(unsigned Priority,
185 const MCSymbol *KeySym) const override;
186 MCSection *getStaticDtorSection(unsigned Priority,
187 const MCSymbol *KeySym) const override;
184188
185189 const MCExpr *lowerRelativeReference(const GlobalValue *LHS,
186190 const GlobalValue *RHS,
13581358 void TargetLoweringObjectFileWasm::InitializeWasm() {
13591359 StaticCtorSection =
13601360 getContext().getWasmSection(".init_array", SectionKind::getData());
1361 StaticDtorSection =
1362 getContext().getWasmSection(".fini_array", SectionKind::getData());
1363 }
1361 }
1362
1363 MCSection *TargetLoweringObjectFileWasm::getStaticCtorSection(
1364 unsigned Priority, const MCSymbol *KeySym) const {
1365 return Priority == UINT16_MAX ?
1366 StaticCtorSection :
1367 getContext().getWasmSection(".init_array." + utostr(Priority),
1368 SectionKind::getData());
1369 }
1370
1371 MCSection *TargetLoweringObjectFileWasm::getStaticDtorSection(
1372 unsigned Priority, const MCSymbol *KeySym) const {
1373 llvm_unreachable("@llvm.global_dtors should have been lowered already");
1374 return nullptr;
1375 }
283283 void writeDataRelocSection();
284284 void writeLinkingMetaDataSection(
285285 ArrayRef Segments, uint32_t DataSize,
286 SmallVector, 4> SymbolFlags);
286 const SmallVector, 4> &SymbolFlags,
287 const SmallVector, 2> &InitFuncs);
287288
288289 uint32_t getProvisionalValue(const WasmRelocationEntry &RelEntry);
289290 void applyRelocations(ArrayRef Relocations,
364365 uint64_t C = Target.getConstant();
365366 uint64_t FixupOffset = Layout.getFragmentOffset(Fragment) + Fixup.getOffset();
366367 MCContext &Ctx = Asm.getContext();
368
369 // The .init_array isn't translated as data, so don't do relocations in it.
370 if (FixupSection.getSectionName().startswith(".init_array"))
371 return;
367372
368373 if (const MCSymbolRefExpr *RefB = Target.getSymB()) {
369374 assert(RefB->getKind() == MCSymbolRefExpr::VK_None &&
904909
905910 void WasmObjectWriter::writeLinkingMetaDataSection(
906911 ArrayRef Segments, uint32_t DataSize,
907 SmallVector, 4> SymbolFlags) {
912 const SmallVector, 4> &SymbolFlags,
913 const SmallVector, 2> &InitFuncs) {
908914 SectionBookkeeping Section;
909915 startSection(Section, wasm::WASM_SEC_CUSTOM, "linking");
910916 SectionBookkeeping SubSection;
936942 endSection(SubSection);
937943 }
938944
945 if (!InitFuncs.empty()) {
946 startSection(SubSection, wasm::WASM_INIT_FUNCS);
947 encodeULEB128(InitFuncs.size(), getStream());
948 for (auto &StartFunc : InitFuncs) {
949 encodeULEB128(StartFunc.first, getStream()); // priority
950 encodeULEB128(StartFunc.second, getStream()); // function index
951 }
952 endSection(SubSection);
953 }
954
939955 endSection(Section);
940956 }
941957
976992 SmallVector Imports;
977993 SmallVector Exports;
978994 SmallVector, 4> SymbolFlags;
995 SmallVector, 2> InitFuncs;
979996 SmallPtrSet IsAddressTaken;
980997 unsigned NumFuncImports = 0;
981998 SmallVector DataSegments;
11311148 if (!Section.isWasmData())
11321149 continue;
11331150
1151 // .init_array sections are handled specially elsewhere.
1152 if (cast(Sec).getSectionName().startswith(".init_array"))
1153 continue;
1154
11341155 DataSize = alignTo(DataSize, Section.getAlignment());
11351156 DataSegments.emplace_back();
11361157 WasmDataSegment &Segment = DataSegments.back();
12901311 registerFunctionType(*Fixup.Symbol);
12911312 }
12921313
1314 // Translate .init_array section contents into start functions.
1315 for (const MCSection &S : Asm) {
1316 const auto &WS = static_cast(S);
1317 if (WS.getSectionName().startswith(".fini_array"))
1318 report_fatal_error(".fini_array sections are unsupported");
1319 if (!WS.getSectionName().startswith(".init_array"))
1320 continue;
1321 if (WS.getFragmentList().empty())
1322 continue;
1323 if (WS.getFragmentList().size() != 2)
1324 report_fatal_error("only one .init_array section fragment supported");
1325 const MCFragment &AlignFrag = *WS.begin();
1326 if (AlignFrag.getKind() != MCFragment::FT_Align)
1327 report_fatal_error(".init_array section should be aligned");
1328 if (cast(AlignFrag).getAlignment() != (is64Bit() ? 8 : 4))
1329 report_fatal_error(".init_array section should be aligned for pointers");
1330 const MCFragment &Frag = *std::next(WS.begin());
1331 if (Frag.hasInstructions() || Frag.getKind() != MCFragment::FT_Data)
1332 report_fatal_error("only data supported in .init_array section");
1333 uint16_t Priority = UINT16_MAX;
1334 if (WS.getSectionName().size() != 11) {
1335 if (WS.getSectionName()[11] != '.')
1336 report_fatal_error(".init_array section priority should start with '.'");
1337 if (WS.getSectionName().substr(12).getAsInteger(10, Priority))
1338 report_fatal_error("invalid .init_array section priority");
1339 }
1340 const auto &DataFrag = cast(Frag);
1341 const SmallVectorImpl &Contents = DataFrag.getContents();
1342 for (const uint8_t *p = (const uint8_t *)Contents.data(),
1343 *end = (const uint8_t *)Contents.data() + Contents.size();
1344 p != end; ++p) {
1345 if (*p != 0)
1346 report_fatal_error("non-symbolic data in .init_array section");
1347 }
1348 for (const MCFixup &Fixup : DataFrag.getFixups()) {
1349 assert(Fixup.getKind() == MCFixup::getKindForSize(is64Bit() ? 8 : 4, false));
1350 const MCExpr *Expr = Fixup.getValue();
1351 auto *Sym = dyn_cast(Expr);
1352 if (!Sym)
1353 report_fatal_error("fixups in .init_array should be symbol references");
1354 if (Sym->getKind() != MCSymbolRefExpr::VK_WebAssembly_FUNCTION)
1355 report_fatal_error("symbols in .init_array should be for functions");
1356 auto I = SymbolIndices.find(cast(&Sym->getSymbol()));
1357 if (I == SymbolIndices.end())
1358 report_fatal_error("symbols in .init_array should be defined");
1359 uint32_t Index = I->second;
1360 InitFuncs.push_back(std::make_pair(Priority, Index));
1361 }
1362 }
1363
12931364 // Write out the Wasm header.
12941365 writeHeader(Asm);
12951366
13001371 // Skip the "memory" section; we import the memory instead.
13011372 writeGlobalSection();
13021373 writeExportSection(Exports);
1303 // TODO: Start Section
13041374 writeElemSection(TableElems);
13051375 writeCodeSection(Asm, Layout, Functions);
13061376 writeDataSection(DataSegments);
13071377 writeNameSection(Functions, Imports, NumFuncImports);
13081378 writeCodeRelocSection();
13091379 writeDataRelocSection();
1310 writeLinkingMetaDataSection(DataSegments, DataSize, SymbolFlags);
1380 writeLinkingMetaDataSection(DataSegments, DataSize, SymbolFlags,
1381 InitFuncs);
13111382
13121383 // TODO: Translate the .comment section to the output.
13131384 // TODO: Translate debug sections to the output.
2424 WebAssemblyInstrInfo.cpp
2525 WebAssemblyLowerBrUnless.cpp
2626 WebAssemblyLowerEmscriptenEHSjLj.cpp
27 WebAssemblyLowerGlobalDtors.cpp
2728 WebAssemblyMachineFunctionInfo.cpp
2829 WebAssemblyMCInstLower.cpp
2930 WebAssemblyOptimizeLiveIntervals.cpp
2727 // LLVM IR passes.
2828 ModulePass *createWebAssemblyLowerEmscriptenEHSjLj(bool DoEH, bool DoSjLj);
2929 void initializeWebAssemblyLowerEmscriptenEHSjLjPass(PassRegistry &);
30 ModulePass *createWebAssemblyLowerGlobalDtors();
3031 ModulePass *createWebAssemblyFixFunctionBitcasts();
3132 FunctionPass *createWebAssemblyOptimizeReturned();
3233
0 //===-- WebAssemblyLowerGlobalDtors.cpp - Lower @llvm.global_dtors --------===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8 ///
9 /// \file
10 /// \brief Lower @llvm.global_dtors.
11 ///
12 /// WebAssembly doesn't have a builtin way to invoke static destructors.
13 /// Implement @llvm.global_dtors by creating wrapper functions that are
14 /// registered in @llvm.global_ctors and which contain a call to
15 /// `__cxa_atexit` to register their destructor functions.
16 ///
17 //===----------------------------------------------------------------------===//
18
19 #include "WebAssembly.h"
20 #include "llvm/IR/Constants.h"
21 #include "llvm/IR/Instructions.h"
22 #include "llvm/IR/Intrinsics.h"
23 #include "llvm/IR/Module.h"
24 #include "llvm/Transforms/Utils/ModuleUtils.h"
25 #include "llvm/Pass.h"
26 #include "llvm/ADT/MapVector.h"
27 #include "llvm/Support/Debug.h"
28 #include "llvm/Support/raw_ostream.h"
29 using namespace llvm;
30
31 #define DEBUG_TYPE "wasm-lower-global-dtors"
32
33 namespace {
34 class LowerGlobalDtors final : public ModulePass {
35 StringRef getPassName() const override {
36 return "WebAssembly Lower @llvm.global_dtors";
37 }
38
39 void getAnalysisUsage(AnalysisUsage &AU) const override {
40 AU.setPreservesCFG();
41 ModulePass::getAnalysisUsage(AU);
42 }
43
44 bool runOnModule(Module &M) override;
45
46 public:
47 static char ID;
48 LowerGlobalDtors() : ModulePass(ID) {}
49 };
50 } // End anonymous namespace
51
52 char LowerGlobalDtors::ID = 0;
53 ModulePass *llvm::createWebAssemblyLowerGlobalDtors() {
54 return new LowerGlobalDtors();
55 }
56
57 bool LowerGlobalDtors::runOnModule(Module &M) {
58 GlobalVariable *GV = M.getGlobalVariable("llvm.global_dtors");
59 if (!GV)
60 return false;
61
62 const ConstantArray *InitList = dyn_cast(GV->getInitializer());
63 if (!InitList)
64 return false;
65
66 // Sanity-check @llvm.global_dtor's type.
67 StructType *ETy = dyn_cast(InitList->getType()->getElementType());
68 if (!ETy || ETy->getNumElements() != 3 ||
69 !ETy->getTypeAtIndex(0U)->isIntegerTy() ||
70 !ETy->getTypeAtIndex(1U)->isPointerTy() ||
71 !ETy->getTypeAtIndex(2U)->isPointerTy())
72 return false; // Not (int, ptr, ptr).
73
74 // Collect the contents of @llvm.global_dtors, collated by priority and
75 // associated symbol.
76 std::map > > DtorFuncs;
77 for (Value *O : InitList->operands()) {
78 ConstantStruct *CS = dyn_cast(O);
79 if (!CS) continue; // Malformed.
80
81 ConstantInt *Priority = dyn_cast(CS->getOperand(0));
82 if (!Priority) continue; // Malformed.
83 uint16_t PriorityValue = Priority->getLimitedValue(UINT16_MAX);
84
85 Constant *DtorFunc = CS->getOperand(1);
86 if (DtorFunc->isNullValue())
87 break; // Found a null terminator, skip the rest.
88
89 Constant *Associated = CS->getOperand(2);
90 Associated = cast(Associated->stripPointerCastsNoFollowAliases());
91
92 DtorFuncs[PriorityValue][Associated].push_back(DtorFunc);
93 }
94 if (DtorFuncs.empty())
95 return false;
96
97 // extern "C" int __cxa_atexit(void (*f)(void *), void *p, void *d);
98 LLVMContext &C = M.getContext();
99 PointerType *VoidStar = Type::getInt8PtrTy(C);
100 Type *AtExitFuncArgs[] = { VoidStar };
101 FunctionType *AtExitFuncTy = FunctionType::get(
102 Type::getVoidTy(C),
103 AtExitFuncArgs,
104 /*isVarArg=*/false);
105
106 Type *AtExitArgs[] = {
107 PointerType::get(AtExitFuncTy, 0),
108 VoidStar,
109 VoidStar
110 };
111 FunctionType *AtExitTy = FunctionType::get(
112 Type::getInt32Ty(C),
113 AtExitArgs,
114 /*isVarArg=*/false);
115 Constant *AtExit = M.getOrInsertFunction("__cxa_atexit", AtExitTy);
116
117 // Declare __dso_local.
118 Constant *DsoHandle = M.getNamedValue("__dso_handle");
119 if (!DsoHandle) {
120 Type *DsoHandleTy = Type::getInt8Ty(C);
121 GlobalVariable *Handle =
122 new GlobalVariable(M, DsoHandleTy, /*isConstant=*/true,
123 GlobalVariable::ExternalWeakLinkage,
124 nullptr, "__dso_handle");
125 Handle->setVisibility(GlobalVariable::HiddenVisibility);
126 DsoHandle = Handle;
127 }
128
129 // For each unique priority level and associated symbol, generate a function
130 // to call all the destructors at that level, and a function to register the
131 // first function with __cxa_atexit.
132 for (auto &PriorityAndMore : DtorFuncs) {
133 uint16_t Priority = PriorityAndMore.first;
134 for (auto &AssociatedAndMore : PriorityAndMore.second) {
135 Constant *Associated = AssociatedAndMore.first;
136
137 Function *CallDtors = Function::Create(
138 AtExitFuncTy, Function::PrivateLinkage,
139 "call_dtors" +
140 (Priority != UINT16_MAX ?
141 (Twine(".") + Twine(Priority)) : Twine()) +
142 (!Associated->isNullValue() ?
143 (Twine(".") + Associated->getName()) : Twine()),
144 &M);
145 BasicBlock *BB = BasicBlock::Create(C, "body", CallDtors);
146
147 for (auto Dtor : AssociatedAndMore.second)
148 CallInst::Create(Dtor, "", BB);
149 ReturnInst::Create(C, BB);
150
151 FunctionType *VoidVoid = FunctionType::get(Type::getVoidTy(C),
152 /*isVarArg=*/false);
153 Function *RegisterCallDtors = Function::Create(
154 VoidVoid, Function::PrivateLinkage,
155 "register_call_dtors" +
156 (Priority != UINT16_MAX ?
157 (Twine(".") + Twine(Priority)) : Twine()) +
158 (!Associated->isNullValue() ?
159 (Twine(".") + Associated->getName()) : Twine()),
160 &M);
161 BasicBlock *EntryBB = BasicBlock::Create(C, "entry", RegisterCallDtors);
162 BasicBlock *FailBB = BasicBlock::Create(C, "fail", RegisterCallDtors);
163 BasicBlock *RetBB = BasicBlock::Create(C, "return", RegisterCallDtors);
164
165 Value *Null = ConstantPointerNull::get(VoidStar);
166 Value *Args[] = { CallDtors, Null, DsoHandle };
167 Value *Res = CallInst::Create(AtExit, Args, "call", EntryBB);
168 Value *Cmp = new ICmpInst(*EntryBB, ICmpInst::ICMP_NE, Res,
169 Constant::getNullValue(Res->getType()));
170 BranchInst::Create(FailBB, RetBB, Cmp, EntryBB);
171
172 // If `__cxa_atexit` hits out-of-memory, trap, so that we don't misbehave.
173 // This should be very rare, because if the process is running out of memory
174 // before main has even started, something is wrong.
175 CallInst::Create(Intrinsic::getDeclaration(&M, Intrinsic::trap),
176 "", FailBB);
177 new UnreachableInst(C, FailBB);
178
179 ReturnInst::Create(C, RetBB);
180
181 // Now register the registration function with @llvm.global_ctors.
182 appendToGlobalCtors(M, RegisterCallDtors, Priority, Associated);
183 }
184 }
185
186 // Now that we've lowered everything, remove @llvm.global_dtors.
187 GV->eraseFromParent();
188
189 return true;
190 }
174174 // control specifically what gets lowered.
175175 addPass(createAtomicExpandPass());
176176
177 // Lower .llvm.global_dtors into .llvm_global_ctors with __cxa_atexit calls.
178 addPass(createWebAssemblyLowerGlobalDtors());
179
177180 // Fix function bitcasts, as WebAssembly requires caller and callee signatures
178181 // to match.
179182 addPass(createWebAssemblyFixFunctionBitcasts());
0 ; RUN: llc < %s -asm-verbose=false | FileCheck --check-prefix=CHECK --check-prefix=FINI --check-prefix=NULL %s
1
2 target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
3 target triple = "wasm32-unknown-unknown-wasm"
4
5 ; Test that @llvm.global_dtors is properly lowered into @llvm.global_ctors,
6 ; grouping dtor calls by priority and associated symbol.
7
8 declare void @orig_ctor()
9 declare void @orig_dtor0()
10 declare void @orig_dtor1a()
11 declare void @orig_dtor1b()
12 declare void @orig_dtor1c0()
13 declare void @orig_dtor1c1a()
14 declare void @orig_dtor1c1b()
15 declare void @orig_dtor65536()
16 declare void @after_the_null()
17
18 @associated1c0 = external global i8
19 @associated1c1 = external global i8
20
21 @llvm.global_ctors = appending global
22 [1 x { i32, void ()*, i8* }]
23 [
24 { i32, void ()*, i8* } { i32 200, void ()* @orig_ctor, i8* null }
25 ]
26
27 @llvm.global_dtors = appending global
28 [9 x { i32, void ()*, i8* }]
29 [
30 { i32, void ()*, i8* } { i32 0, void ()* @orig_dtor0, i8* null },
31 { i32, void ()*, i8* } { i32 1, void ()* @orig_dtor1a, i8* null },
32 { i32, void ()*, i8* } { i32 1, void ()* @orig_dtor1b, i8* null },
33 { i32, void ()*, i8* } { i32 1, void ()* @orig_dtor1c0, i8* @associated1c0 },
34 { i32, void ()*, i8* } { i32 1, void ()* @orig_dtor1c1a, i8* @associated1c1 },
35 { i32, void ()*, i8* } { i32 1, void ()* @orig_dtor1c1b, i8* @associated1c1 },
36 { i32, void ()*, i8* } { i32 65535, void ()* @orig_dtor65536, i8* null },
37 { i32, void ()*, i8* } { i32 65535, void ()* null, i8* null },
38 { i32, void ()*, i8* } { i32 65535, void ()* @after_the_null, i8* null }
39 ]
40
41 ; CHECK-LABEL: .Lcall_dtors.0:
42 ; CHECK-NEXT: .param i32{{$}}
43 ; CHECK-NEXT: call orig_dtor0@FUNCTION{{$}}
44
45 ; CHECK-LABEL: .Lregister_call_dtors.0:
46 ; CHECK-NEXT: block
47 ; CHECK-NEXT: i32.const $push2=, .Lcall_dtors.0@FUNCTION{{$}}
48 ; CHECK-NEXT: i32.const $push1=, 0
49 ; CHECK-NEXT: i32.const $push0=, __dso_handle
50 ; CHECK-NEXT: i32.call $push3=, __cxa_atexit@FUNCTION, $pop2, $pop1, $pop0{{$}}
51 ; CHECK-NEXT: br_if 0, $pop3
52 ; CHECK-NEXT: return
53 ; CHECK: end_block
54 ; CHECK-NEXT: unreachable
55
56 ; CHECK-LABEL: .Lcall_dtors.1:
57 ; CHECK-NEXT: .param i32{{$}}
58 ; CHECK-NEXT: call orig_dtor1a@FUNCTION{{$}}
59 ; CHECK-NEXT: call orig_dtor1b@FUNCTION{{$}}
60
61 ; CHECK-LABEL: .Lregister_call_dtors.1:
62 ; CHECK-NEXT: block
63 ; CHECK-NEXT: i32.const $push2=, .Lcall_dtors.1@FUNCTION{{$}}
64 ; CHECK-NEXT: i32.const $push1=, 0
65 ; CHECK-NEXT: i32.const $push0=, __dso_handle
66 ; CHECK-NEXT: i32.call $push3=, __cxa_atexit@FUNCTION, $pop2, $pop1, $pop0{{$}}
67 ; CHECK-NEXT: br_if 0, $pop3
68 ; CHECK-NEXT: return
69 ; CHECK: end_block
70 ; CHECK-NEXT: unreachable
71
72 ; CHECK-LABEL: .Lcall_dtors.1.associated1c0:
73 ; CHECK-NEXT: .param i32{{$}}
74 ; CHECK-NEXT: call orig_dtor1c0@FUNCTION{{$}}
75
76 ; CHECK-LABEL: .Lregister_call_dtors.1.associated1c0:
77 ; CHECK-NEXT: block
78 ; CHECK-NEXT: i32.const $push2=, .Lcall_dtors.1.associated1c0@FUNCTION{{$}}
79 ; CHECK-NEXT: i32.const $push1=, 0
80 ; CHECK-NEXT: i32.const $push0=, __dso_handle
81 ; CHECK-NEXT: i32.call $push3=, __cxa_atexit@FUNCTION, $pop2, $pop1, $pop0{{$}}
82 ; CHECK-NEXT: br_if 0, $pop3
83 ; CHECK-NEXT: return
84 ; CHECK: end_block
85 ; CHECK-NEXT: unreachable
86
87 ; CHECK-LABEL: .Lcall_dtors.1.associated1c1:
88 ; CHECK-NEXT: .param i32{{$}}
89 ; CHECK-NEXT: call orig_dtor1c1a@FUNCTION{{$}}
90 ; CHECK-NEXT: call orig_dtor1c1b@FUNCTION{{$}}
91
92 ; CHECK-LABEL: .Lregister_call_dtors.1.associated1c1:
93 ; CHECK-NEXT: block
94 ; CHECK-NEXT: i32.const $push2=, .Lcall_dtors.1.associated1c1@FUNCTION{{$}}
95 ; CHECK-NEXT: i32.const $push1=, 0
96 ; CHECK-NEXT: i32.const $push0=, __dso_handle
97 ; CHECK-NEXT: i32.call $push3=, __cxa_atexit@FUNCTION, $pop2, $pop1, $pop0{{$}}
98 ; CHECK-NEXT: br_if 0, $pop3
99 ; CHECK-NEXT: return
100 ; CHECK: end_block
101 ; CHECK-NEXT: unreachable
102
103 ; CHECK-LABEL: .Lcall_dtors:
104 ; CHECK-NEXT: .param i32{{$}}
105 ; CHECK-NEXT: call orig_dtor65536@FUNCTION{{$}}
106
107 ; CHECK-LABEL: .Lregister_call_dtors:
108 ; CHECK-NEXT: block
109 ; CHECK-NEXT: i32.const $push2=, .Lcall_dtors@FUNCTION{{$}}
110 ; CHECK-NEXT: i32.const $push1=, 0
111 ; CHECK-NEXT: i32.const $push0=, __dso_handle
112 ; CHECK-NEXT: i32.call $push3=, __cxa_atexit@FUNCTION, $pop2, $pop1, $pop0{{$}}
113 ; CHECK-NEXT: br_if 0, $pop3
114 ; CHECK-NEXT: return
115 ; CHECK: end_block
116 ; CHECK-NEXT: unreachable
117
118 ; CHECK-LABEL: .section .init_array.0,"",@
119 ; CHECK: .int32 .Lregister_call_dtors.0@FUNCTION{{$}}
120 ; CHECK-LABEL: .section .init_array.1,"",@
121 ; CHECK: .int32 .Lregister_call_dtors.1@FUNCTION{{$}}
122 ; CHECK-LABEL: .section .init_array.200,"",@
123 ; CHECK: .int32 orig_ctor@FUNCTION{{$}}
124 ; CHECK-LABEL: .section .init_array,"",@
125 ; CHECK: .int32 .Lregister_call_dtors@FUNCTION{{$}}
126
127 ; CHECK-LABEL: .weak __dso_handle
128
129 ; CHECK-LABEL: .functype __cxa_atexit, i32, i32, i32, i32{{$}}
130
131 ; We shouldn't make use of a .fini_array section.
132
133 ; FINI-NOT: fini_array
134
135 ; This function is listed after the null terminator, so it should
136 ; be excluded.
137
138 ; NULL-NOT: after_the_null
11
22 @global1 = global i32 1025, align 8
33
4 declare void @func0()
45 declare void @func1()
6 declare void @func2()
7 declare void @func3()
58
6 declare void @func2()
9 @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 }]
710
8 @llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 65535, void ()* @func1, i8* null }]
9
10 @llvm.global_dtors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 65535, void ()* @func2, i8* null }]
11 @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 }]
1112
1213
1314 ; CHECK: - Type: IMPORT
1516 ; CHECK-NEXT: - Module: env
1617 ; CHECK-NEXT: Field: __linear_memory
1718 ; CHECK-NEXT: Kind: MEMORY
18 ; CHECK-NEXT: Memory:
19 ; CHECK-NEXT: Memory:
1920 ; CHECK-NEXT: Initial: 0x00000001
2021 ; CHECK-NEXT: - Module: env
2122 ; CHECK-NEXT: Field: __indirect_function_table
2223 ; CHECK-NEXT: Kind: TABLE
23 ; CHECK-NEXT: Table:
24 ; CHECK-NEXT: Table:
2425 ; CHECK-NEXT: ElemType: ANYFUNC
25 ; CHECK-NEXT: Limits:
26 ; CHECK-NEXT: Limits:
2627 ; CHECK-NEXT: Initial: 0x00000002
28 ; CHECK-NEXT: - Module: env
29 ; CHECK-NEXT: Field: func3
30 ; CHECK-NEXT: Kind: FUNCTION
31 ; CHECK-NEXT: SigIndex: 1
32 ; CHECK-NEXT: - Module: env
33 ; CHECK-NEXT: Field: __dso_handle
34 ; CHECK-NEXT: Kind: GLOBAL
35 ; CHECK-NEXT: GlobalType: I32
36 ; CHECK-NEXT: GlobalMutable: false
37 ; CHECK-NEXT: - Module: env
38 ; CHECK-NEXT: Field: __cxa_atexit
39 ; CHECK-NEXT: Kind: FUNCTION
40 ; CHECK-NEXT: SigIndex: 2
41 ; CHECK-NEXT: - Module: env
42 ; CHECK-NEXT: Field: func2
43 ; CHECK-NEXT: Kind: FUNCTION
44 ; CHECK-NEXT: SigIndex: 1
2745 ; CHECK-NEXT: - Module: env
2846 ; CHECK-NEXT: Field: func1
2947 ; CHECK-NEXT: Kind: FUNCTION
30 ; CHECK-NEXT: SigIndex: 0
48 ; CHECK-NEXT: SigIndex: 1
3149 ; CHECK-NEXT: - Module: env
32 ; CHECK-NEXT: Field: func2
50 ; CHECK-NEXT: Field: func0
3351 ; CHECK-NEXT: Kind: FUNCTION
34 ; CHECK-NEXT: SigIndex: 0
52 ; CHECK-NEXT: SigIndex: 1
53 ; CHECK-NEXT: - Type: FUNCTION
54 ; CHECK-NEXT: FunctionTypes: [ 0, 1, 0, 1 ]
3555 ; CHECK-NEXT: - Type: GLOBAL
3656 ; CHECK-NEXT: Globals:
3757 ; CHECK-NEXT: - Type: I32
4161 ; CHECK-NEXT: Value: 0
4262 ; CHECK-NEXT: - Type: EXPORT
4363 ; CHECK-NEXT: Exports:
64 ; CHECK-NEXT: - Name: .Lcall_dtors.42
65 ; CHECK-NEXT: Kind: FUNCTION
66 ; CHECK-NEXT: Index: 5
67 ; CHECK-NEXT: - Name: .Lregister_call_dtors.42
68 ; CHECK-NEXT: Kind: FUNCTION
69 ; CHECK-NEXT: Index: 6
70 ; CHECK-NEXT: - Name: .Lcall_dtors
71 ; CHECK-NEXT: Kind: FUNCTION
72 ; CHECK-NEXT: Index: 7
73 ; CHECK-NEXT: - Name: .Lregister_call_dtors
74 ; CHECK-NEXT: Kind: FUNCTION
75 ; CHECK-NEXT: Index: 8
4476 ; CHECK-NEXT: - Name: global1
4577 ; CHECK-NEXT: Kind: GLOBAL
46 ; CHECK-NEXT: Index: 0
78 ; CHECK-NEXT: Index: 1
4779 ; CHECK-NEXT: - Type: ELEM
4880 ; CHECK-NEXT: Segments:
4981 ; CHECK-NEXT: - Offset:
5082 ; CHECK-NEXT: Opcode: I32_CONST
5183 ; CHECK-NEXT: Value: 0
52 ; CHECK-NEXT: Functions: [ 0, 1 ]
53 ; CHECK-NEXT: - Type: DATA
84 ; CHECK-NEXT: Functions: [ 5, 7 ]
85 ; CHECK-NEXT: - Type: CODE
5486 ; CHECK-NEXT: Relocations:
55 ; CHECK-NEXT: - Type: R_WEBASSEMBLY_TABLE_INDEX_I32
87 ; CHECK-NEXT: - Type: R_WEBASSEMBLY_FUNCTION_INDEX_LEB
88 ; CHECK-NEXT: Index: 0
89 ; CHECK-NEXT: Offset: 0x00000004
90 ; CHECK-NEXT: - Type: R_WEBASSEMBLY_TABLE_INDEX_SLEB
5691 ; CHECK-NEXT: Index: 0
5792 ; CHECK-NEXT: Offset: 0x0000000F
58 ; CHECK-NEXT: - Type: R_WEBASSEMBLY_TABLE_INDEX_I32
93 ; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_SLEB
94 ; CHECK-NEXT: Index: 0
95 ; CHECK-NEXT: Offset: 0x00000017
96 ; CHECK-NEXT: - Type: R_WEBASSEMBLY_FUNCTION_INDEX_LEB
5997 ; CHECK-NEXT: Index: 1
60 ; CHECK-NEXT: Offset: 0x00000018
98 ; CHECK-NEXT: Offset: 0x0000001D
99 ; CHECK-NEXT: - Type: R_WEBASSEMBLY_FUNCTION_INDEX_LEB
100 ; CHECK-NEXT: Index: 2
101 ; CHECK-NEXT: Offset: 0x0000002C
102 ; CHECK-NEXT: - Type: R_WEBASSEMBLY_TABLE_INDEX_SLEB
103 ; CHECK-NEXT: Index: 1
104 ; CHECK-NEXT: Offset: 0x00000037
105 ; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_SLEB
106 ; CHECK-NEXT: Index: 0
107 ; CHECK-NEXT: Offset: 0x0000003F
108 ; CHECK-NEXT: - Type: R_WEBASSEMBLY_FUNCTION_INDEX_LEB
109 ; CHECK-NEXT: Index: 1
110 ; CHECK-NEXT: Offset: 0x00000045
111 ; CHECK-NEXT: Functions:
112 ; CHECK-NEXT: - Locals:
113 ; CHECK-NEXT: Body: 1080808080000B
114 ; CHECK-NEXT: - Locals:
115 ; CHECK-NEXT: Body: 0240418080808000410041FFFFFFFF7F1081808080000D000F0B00000B
116 ; CHECK-NEXT: - Locals:
117 ; CHECK-NEXT: Body: 1082808080000B
118 ; CHECK-NEXT: - Locals:
119 ; CHECK-NEXT: Body: 0240418180808000410041FFFFFFFF7F1081808080000D000F0B00000B
120 ; CHECK-NEXT: - Type: DATA
61121 ; CHECK-NEXT: Segments:
62122 ; CHECK-NEXT: - SectionOffset: 6
63123 ; CHECK-NEXT: MemoryIndex: 0
65125 ; CHECK-NEXT: Opcode: I32_CONST
66126 ; CHECK-NEXT: Value: 0
67127 ; CHECK-NEXT: Content: '01040000'
68 ; CHECK-NEXT: - SectionOffset: 15
69 ; CHECK-NEXT: MemoryIndex: 0
70 ; CHECK-NEXT: Offset:
71 ; CHECK-NEXT: Opcode: I32_CONST
72 ; CHECK-NEXT: Value: 4
73 ; CHECK-NEXT: Content: '00000000'
74 ; CHECK-NEXT: - SectionOffset: 24
75 ; CHECK-NEXT: MemoryIndex: 0
76 ; CHECK-NEXT: Offset:
77 ; CHECK-NEXT: Opcode: I32_CONST
78 ; CHECK-NEXT: Value: 8
79 ; CHECK-NEXT: Content: '01000000'
80128 ; CHECK-NEXT: - Type: CUSTOM
81129 ; CHECK-NEXT: Name: name
82130 ; CHECK-NEXT: FunctionNames:
83131 ; CHECK-NEXT: - Index: 0
132 ; CHECK-NEXT: Name: func3
133 ; CHECK-NEXT: - Index: 1
134 ; CHECK-NEXT: Name: __cxa_atexit
135 ; CHECK-NEXT: - Index: 2
136 ; CHECK-NEXT: Name: func2
137 ; CHECK-NEXT: - Index: 3
84138 ; CHECK-NEXT: Name: func1
85 ; CHECK-NEXT: - Index: 1
86 ; CHECK-NEXT: Name: func2
139 ; CHECK-NEXT: - Index: 4
140 ; CHECK-NEXT: Name: func0
141 ; CHECK-NEXT: - Index: 5
142 ; CHECK-NEXT: Name: .Lcall_dtors.42
143 ; CHECK-NEXT: - Index: 6
144 ; CHECK-NEXT: Name: .Lregister_call_dtors.42
145 ; CHECK-NEXT: - Index: 7
146 ; CHECK-NEXT: Name: .Lcall_dtors
147 ; CHECK-NEXT: - Index: 8
148 ; CHECK-NEXT: Name: .Lregister_call_dtors
87149 ; CHECK-NEXT: - Type: CUSTOM
88150 ; CHECK-NEXT: Name: linking
89 ; CHECK-NEXT: DataSize: 12
90 ; CHECK-NEXT: SegmentInfo:
151 ; CHECK-NEXT: DataSize: 4
152 ; CHECK-NEXT: SymbolInfo:
153 ; CHECK-NEXT: - Name: __dso_handle
154 ; CHECK-NEXT: Flags: [ BINDING_WEAK, VISIBILITY_HIDDEN ]
155 ; CHECK-NEXT: - Name: .Lcall_dtors.42
156 ; CHECK-NEXT: Flags: [ BINDING_LOCAL ]
157 ; CHECK-NEXT: - Name: .Lregister_call_dtors.42
158 ; CHECK-NEXT: Flags: [ BINDING_LOCAL ]
159 ; CHECK-NEXT: - Name: .Lcall_dtors
160 ; CHECK-NEXT: Flags: [ BINDING_LOCAL ]
161 ; CHECK-NEXT: - Name: .Lregister_call_dtors
162 ; CHECK-NEXT: Flags: [ BINDING_LOCAL ]
163 ; CHECK-NEXT: SegmentInfo:
91164 ; CHECK-NEXT: - Index: 0
92165 ; CHECK-NEXT: Name: .data.global1
93166 ; CHECK-NEXT: Alignment: 8
94167 ; CHECK-NEXT: Flags: [ ]
95 ; CHECK-NEXT: - Index: 1
96 ; CHECK-NEXT: Name: .init_array
97 ; CHECK-NEXT: Alignment: 4
98 ; CHECK-NEXT: Flags: [ ]
99 ; CHECK-NEXT: - Index: 2
100 ; CHECK-NEXT: Name: .fini_array
101 ; CHECK-NEXT: Alignment: 4
102 ; CHECK-NEXT: Flags: [ ]
168 ; CHECK-NEXT: InitFunctions:
169 ; CHECK-NEXT: - Priority: 42
170 ; CHECK-NEXT: FunctionIndex: 3
171 ; CHECK-NEXT: - Priority: 42
172 ; CHECK-NEXT: FunctionIndex: 6
173 ; CHECK-NEXT: - Priority: 65535
174 ; CHECK-NEXT: FunctionIndex: 4
175 ; CHECK-NEXT: - Priority: 65535
176 ; CHECK-NEXT: FunctionIndex: 8
103177 ; CHECK-NEXT: ...
104