2017-11-18 02:14:09 +08:00
|
|
|
//===- Writer.cpp ---------------------------------------------------------===//
|
|
|
|
//
|
2019-01-19 16:50:56 +08:00
|
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
2017-11-18 02:14:09 +08:00
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
#include "Writer.h"
|
|
|
|
#include "Config.h"
|
2018-01-10 09:13:34 +08:00
|
|
|
#include "InputChunks.h"
|
2018-12-08 14:17:43 +08:00
|
|
|
#include "InputEvent.h"
|
2018-02-23 13:08:53 +08:00
|
|
|
#include "InputGlobal.h"
|
2021-01-05 19:08:58 +08:00
|
|
|
#include "InputTable.h"
|
2020-03-28 07:52:27 +08:00
|
|
|
#include "MapFile.h"
|
2017-11-18 02:14:09 +08:00
|
|
|
#include "OutputSections.h"
|
|
|
|
#include "OutputSegment.h"
|
2019-05-21 17:13:09 +08:00
|
|
|
#include "Relocations.h"
|
2017-11-18 02:14:09 +08:00
|
|
|
#include "SymbolTable.h"
|
2019-05-21 17:13:09 +08:00
|
|
|
#include "SyntheticSections.h"
|
2017-11-18 02:14:09 +08:00
|
|
|
#include "WriterUtils.h"
|
|
|
|
#include "lld/Common/ErrorHandler.h"
|
2017-11-29 04:39:17 +08:00
|
|
|
#include "lld/Common/Memory.h"
|
2018-03-07 18:37:50 +08:00
|
|
|
#include "lld/Common/Strings.h"
|
2018-02-21 05:53:18 +08:00
|
|
|
#include "llvm/ADT/DenseSet.h"
|
[WebAssembly] Target features section
Summary:
Implements a new target features section in assembly and object files
that records what features are used, required, and disallowed in
WebAssembly objects. The linker uses this information to ensure that
all objects participating in a link are feature-compatible and records
the set of used features in the output binary for use by optimizers
and other tools later in the toolchain.
The "atomics" feature is always required or disallowed to prevent
linking code with stripped atomics into multithreaded binaries. Other
features are marked used if they are enabled globally or on any
function in a module.
Future CLs will add linker flags for ignoring feature compatibility
checks and for specifying the set of allowed features, implement using
the presence of the "atomics" feature to control the type of memory
and segments in the linked binary, and add front-end flags for
relaxing the linkage policy for atomics.
Reviewers: aheejin, sbc100, dschuff
Subscribers: jgravelle-google, hiraditya, sunfish, mgrang, jfb, jdoerfert, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D59173
llvm-svn: 356610
2019-03-21 04:26:45 +08:00
|
|
|
#include "llvm/ADT/SmallSet.h"
|
2019-01-17 10:29:41 +08:00
|
|
|
#include "llvm/ADT/SmallVector.h"
|
2018-04-11 00:12:49 +08:00
|
|
|
#include "llvm/ADT/StringMap.h"
|
2018-02-23 13:08:53 +08:00
|
|
|
#include "llvm/BinaryFormat/Wasm.h"
|
2020-09-29 04:06:34 +08:00
|
|
|
#include "llvm/BinaryFormat/WasmTraits.h"
|
2017-11-18 02:14:09 +08:00
|
|
|
#include "llvm/Support/FileOutputBuffer.h"
|
|
|
|
#include "llvm/Support/Format.h"
|
|
|
|
#include "llvm/Support/FormatVariadic.h"
|
|
|
|
#include "llvm/Support/LEB128.h"
|
[Support] Move LLD's parallel algorithm wrappers to support
Essentially takes the lld/Common/Threads.h wrappers and moves them to
the llvm/Support/Paralle.h algorithm header.
The changes are:
- Remove policy parameter, since all clients use `par`.
- Rename the methods to `parallelSort` etc to match LLVM style, since
they are no longer C++17 pstl compatible.
- Move algorithms from llvm::parallel:: to llvm::, since they have
"parallel" in the name and are no longer overloads of the regular
algorithms.
- Add range overloads
- Use the sequential algorithm directly when 1 thread is requested
(skips task grouping)
- Fix the index type of parallelForEachN to size_t. Nobody in LLVM was
using any other parameter, and it made overload resolution hard for
for_each_n(par, 0, foo.size(), ...) because 0 is int, not size_t.
Remove Threads.h and update LLD for that.
This is a prerequisite for parallel public symbol processing in the PDB
library, which is in LLVM.
Reviewed By: MaskRay, aganea
Differential Revision: https://reviews.llvm.org/D79390
2020-05-05 11:03:19 +08:00
|
|
|
#include "llvm/Support/Parallel.h"
|
2017-11-18 02:14:09 +08:00
|
|
|
|
|
|
|
#include <cstdarg>
|
2018-01-13 06:25:17 +08:00
|
|
|
#include <map>
|
2017-11-18 02:14:09 +08:00
|
|
|
|
|
|
|
#define DEBUG_TYPE "lld"
|
|
|
|
|
|
|
|
using namespace llvm;
|
|
|
|
using namespace llvm::wasm;
|
|
|
|
|
2019-10-10 13:25:39 +08:00
|
|
|
namespace lld {
|
|
|
|
namespace wasm {
|
2019-02-05 03:13:46 +08:00
|
|
|
static constexpr int stackAlignment = 16;
|
2017-11-18 02:14:09 +08:00
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
// The writer writes a SymbolTable result to a file.
|
|
|
|
class Writer {
|
|
|
|
public:
|
|
|
|
void run();
|
|
|
|
|
|
|
|
private:
|
|
|
|
void openFile();
|
|
|
|
|
2020-05-22 02:33:25 +08:00
|
|
|
bool needsPassiveInitialization(const OutputSegment *segment);
|
|
|
|
bool hasPassiveInitializedSegments();
|
|
|
|
|
2020-12-10 23:40:48 +08:00
|
|
|
void createSyntheticInitFunctions();
|
2019-07-04 06:04:54 +08:00
|
|
|
void createInitMemoryFunction();
|
2020-12-10 10:14:31 +08:00
|
|
|
void createStartFunction();
|
|
|
|
void createApplyDataRelocationsFunction();
|
|
|
|
void createApplyGlobalRelocationsFunction();
|
2019-04-05 02:40:51 +08:00
|
|
|
void createCallCtorsFunction();
|
[WebAssembly] Implement thread-local storage (local-exec model)
Summary:
Thread local variables are placed inside a `.tdata` segment. Their symbols are
offsets from the start of the segment. The address of a thread local variable
is computed as `__tls_base` + the offset from the start of the segment.
`.tdata` segment is a passive segment and `memory.init` is used once per thread
to initialize the thread local storage.
`__tls_base` is a wasm global. Since each thread has its own wasm instance,
it is effectively thread local. Currently, `__tls_base` must be initialized
at thread startup, and so cannot be used with dynamic libraries.
`__tls_base` is to be initialized with a new linker-synthesized function,
`__wasm_init_tls`, which takes as an argument a block of memory to use as the
storage for thread locals. It then initializes the block of memory and sets
`__tls_base`. As `__wasm_init_tls` will handle the memory initialization,
the memory does not have to be zeroed.
To help allocating memory for thread-local storage, a new compiler intrinsic
is introduced: `__builtin_wasm_tls_size()`. This instrinsic function returns
the size of the thread-local storage for the current function.
The expected usage is to run something like the following upon thread startup:
__wasm_init_tls(malloc(__builtin_wasm_tls_size()));
Reviewers: tlively, aheejin, kripken, sbc100
Subscribers: dschuff, jgravelle-google, hiraditya, sunfish, jfb, cfe-commits, llvm-commits
Tags: #clang, #llvm
Differential Revision: https://reviews.llvm.org/D64537
llvm-svn: 366272
2019-07-17 06:00:45 +08:00
|
|
|
void createInitTLSFunction();
|
2020-10-01 08:21:57 +08:00
|
|
|
void createCommandExportWrappers();
|
|
|
|
void createCommandExportWrapper(uint32_t functionIndex, DefinedFunction *f);
|
2019-04-05 02:40:51 +08:00
|
|
|
|
2018-01-10 07:56:44 +08:00
|
|
|
void assignIndexes();
|
2019-05-21 17:13:09 +08:00
|
|
|
void populateSymtab();
|
|
|
|
void populateProducers();
|
|
|
|
void populateTargetFeatures();
|
|
|
|
void calculateInitFunctions();
|
2017-11-18 02:14:09 +08:00
|
|
|
void calculateImports();
|
2018-01-19 07:40:49 +08:00
|
|
|
void calculateExports();
|
2018-05-05 07:14:42 +08:00
|
|
|
void calculateCustomSections();
|
2017-11-18 02:14:09 +08:00
|
|
|
void calculateTypes();
|
|
|
|
void createOutputSegments();
|
|
|
|
void layoutMemory();
|
|
|
|
void createHeader();
|
2019-05-21 17:13:09 +08:00
|
|
|
|
|
|
|
void addSection(OutputSection *sec);
|
|
|
|
|
|
|
|
void addSections();
|
2019-05-23 18:06:03 +08:00
|
|
|
|
2018-04-11 00:12:49 +08:00
|
|
|
void createCustomSections();
|
2019-05-21 17:13:09 +08:00
|
|
|
void createSyntheticSections();
|
|
|
|
void finalizeSections();
|
2017-11-18 02:14:09 +08:00
|
|
|
|
|
|
|
// Custom sections
|
|
|
|
void createRelocSections();
|
|
|
|
|
|
|
|
void writeHeader();
|
|
|
|
void writeSections();
|
|
|
|
|
|
|
|
uint64_t fileSize = 0;
|
|
|
|
|
2019-05-21 17:13:09 +08:00
|
|
|
std::vector<WasmInitEntry> initFunctions;
|
2018-04-11 00:12:49 +08:00
|
|
|
llvm::StringMap<std::vector<InputSection *>> customSectionMapping;
|
|
|
|
|
2020-10-01 08:21:57 +08:00
|
|
|
// Stable storage for command export wrapper function name strings.
|
|
|
|
std::list<std::string> commandExportWrapperNames;
|
|
|
|
|
2017-11-18 02:14:09 +08:00
|
|
|
// Elements that are used to construct the final output
|
|
|
|
std::string header;
|
|
|
|
std::vector<OutputSection *> outputSections;
|
|
|
|
|
|
|
|
std::unique_ptr<FileOutputBuffer> buffer;
|
|
|
|
|
|
|
|
std::vector<OutputSegment *> segments;
|
|
|
|
llvm::SmallDenseMap<StringRef, OutputSegment *> segmentMap;
|
|
|
|
};
|
|
|
|
|
|
|
|
} // anonymous namespace
|
|
|
|
|
2018-05-05 07:14:42 +08:00
|
|
|
void Writer::calculateCustomSections() {
|
|
|
|
log("calculateCustomSections");
|
|
|
|
bool stripDebug = config->stripDebug || config->stripAll;
|
|
|
|
for (ObjFile *file : symtab->objectFiles) {
|
|
|
|
for (InputSection *section : file->customSections) {
|
2020-12-10 02:57:27 +08:00
|
|
|
// Exclude COMDAT sections that are not selected for inclusion
|
|
|
|
if (section->discarded)
|
|
|
|
continue;
|
2018-05-05 07:14:42 +08:00
|
|
|
StringRef name = section->getName();
|
|
|
|
// These custom sections are known the linker and synthesized rather than
|
2020-03-31 08:37:01 +08:00
|
|
|
// blindly copied.
|
2019-01-17 10:29:41 +08:00
|
|
|
if (name == "linking" || name == "name" || name == "producers" ||
|
[WebAssembly] Target features section
Summary:
Implements a new target features section in assembly and object files
that records what features are used, required, and disallowed in
WebAssembly objects. The linker uses this information to ensure that
all objects participating in a link are feature-compatible and records
the set of used features in the output binary for use by optimizers
and other tools later in the toolchain.
The "atomics" feature is always required or disallowed to prevent
linking code with stripped atomics into multithreaded binaries. Other
features are marked used if they are enabled globally or on any
function in a module.
Future CLs will add linker flags for ignoring feature compatibility
checks and for specifying the set of allowed features, implement using
the presence of the "atomics" feature to control the type of memory
and segments in the linked binary, and add front-end flags for
relaxing the linkage policy for atomics.
Reviewers: aheejin, sbc100, dschuff
Subscribers: jgravelle-google, hiraditya, sunfish, mgrang, jfb, jdoerfert, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D59173
llvm-svn: 356610
2019-03-21 04:26:45 +08:00
|
|
|
name == "target_features" || name.startswith("reloc."))
|
2018-05-05 07:14:42 +08:00
|
|
|
continue;
|
2020-03-31 08:37:01 +08:00
|
|
|
// These custom sections are generated by `clang -fembed-bitcode`.
|
|
|
|
// These are used by the rust toolchain to ship LTO data along with
|
|
|
|
// compiled object code, but they don't want this included in the linker
|
|
|
|
// output.
|
|
|
|
if (name == ".llvmbc" || name == ".llvmcmd")
|
|
|
|
continue;
|
|
|
|
// Strip debug section in that option was specified.
|
2018-05-05 07:14:42 +08:00
|
|
|
if (stripDebug && name.startswith(".debug_"))
|
|
|
|
continue;
|
2020-03-31 08:37:01 +08:00
|
|
|
// Otherwise include custom sections by default and concatenate their
|
|
|
|
// contents.
|
2018-05-05 07:14:42 +08:00
|
|
|
customSectionMapping[name].push_back(section);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-11 00:12:49 +08:00
|
|
|
void Writer::createCustomSections() {
|
|
|
|
log("createCustomSections");
|
|
|
|
for (auto &pair : customSectionMapping) {
|
|
|
|
StringRef name = pair.first();
|
2018-05-15 21:36:20 +08:00
|
|
|
LLVM_DEBUG(dbgs() << "createCustomSection: " << name << "\n");
|
2019-07-11 13:40:30 +08:00
|
|
|
|
2020-01-29 03:23:46 +08:00
|
|
|
OutputSection *sec = make<CustomSection>(std::string(name), pair.second);
|
2019-05-24 21:28:27 +08:00
|
|
|
if (config->relocatable || config->emitRelocs) {
|
2019-05-21 17:13:09 +08:00
|
|
|
auto *sym = make<OutputSectionSymbol>(sec);
|
|
|
|
out.linkingSec->addToSymtab(sym);
|
|
|
|
sec->sectionSym = sym;
|
|
|
|
}
|
|
|
|
addSection(sec);
|
2017-12-12 06:00:56 +08:00
|
|
|
}
|
2017-11-18 02:14:09 +08:00
|
|
|
}
|
|
|
|
|
2017-12-20 03:56:27 +08:00
|
|
|
// Create relocations sections in the final output.
|
2017-11-18 02:14:09 +08:00
|
|
|
// These are only created when relocatable output is requested.
|
|
|
|
void Writer::createRelocSections() {
|
|
|
|
log("createRelocSections");
|
|
|
|
// Don't use iterator here since we are adding to OutputSection
|
|
|
|
size_t origSize = outputSections.size();
|
2018-04-25 07:09:57 +08:00
|
|
|
for (size_t i = 0; i < origSize; i++) {
|
2019-05-21 17:13:09 +08:00
|
|
|
LLVM_DEBUG(dbgs() << "check section " << i << "\n");
|
|
|
|
OutputSection *sec = outputSections[i];
|
|
|
|
|
|
|
|
// Count the number of needed sections.
|
2019-07-10 17:10:01 +08:00
|
|
|
uint32_t count = sec->getNumRelocations();
|
2017-11-18 02:14:09 +08:00
|
|
|
if (!count)
|
|
|
|
continue;
|
|
|
|
|
2018-02-28 08:01:31 +08:00
|
|
|
StringRef name;
|
2019-05-21 17:13:09 +08:00
|
|
|
if (sec->type == WASM_SEC_DATA)
|
2018-02-28 08:01:31 +08:00
|
|
|
name = "reloc.DATA";
|
2019-05-21 17:13:09 +08:00
|
|
|
else if (sec->type == WASM_SEC_CODE)
|
2018-02-28 08:01:31 +08:00
|
|
|
name = "reloc.CODE";
|
2019-05-21 17:13:09 +08:00
|
|
|
else if (sec->type == WASM_SEC_CUSTOM)
|
|
|
|
name = saver.save("reloc." + sec->name);
|
2017-11-18 02:14:09 +08:00
|
|
|
else
|
2018-05-05 07:14:42 +08:00
|
|
|
llvm_unreachable(
|
|
|
|
"relocations only supported for code, data, or custom sections");
|
2017-11-18 02:14:09 +08:00
|
|
|
|
2019-05-21 17:13:09 +08:00
|
|
|
addSection(make<RelocSection>(name, sec));
|
2018-01-13 06:25:17 +08:00
|
|
|
}
|
2017-11-18 02:14:09 +08:00
|
|
|
}
|
|
|
|
|
2019-05-21 17:13:09 +08:00
|
|
|
void Writer::populateProducers() {
|
2019-01-17 10:29:41 +08:00
|
|
|
for (ObjFile *file : symtab->objectFiles) {
|
|
|
|
const WasmProducerInfo &info = file->getWasmObj()->getProducerInfo();
|
2019-05-21 17:13:09 +08:00
|
|
|
out.producersSec->addInfo(info);
|
[WebAssembly] Target features section
Summary:
Implements a new target features section in assembly and object files
that records what features are used, required, and disallowed in
WebAssembly objects. The linker uses this information to ensure that
all objects participating in a link are feature-compatible and records
the set of used features in the output binary for use by optimizers
and other tools later in the toolchain.
The "atomics" feature is always required or disallowed to prevent
linking code with stripped atomics into multithreaded binaries. Other
features are marked used if they are enabled globally or on any
function in a module.
Future CLs will add linker flags for ignoring feature compatibility
checks and for specifying the set of allowed features, implement using
the presence of the "atomics" feature to control the type of memory
and segments in the linked binary, and add front-end flags for
relaxing the linkage policy for atomics.
Reviewers: aheejin, sbc100, dschuff
Subscribers: jgravelle-google, hiraditya, sunfish, mgrang, jfb, jdoerfert, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D59173
llvm-svn: 356610
2019-03-21 04:26:45 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-18 02:14:09 +08:00
|
|
|
void Writer::writeHeader() {
|
|
|
|
memcpy(buffer->getBufferStart(), header.data(), header.size());
|
|
|
|
}
|
|
|
|
|
|
|
|
void Writer::writeSections() {
|
|
|
|
uint8_t *buf = buffer->getBufferStart();
|
2019-05-21 17:13:09 +08:00
|
|
|
parallelForEach(outputSections, [buf](OutputSection *s) {
|
|
|
|
assert(s->isNeeded());
|
|
|
|
s->writeTo(buf);
|
|
|
|
});
|
2017-11-18 02:14:09 +08:00
|
|
|
}
|
|
|
|
|
2020-11-10 09:52:39 +08:00
|
|
|
static void setGlobalPtr(DefinedGlobal *g, uint64_t memoryPtr) {
|
|
|
|
if (config->is64.getValueOr(false)) {
|
|
|
|
assert(g->global->global.InitExpr.Opcode == WASM_OPCODE_I64_CONST);
|
|
|
|
g->global->global.InitExpr.Value.Int64 = memoryPtr;
|
|
|
|
} else {
|
|
|
|
assert(g->global->global.InitExpr.Opcode == WASM_OPCODE_I32_CONST);
|
|
|
|
g->global->global.InitExpr.Value.Int32 = memoryPtr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-18 02:14:09 +08:00
|
|
|
// Fix the memory layout of the output binary. This assigns memory offsets
|
2017-12-01 08:53:21 +08:00
|
|
|
// to each of the input data sections as well as the explicit stack region.
|
2018-05-04 01:21:53 +08:00
|
|
|
// The default memory layout is as follows, from low to high.
|
|
|
|
//
|
2019-07-16 16:08:17 +08:00
|
|
|
// - initialized data (starting at Config->globalBase)
|
2018-02-03 06:59:56 +08:00
|
|
|
// - BSS data (not currently implemented in llvm)
|
|
|
|
// - explicit stack (Config->ZStackSize)
|
|
|
|
// - heap start / unallocated
|
2018-05-04 01:21:53 +08:00
|
|
|
//
|
|
|
|
// The --stack-first option means that stack is placed before any static data.
|
2018-08-30 05:03:16 +08:00
|
|
|
// This can be useful since it means that stack overflow traps immediately
|
|
|
|
// rather than overwriting global data, but also increases code size since all
|
|
|
|
// static data loads and stores requires larger offsets.
|
2017-11-18 02:14:09 +08:00
|
|
|
void Writer::layoutMemory() {
|
2020-04-04 07:18:29 +08:00
|
|
|
uint64_t memoryPtr = 0;
|
2017-11-18 02:14:09 +08:00
|
|
|
|
2018-05-04 01:21:53 +08:00
|
|
|
auto placeStack = [&]() {
|
2019-07-11 21:13:25 +08:00
|
|
|
if (config->relocatable || config->isPic)
|
2018-05-04 01:21:53 +08:00
|
|
|
return;
|
2019-02-05 03:13:46 +08:00
|
|
|
memoryPtr = alignTo(memoryPtr, stackAlignment);
|
|
|
|
if (config->zStackSize != alignTo(config->zStackSize, stackAlignment))
|
|
|
|
error("stack size must be " + Twine(stackAlignment) + "-byte aligned");
|
2018-05-04 01:21:53 +08:00
|
|
|
log("mem: stack size = " + Twine(config->zStackSize));
|
|
|
|
log("mem: stack base = " + Twine(memoryPtr));
|
|
|
|
memoryPtr += config->zStackSize;
|
2018-11-16 02:15:54 +08:00
|
|
|
auto *sp = cast<DefinedGlobal>(WasmSym::stackPointer);
|
2020-06-30 08:53:09 +08:00
|
|
|
switch (sp->global->global.InitExpr.Opcode) {
|
|
|
|
case WASM_OPCODE_I32_CONST:
|
|
|
|
sp->global->global.InitExpr.Value.Int32 = memoryPtr;
|
|
|
|
break;
|
|
|
|
case WASM_OPCODE_I64_CONST:
|
|
|
|
sp->global->global.InitExpr.Value.Int64 = memoryPtr;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
llvm_unreachable("init expr must be i32/i64.const");
|
|
|
|
}
|
2018-05-04 01:21:53 +08:00
|
|
|
log("mem: stack top = " + Twine(memoryPtr));
|
|
|
|
};
|
|
|
|
|
|
|
|
if (config->stackFirst) {
|
|
|
|
placeStack();
|
|
|
|
} else {
|
|
|
|
memoryPtr = config->globalBase;
|
|
|
|
log("mem: global base = " + Twine(config->globalBase));
|
|
|
|
}
|
|
|
|
|
2019-06-27 04:12:33 +08:00
|
|
|
if (WasmSym::globalBase)
|
2019-08-14 01:02:02 +08:00
|
|
|
WasmSym::globalBase->setVirtualAddress(memoryPtr);
|
2019-06-27 04:12:33 +08:00
|
|
|
|
2020-04-04 07:18:29 +08:00
|
|
|
uint64_t dataStart = memoryPtr;
|
2017-11-18 02:14:09 +08:00
|
|
|
|
2018-02-03 06:59:56 +08:00
|
|
|
// Arbitrarily set __dso_handle handle to point to the start of the data
|
|
|
|
// segments.
|
|
|
|
if (WasmSym::dsoHandle)
|
2018-05-04 01:21:53 +08:00
|
|
|
WasmSym::dsoHandle->setVirtualAddress(dataStart);
|
2018-02-03 06:59:56 +08:00
|
|
|
|
2019-05-21 17:13:09 +08:00
|
|
|
out.dylinkSec->memAlign = 0;
|
2017-11-18 02:14:09 +08:00
|
|
|
for (OutputSegment *seg : segments) {
|
2019-05-21 17:13:09 +08:00
|
|
|
out.dylinkSec->memAlign = std::max(out.dylinkSec->memAlign, seg->alignment);
|
2019-01-18 06:09:09 +08:00
|
|
|
memoryPtr = alignTo(memoryPtr, 1ULL << seg->alignment);
|
2017-11-18 02:14:09 +08:00
|
|
|
seg->startVA = memoryPtr;
|
2018-03-14 21:50:20 +08:00
|
|
|
log(formatv("mem: {0,-15} offset={1,-8} size={2,-8} align={3}", seg->name,
|
|
|
|
memoryPtr, seg->size, seg->alignment));
|
[WebAssembly] Implement thread-local storage (local-exec model)
Summary:
Thread local variables are placed inside a `.tdata` segment. Their symbols are
offsets from the start of the segment. The address of a thread local variable
is computed as `__tls_base` + the offset from the start of the segment.
`.tdata` segment is a passive segment and `memory.init` is used once per thread
to initialize the thread local storage.
`__tls_base` is a wasm global. Since each thread has its own wasm instance,
it is effectively thread local. Currently, `__tls_base` must be initialized
at thread startup, and so cannot be used with dynamic libraries.
`__tls_base` is to be initialized with a new linker-synthesized function,
`__wasm_init_tls`, which takes as an argument a block of memory to use as the
storage for thread locals. It then initializes the block of memory and sets
`__tls_base`. As `__wasm_init_tls` will handle the memory initialization,
the memory does not have to be zeroed.
To help allocating memory for thread-local storage, a new compiler intrinsic
is introduced: `__builtin_wasm_tls_size()`. This instrinsic function returns
the size of the thread-local storage for the current function.
The expected usage is to run something like the following upon thread startup:
__wasm_init_tls(malloc(__builtin_wasm_tls_size()));
Reviewers: tlively, aheejin, kripken, sbc100
Subscribers: dschuff, jgravelle-google, hiraditya, sunfish, jfb, cfe-commits, llvm-commits
Tags: #clang, #llvm
Differential Revision: https://reviews.llvm.org/D64537
llvm-svn: 366272
2019-07-17 06:00:45 +08:00
|
|
|
|
2020-12-19 02:02:29 +08:00
|
|
|
if (!config->relocatable && seg->name == ".tdata") {
|
2020-11-10 09:52:39 +08:00
|
|
|
if (config->sharedMemory) {
|
|
|
|
auto *tlsSize = cast<DefinedGlobal>(WasmSym::tlsSize);
|
|
|
|
setGlobalPtr(tlsSize, seg->size);
|
[WebAssembly] Compute and export TLS block alignment
Summary:
Add immutable WASM global `__tls_align` which stores the alignment
requirements of the TLS segment.
Add `__builtin_wasm_tls_align()` intrinsic to get this alignment in Clang.
The expected usage has now changed to:
__wasm_init_tls(memalign(__builtin_wasm_tls_align(),
__builtin_wasm_tls_size()));
Reviewers: tlively, aheejin, sbc100, sunfish, alexcrichton
Reviewed By: tlively
Subscribers: dschuff, jgravelle-google, hiraditya, cfe-commits, llvm-commits
Tags: #clang, #llvm
Differential Revision: https://reviews.llvm.org/D65028
llvm-svn: 366624
2019-07-20 07:34:16 +08:00
|
|
|
|
2020-11-10 09:52:39 +08:00
|
|
|
auto *tlsAlign = cast<DefinedGlobal>(WasmSym::tlsAlign);
|
|
|
|
setGlobalPtr(tlsAlign, int64_t{1} << seg->alignment);
|
|
|
|
} else {
|
|
|
|
auto *tlsBase = cast<DefinedGlobal>(WasmSym::tlsBase);
|
|
|
|
setGlobalPtr(tlsBase, memoryPtr);
|
|
|
|
}
|
[WebAssembly] Implement thread-local storage (local-exec model)
Summary:
Thread local variables are placed inside a `.tdata` segment. Their symbols are
offsets from the start of the segment. The address of a thread local variable
is computed as `__tls_base` + the offset from the start of the segment.
`.tdata` segment is a passive segment and `memory.init` is used once per thread
to initialize the thread local storage.
`__tls_base` is a wasm global. Since each thread has its own wasm instance,
it is effectively thread local. Currently, `__tls_base` must be initialized
at thread startup, and so cannot be used with dynamic libraries.
`__tls_base` is to be initialized with a new linker-synthesized function,
`__wasm_init_tls`, which takes as an argument a block of memory to use as the
storage for thread locals. It then initializes the block of memory and sets
`__tls_base`. As `__wasm_init_tls` will handle the memory initialization,
the memory does not have to be zeroed.
To help allocating memory for thread-local storage, a new compiler intrinsic
is introduced: `__builtin_wasm_tls_size()`. This instrinsic function returns
the size of the thread-local storage for the current function.
The expected usage is to run something like the following upon thread startup:
__wasm_init_tls(malloc(__builtin_wasm_tls_size()));
Reviewers: tlively, aheejin, kripken, sbc100
Subscribers: dschuff, jgravelle-google, hiraditya, sunfish, jfb, cfe-commits, llvm-commits
Tags: #clang, #llvm
Differential Revision: https://reviews.llvm.org/D64537
llvm-svn: 366272
2019-07-17 06:00:45 +08:00
|
|
|
}
|
2020-11-10 09:52:39 +08:00
|
|
|
|
|
|
|
memoryPtr += seg->size;
|
2017-11-18 02:14:09 +08:00
|
|
|
}
|
|
|
|
|
2019-09-05 03:50:39 +08:00
|
|
|
// Make space for the memory initialization flag
|
2020-12-10 10:14:31 +08:00
|
|
|
if (config->sharedMemory && hasPassiveInitializedSegments()) {
|
2019-09-05 03:50:39 +08:00
|
|
|
memoryPtr = alignTo(memoryPtr, 4);
|
2020-12-10 10:14:31 +08:00
|
|
|
WasmSym::initMemoryFlag = symtab->addSyntheticDataSymbol(
|
|
|
|
"__wasm_init_memory_flag", WASM_SYMBOL_VISIBILITY_HIDDEN);
|
|
|
|
WasmSym::initMemoryFlag->markLive();
|
2019-09-05 03:50:39 +08:00
|
|
|
WasmSym::initMemoryFlag->setVirtualAddress(memoryPtr);
|
|
|
|
log(formatv("mem: {0,-15} offset={1,-8} size={2,-8} align={3}",
|
|
|
|
"__wasm_init_memory_flag", memoryPtr, 4, 4));
|
|
|
|
memoryPtr += 4;
|
|
|
|
}
|
|
|
|
|
2018-02-07 11:04:53 +08:00
|
|
|
if (WasmSym::dataEnd)
|
|
|
|
WasmSym::dataEnd->setVirtualAddress(memoryPtr);
|
2018-02-03 06:59:56 +08:00
|
|
|
|
2020-10-28 03:46:07 +08:00
|
|
|
uint64_t staticDataSize = memoryPtr - dataStart;
|
|
|
|
log("mem: static data = " + Twine(staticDataSize));
|
2020-12-03 09:14:57 +08:00
|
|
|
if (config->isPic)
|
2020-10-28 03:46:07 +08:00
|
|
|
out.dylinkSec->memSize = staticDataSize;
|
2018-11-16 02:15:54 +08:00
|
|
|
|
2018-05-04 01:21:53 +08:00
|
|
|
if (!config->stackFirst)
|
|
|
|
placeStack();
|
2018-02-23 13:08:53 +08:00
|
|
|
|
2020-12-03 09:14:57 +08:00
|
|
|
if (WasmSym::heapBase) {
|
|
|
|
// Set `__heap_base` to directly follow the end of the stack or global data.
|
|
|
|
// The fact that this comes last means that a malloc/brk implementation
|
|
|
|
// can grow the heap at runtime.
|
|
|
|
log("mem: heap base = " + Twine(memoryPtr));
|
2018-02-03 06:59:56 +08:00
|
|
|
WasmSym::heapBase->setVirtualAddress(memoryPtr);
|
2020-12-03 09:14:57 +08:00
|
|
|
}
|
2017-11-18 02:14:09 +08:00
|
|
|
|
2020-07-07 04:34:16 +08:00
|
|
|
uint64_t maxMemorySetting = 1ULL
|
|
|
|
<< (config->is64.getValueOr(false) ? 48 : 32);
|
2020-06-30 08:53:09 +08:00
|
|
|
|
2018-03-14 21:53:58 +08:00
|
|
|
if (config->initialMemory != 0) {
|
|
|
|
if (config->initialMemory != alignTo(config->initialMemory, WasmPageSize))
|
|
|
|
error("initial memory must be " + Twine(WasmPageSize) + "-byte aligned");
|
|
|
|
if (memoryPtr > config->initialMemory)
|
|
|
|
error("initial memory too small, " + Twine(memoryPtr) + " bytes needed");
|
2020-06-30 08:53:09 +08:00
|
|
|
if (config->initialMemory > maxMemorySetting)
|
|
|
|
error("initial memory too large, cannot be greater than " +
|
|
|
|
Twine(maxMemorySetting));
|
2020-04-04 07:18:29 +08:00
|
|
|
memoryPtr = config->initialMemory;
|
2018-03-14 21:53:58 +08:00
|
|
|
}
|
2019-05-21 17:13:09 +08:00
|
|
|
out.memorySec->numMemoryPages =
|
|
|
|
alignTo(memoryPtr, WasmPageSize) / WasmPageSize;
|
|
|
|
log("mem: total pages = " + Twine(out.memorySec->numMemoryPages));
|
2018-03-14 21:53:58 +08:00
|
|
|
|
2020-12-03 09:14:57 +08:00
|
|
|
if (config->maxMemory != 0) {
|
2018-03-14 21:53:58 +08:00
|
|
|
if (config->maxMemory != alignTo(config->maxMemory, WasmPageSize))
|
|
|
|
error("maximum memory must be " + Twine(WasmPageSize) + "-byte aligned");
|
|
|
|
if (memoryPtr > config->maxMemory)
|
|
|
|
error("maximum memory too small, " + Twine(memoryPtr) + " bytes needed");
|
2020-06-30 08:53:09 +08:00
|
|
|
if (config->maxMemory > maxMemorySetting)
|
|
|
|
error("maximum memory too large, cannot be greater than " +
|
|
|
|
Twine(maxMemorySetting));
|
2020-12-03 09:14:57 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Check max if explicitly supplied or required by shared memory
|
|
|
|
if (config->maxMemory != 0 || config->sharedMemory) {
|
|
|
|
uint64_t max = config->maxMemory;
|
|
|
|
if (max == 0) {
|
|
|
|
// If no maxMemory config was supplied but we are building with
|
|
|
|
// shared memory, we need to pick a sensible upper limit.
|
|
|
|
if (config->isPic)
|
|
|
|
max = maxMemorySetting;
|
|
|
|
else
|
|
|
|
max = alignTo(memoryPtr, WasmPageSize);
|
|
|
|
}
|
|
|
|
out.memorySec->maxMemoryPages = max / WasmPageSize;
|
2019-05-21 17:13:09 +08:00
|
|
|
log("mem: max pages = " + Twine(out.memorySec->maxMemoryPages));
|
2018-03-14 21:53:58 +08:00
|
|
|
}
|
2017-11-18 02:14:09 +08:00
|
|
|
}
|
|
|
|
|
2019-05-21 17:13:09 +08:00
|
|
|
void Writer::addSection(OutputSection *sec) {
|
|
|
|
if (!sec->isNeeded())
|
|
|
|
return;
|
|
|
|
log("addSection: " + toString(*sec));
|
|
|
|
sec->sectionIndex = outputSections.size();
|
2017-11-18 02:14:09 +08:00
|
|
|
outputSections.push_back(sec);
|
|
|
|
}
|
|
|
|
|
2019-05-23 18:06:03 +08:00
|
|
|
// If a section name is valid as a C identifier (which is rare because of
|
|
|
|
// the leading '.'), linkers are expected to define __start_<secname> and
|
|
|
|
// __stop_<secname> symbols. They are at beginning and end of the section,
|
|
|
|
// respectively. This is not requested by the ELF standard, but GNU ld and
|
|
|
|
// gold provide the feature, and used by many programs.
|
2019-07-08 18:35:08 +08:00
|
|
|
static void addStartStopSymbols(const OutputSegment *seg) {
|
|
|
|
StringRef name = seg->name;
|
|
|
|
if (!isValidCIdentifier(name))
|
2019-05-23 18:06:03 +08:00
|
|
|
return;
|
2019-07-10 03:47:32 +08:00
|
|
|
LLVM_DEBUG(dbgs() << "addStartStopSymbols: " << name << "\n");
|
2020-08-08 04:24:43 +08:00
|
|
|
uint64_t start = seg->startVA;
|
|
|
|
uint64_t stop = start + seg->size;
|
2019-07-08 18:35:08 +08:00
|
|
|
symtab->addOptionalDataSymbol(saver.save("__start_" + name), start);
|
|
|
|
symtab->addOptionalDataSymbol(saver.save("__stop_" + name), stop);
|
2019-05-23 18:06:03 +08:00
|
|
|
}
|
|
|
|
|
2019-05-21 17:13:09 +08:00
|
|
|
void Writer::addSections() {
|
|
|
|
addSection(out.dylinkSec);
|
|
|
|
addSection(out.typeSec);
|
|
|
|
addSection(out.importSec);
|
|
|
|
addSection(out.functionSec);
|
|
|
|
addSection(out.tableSec);
|
|
|
|
addSection(out.memorySec);
|
|
|
|
addSection(out.eventSec);
|
2020-03-25 10:36:13 +08:00
|
|
|
addSection(out.globalSec);
|
2019-05-21 17:13:09 +08:00
|
|
|
addSection(out.exportSec);
|
2019-09-05 03:50:39 +08:00
|
|
|
addSection(out.startSec);
|
2019-05-21 17:13:09 +08:00
|
|
|
addSection(out.elemSec);
|
|
|
|
addSection(out.dataCountSec);
|
2019-07-11 13:40:30 +08:00
|
|
|
|
2019-05-21 17:13:09 +08:00
|
|
|
addSection(make<CodeSection>(out.functionSec->inputFunctions));
|
|
|
|
addSection(make<DataSection>(segments));
|
|
|
|
|
2018-04-11 00:12:49 +08:00
|
|
|
createCustomSections();
|
2017-11-18 02:14:09 +08:00
|
|
|
|
2019-05-21 17:13:09 +08:00
|
|
|
addSection(out.linkingSec);
|
2019-05-24 21:28:27 +08:00
|
|
|
if (config->emitRelocs || config->relocatable) {
|
2018-03-05 20:33:58 +08:00
|
|
|
createRelocSections();
|
2018-02-28 07:58:03 +08:00
|
|
|
}
|
2019-01-17 10:29:41 +08:00
|
|
|
|
2019-05-21 17:13:09 +08:00
|
|
|
addSection(out.nameSec);
|
|
|
|
addSection(out.producersSec);
|
|
|
|
addSection(out.targetFeaturesSec);
|
|
|
|
}
|
2019-01-17 10:29:41 +08:00
|
|
|
|
2019-05-21 17:13:09 +08:00
|
|
|
void Writer::finalizeSections() {
|
2017-11-18 02:14:09 +08:00
|
|
|
for (OutputSection *s : outputSections) {
|
|
|
|
s->setOffset(fileSize);
|
|
|
|
s->finalizeContents();
|
|
|
|
fileSize += s->getSize();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-21 17:13:09 +08:00
|
|
|
void Writer::populateTargetFeatures() {
|
2019-05-31 05:57:23 +08:00
|
|
|
StringMap<std::string> used;
|
|
|
|
StringMap<std::string> required;
|
|
|
|
StringMap<std::string> disallowed;
|
2019-09-05 03:50:39 +08:00
|
|
|
SmallSet<std::string, 8> &allowed = out.targetFeaturesSec->features;
|
[WebAssembly] Implement thread-local storage (local-exec model)
Summary:
Thread local variables are placed inside a `.tdata` segment. Their symbols are
offsets from the start of the segment. The address of a thread local variable
is computed as `__tls_base` + the offset from the start of the segment.
`.tdata` segment is a passive segment and `memory.init` is used once per thread
to initialize the thread local storage.
`__tls_base` is a wasm global. Since each thread has its own wasm instance,
it is effectively thread local. Currently, `__tls_base` must be initialized
at thread startup, and so cannot be used with dynamic libraries.
`__tls_base` is to be initialized with a new linker-synthesized function,
`__wasm_init_tls`, which takes as an argument a block of memory to use as the
storage for thread locals. It then initializes the block of memory and sets
`__tls_base`. As `__wasm_init_tls` will handle the memory initialization,
the memory does not have to be zeroed.
To help allocating memory for thread-local storage, a new compiler intrinsic
is introduced: `__builtin_wasm_tls_size()`. This instrinsic function returns
the size of the thread-local storage for the current function.
The expected usage is to run something like the following upon thread startup:
__wasm_init_tls(malloc(__builtin_wasm_tls_size()));
Reviewers: tlively, aheejin, kripken, sbc100
Subscribers: dschuff, jgravelle-google, hiraditya, sunfish, jfb, cfe-commits, llvm-commits
Tags: #clang, #llvm
Differential Revision: https://reviews.llvm.org/D64537
llvm-svn: 366272
2019-07-17 06:00:45 +08:00
|
|
|
bool tlsUsed = false;
|
[WebAssembly] Target features section
Summary:
Implements a new target features section in assembly and object files
that records what features are used, required, and disallowed in
WebAssembly objects. The linker uses this information to ensure that
all objects participating in a link are feature-compatible and records
the set of used features in the output binary for use by optimizers
and other tools later in the toolchain.
The "atomics" feature is always required or disallowed to prevent
linking code with stripped atomics into multithreaded binaries. Other
features are marked used if they are enabled globally or on any
function in a module.
Future CLs will add linker flags for ignoring feature compatibility
checks and for specifying the set of allowed features, implement using
the presence of the "atomics" feature to control the type of memory
and segments in the linked binary, and add front-end flags for
relaxing the linkage policy for atomics.
Reviewers: aheejin, sbc100, dschuff
Subscribers: jgravelle-google, hiraditya, sunfish, mgrang, jfb, jdoerfert, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D59173
llvm-svn: 356610
2019-03-21 04:26:45 +08:00
|
|
|
|
2019-03-26 12:11:05 +08:00
|
|
|
// Only infer used features if user did not specify features
|
|
|
|
bool inferFeatures = !config->features.hasValue();
|
|
|
|
|
|
|
|
if (!inferFeatures) {
|
2019-09-05 03:50:39 +08:00
|
|
|
auto &explicitFeatures = config->features.getValue();
|
|
|
|
allowed.insert(explicitFeatures.begin(), explicitFeatures.end());
|
2019-03-26 12:11:05 +08:00
|
|
|
if (!config->checkFeatures)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
[WebAssembly] Target features section
Summary:
Implements a new target features section in assembly and object files
that records what features are used, required, and disallowed in
WebAssembly objects. The linker uses this information to ensure that
all objects participating in a link are feature-compatible and records
the set of used features in the output binary for use by optimizers
and other tools later in the toolchain.
The "atomics" feature is always required or disallowed to prevent
linking code with stripped atomics into multithreaded binaries. Other
features are marked used if they are enabled globally or on any
function in a module.
Future CLs will add linker flags for ignoring feature compatibility
checks and for specifying the set of allowed features, implement using
the presence of the "atomics" feature to control the type of memory
and segments in the linked binary, and add front-end flags for
relaxing the linkage policy for atomics.
Reviewers: aheejin, sbc100, dschuff
Subscribers: jgravelle-google, hiraditya, sunfish, mgrang, jfb, jdoerfert, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D59173
llvm-svn: 356610
2019-03-21 04:26:45 +08:00
|
|
|
// Find the sets of used, required, and disallowed features
|
|
|
|
for (ObjFile *file : symtab->objectFiles) {
|
2019-05-31 05:57:23 +08:00
|
|
|
StringRef fileName(file->getName());
|
[WebAssembly] Target features section
Summary:
Implements a new target features section in assembly and object files
that records what features are used, required, and disallowed in
WebAssembly objects. The linker uses this information to ensure that
all objects participating in a link are feature-compatible and records
the set of used features in the output binary for use by optimizers
and other tools later in the toolchain.
The "atomics" feature is always required or disallowed to prevent
linking code with stripped atomics into multithreaded binaries. Other
features are marked used if they are enabled globally or on any
function in a module.
Future CLs will add linker flags for ignoring feature compatibility
checks and for specifying the set of allowed features, implement using
the presence of the "atomics" feature to control the type of memory
and segments in the linked binary, and add front-end flags for
relaxing the linkage policy for atomics.
Reviewers: aheejin, sbc100, dschuff
Subscribers: jgravelle-google, hiraditya, sunfish, mgrang, jfb, jdoerfert, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D59173
llvm-svn: 356610
2019-03-21 04:26:45 +08:00
|
|
|
for (auto &feature : file->getWasmObj()->getTargetFeatures()) {
|
|
|
|
switch (feature.Prefix) {
|
|
|
|
case WASM_FEATURE_PREFIX_USED:
|
2020-01-29 03:23:46 +08:00
|
|
|
used.insert({feature.Name, std::string(fileName)});
|
[WebAssembly] Target features section
Summary:
Implements a new target features section in assembly and object files
that records what features are used, required, and disallowed in
WebAssembly objects. The linker uses this information to ensure that
all objects participating in a link are feature-compatible and records
the set of used features in the output binary for use by optimizers
and other tools later in the toolchain.
The "atomics" feature is always required or disallowed to prevent
linking code with stripped atomics into multithreaded binaries. Other
features are marked used if they are enabled globally or on any
function in a module.
Future CLs will add linker flags for ignoring feature compatibility
checks and for specifying the set of allowed features, implement using
the presence of the "atomics" feature to control the type of memory
and segments in the linked binary, and add front-end flags for
relaxing the linkage policy for atomics.
Reviewers: aheejin, sbc100, dschuff
Subscribers: jgravelle-google, hiraditya, sunfish, mgrang, jfb, jdoerfert, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D59173
llvm-svn: 356610
2019-03-21 04:26:45 +08:00
|
|
|
break;
|
|
|
|
case WASM_FEATURE_PREFIX_REQUIRED:
|
2020-01-29 03:23:46 +08:00
|
|
|
used.insert({feature.Name, std::string(fileName)});
|
|
|
|
required.insert({feature.Name, std::string(fileName)});
|
[WebAssembly] Target features section
Summary:
Implements a new target features section in assembly and object files
that records what features are used, required, and disallowed in
WebAssembly objects. The linker uses this information to ensure that
all objects participating in a link are feature-compatible and records
the set of used features in the output binary for use by optimizers
and other tools later in the toolchain.
The "atomics" feature is always required or disallowed to prevent
linking code with stripped atomics into multithreaded binaries. Other
features are marked used if they are enabled globally or on any
function in a module.
Future CLs will add linker flags for ignoring feature compatibility
checks and for specifying the set of allowed features, implement using
the presence of the "atomics" feature to control the type of memory
and segments in the linked binary, and add front-end flags for
relaxing the linkage policy for atomics.
Reviewers: aheejin, sbc100, dschuff
Subscribers: jgravelle-google, hiraditya, sunfish, mgrang, jfb, jdoerfert, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D59173
llvm-svn: 356610
2019-03-21 04:26:45 +08:00
|
|
|
break;
|
|
|
|
case WASM_FEATURE_PREFIX_DISALLOWED:
|
2020-01-29 03:23:46 +08:00
|
|
|
disallowed.insert({feature.Name, std::string(fileName)});
|
[WebAssembly] Target features section
Summary:
Implements a new target features section in assembly and object files
that records what features are used, required, and disallowed in
WebAssembly objects. The linker uses this information to ensure that
all objects participating in a link are feature-compatible and records
the set of used features in the output binary for use by optimizers
and other tools later in the toolchain.
The "atomics" feature is always required or disallowed to prevent
linking code with stripped atomics into multithreaded binaries. Other
features are marked used if they are enabled globally or on any
function in a module.
Future CLs will add linker flags for ignoring feature compatibility
checks and for specifying the set of allowed features, implement using
the presence of the "atomics" feature to control the type of memory
and segments in the linked binary, and add front-end flags for
relaxing the linkage policy for atomics.
Reviewers: aheejin, sbc100, dschuff
Subscribers: jgravelle-google, hiraditya, sunfish, mgrang, jfb, jdoerfert, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D59173
llvm-svn: 356610
2019-03-21 04:26:45 +08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
error("Unrecognized feature policy prefix " +
|
|
|
|
std::to_string(feature.Prefix));
|
|
|
|
}
|
|
|
|
}
|
[WebAssembly] Implement thread-local storage (local-exec model)
Summary:
Thread local variables are placed inside a `.tdata` segment. Their symbols are
offsets from the start of the segment. The address of a thread local variable
is computed as `__tls_base` + the offset from the start of the segment.
`.tdata` segment is a passive segment and `memory.init` is used once per thread
to initialize the thread local storage.
`__tls_base` is a wasm global. Since each thread has its own wasm instance,
it is effectively thread local. Currently, `__tls_base` must be initialized
at thread startup, and so cannot be used with dynamic libraries.
`__tls_base` is to be initialized with a new linker-synthesized function,
`__wasm_init_tls`, which takes as an argument a block of memory to use as the
storage for thread locals. It then initializes the block of memory and sets
`__tls_base`. As `__wasm_init_tls` will handle the memory initialization,
the memory does not have to be zeroed.
To help allocating memory for thread-local storage, a new compiler intrinsic
is introduced: `__builtin_wasm_tls_size()`. This instrinsic function returns
the size of the thread-local storage for the current function.
The expected usage is to run something like the following upon thread startup:
__wasm_init_tls(malloc(__builtin_wasm_tls_size()));
Reviewers: tlively, aheejin, kripken, sbc100
Subscribers: dschuff, jgravelle-google, hiraditya, sunfish, jfb, cfe-commits, llvm-commits
Tags: #clang, #llvm
Differential Revision: https://reviews.llvm.org/D64537
llvm-svn: 366272
2019-07-17 06:00:45 +08:00
|
|
|
|
2019-09-05 03:50:39 +08:00
|
|
|
// Find TLS data segments
|
|
|
|
auto isTLS = [](InputSegment *segment) {
|
[WebAssembly] Implement thread-local storage (local-exec model)
Summary:
Thread local variables are placed inside a `.tdata` segment. Their symbols are
offsets from the start of the segment. The address of a thread local variable
is computed as `__tls_base` + the offset from the start of the segment.
`.tdata` segment is a passive segment and `memory.init` is used once per thread
to initialize the thread local storage.
`__tls_base` is a wasm global. Since each thread has its own wasm instance,
it is effectively thread local. Currently, `__tls_base` must be initialized
at thread startup, and so cannot be used with dynamic libraries.
`__tls_base` is to be initialized with a new linker-synthesized function,
`__wasm_init_tls`, which takes as an argument a block of memory to use as the
storage for thread locals. It then initializes the block of memory and sets
`__tls_base`. As `__wasm_init_tls` will handle the memory initialization,
the memory does not have to be zeroed.
To help allocating memory for thread-local storage, a new compiler intrinsic
is introduced: `__builtin_wasm_tls_size()`. This instrinsic function returns
the size of the thread-local storage for the current function.
The expected usage is to run something like the following upon thread startup:
__wasm_init_tls(malloc(__builtin_wasm_tls_size()));
Reviewers: tlively, aheejin, kripken, sbc100
Subscribers: dschuff, jgravelle-google, hiraditya, sunfish, jfb, cfe-commits, llvm-commits
Tags: #clang, #llvm
Differential Revision: https://reviews.llvm.org/D64537
llvm-svn: 366272
2019-07-17 06:00:45 +08:00
|
|
|
StringRef name = segment->getName();
|
2019-09-05 03:50:39 +08:00
|
|
|
return segment->live &&
|
|
|
|
(name.startswith(".tdata") || name.startswith(".tbss"));
|
|
|
|
};
|
|
|
|
tlsUsed = tlsUsed ||
|
|
|
|
std::any_of(file->segments.begin(), file->segments.end(), isTLS);
|
[WebAssembly] Target features section
Summary:
Implements a new target features section in assembly and object files
that records what features are used, required, and disallowed in
WebAssembly objects. The linker uses this information to ensure that
all objects participating in a link are feature-compatible and records
the set of used features in the output binary for use by optimizers
and other tools later in the toolchain.
The "atomics" feature is always required or disallowed to prevent
linking code with stripped atomics into multithreaded binaries. Other
features are marked used if they are enabled globally or on any
function in a module.
Future CLs will add linker flags for ignoring feature compatibility
checks and for specifying the set of allowed features, implement using
the presence of the "atomics" feature to control the type of memory
and segments in the linked binary, and add front-end flags for
relaxing the linkage policy for atomics.
Reviewers: aheejin, sbc100, dschuff
Subscribers: jgravelle-google, hiraditya, sunfish, mgrang, jfb, jdoerfert, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D59173
llvm-svn: 356610
2019-03-21 04:26:45 +08:00
|
|
|
}
|
|
|
|
|
2019-03-26 12:11:05 +08:00
|
|
|
if (inferFeatures)
|
2020-01-29 03:23:46 +08:00
|
|
|
for (const auto &key : used.keys())
|
|
|
|
allowed.insert(std::string(key));
|
2019-05-31 05:57:23 +08:00
|
|
|
|
2019-03-26 12:11:05 +08:00
|
|
|
if (!config->checkFeatures)
|
|
|
|
return;
|
|
|
|
|
2020-07-31 08:44:32 +08:00
|
|
|
if (!config->relocatable && allowed.count("mutable-globals") == 0) {
|
2020-09-15 09:28:26 +08:00
|
|
|
for (const Symbol *sym : out.importSec->importedSymbols) {
|
2020-07-24 06:06:21 +08:00
|
|
|
if (auto *global = dyn_cast<GlobalSymbol>(sym)) {
|
|
|
|
if (global->getGlobalType()->Mutable) {
|
2020-09-15 09:28:26 +08:00
|
|
|
error(Twine("mutable global imported but 'mutable-globals' feature "
|
|
|
|
"not present in inputs: `") +
|
|
|
|
toString(*sym) + "`. Use --no-check-features to suppress.");
|
2020-07-24 06:06:21 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-09-15 09:28:26 +08:00
|
|
|
for (const Symbol *sym : out.exportSec->exportedSymbols) {
|
2020-09-19 07:10:39 +08:00
|
|
|
if (isa<GlobalSymbol>(sym)) {
|
2020-09-15 09:28:26 +08:00
|
|
|
error(Twine("mutable global exported but 'mutable-globals' feature "
|
|
|
|
"not present in inputs: `") +
|
|
|
|
toString(*sym) + "`. Use --no-check-features to suppress.");
|
|
|
|
}
|
|
|
|
}
|
2020-07-24 06:06:21 +08:00
|
|
|
}
|
|
|
|
|
[WebAssembly] Disallow 'shared-mem' rather than 'atomics'
Summary:
The WebAssembly backend automatically lowers atomic operations and TLS
to nonatomic operations and non-TLS data when either are present and
the atomics or bulk-memory features are not present, respectively. The
resulting object is no longer thread-safe, so the linker has to be
told not to allow it to be linked into a module with shared
memory. This was previously done by disallowing the 'atomics' feature,
which prevented any objct with its atomic operations or TLS removed
from being linked with any object containing atomics or TLS, and
therefore preventing it from being linked into a module with shared
memory since shared memory requires atomics.
However, as of https://github.com/WebAssembly/threads/issues/144, the
validation rules are relaxed to allow atomic operations to validate
with unshared memories, which makes it perfectly safe to link an
object with stripped atomics and TLS with another object that still
contains TLS and atomics as long as the resulting module has an
unshared memory. To allow this kind of link, this patch disallows a
pseudo-feature 'shared-mem' rather than 'atomics' to communicate to
the linker that the object is not thread-safe. This means that the
'atomics' feature is available to accurately reflect whether or not an
object has atomics enabled.
As a drive-by tweak, this change also requires that bulk-memory be
enabled in addition to atomics in order to use shared memory. This is
because initializing shared memories requires bulk-memory operations.
Reviewers: aheejin, sbc100
Subscribers: dschuff, jgravelle-google, hiraditya, sunfish, jfb, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D79542
2020-05-07 10:33:24 +08:00
|
|
|
if (config->sharedMemory) {
|
|
|
|
if (disallowed.count("shared-mem"))
|
|
|
|
error("--shared-memory is disallowed by " + disallowed["shared-mem"] +
|
|
|
|
" because it was not compiled with 'atomics' or 'bulk-memory' "
|
|
|
|
"features.");
|
|
|
|
|
|
|
|
for (auto feature : {"atomics", "bulk-memory"})
|
|
|
|
if (!allowed.count(feature))
|
|
|
|
error(StringRef("'") + feature +
|
|
|
|
"' feature must be used in order to use shared memory");
|
|
|
|
}
|
2019-07-04 06:04:54 +08:00
|
|
|
|
[WebAssembly] Disallow 'shared-mem' rather than 'atomics'
Summary:
The WebAssembly backend automatically lowers atomic operations and TLS
to nonatomic operations and non-TLS data when either are present and
the atomics or bulk-memory features are not present, respectively. The
resulting object is no longer thread-safe, so the linker has to be
told not to allow it to be linked into a module with shared
memory. This was previously done by disallowing the 'atomics' feature,
which prevented any objct with its atomic operations or TLS removed
from being linked with any object containing atomics or TLS, and
therefore preventing it from being linked into a module with shared
memory since shared memory requires atomics.
However, as of https://github.com/WebAssembly/threads/issues/144, the
validation rules are relaxed to allow atomic operations to validate
with unshared memories, which makes it perfectly safe to link an
object with stripped atomics and TLS with another object that still
contains TLS and atomics as long as the resulting module has an
unshared memory. To allow this kind of link, this patch disallows a
pseudo-feature 'shared-mem' rather than 'atomics' to communicate to
the linker that the object is not thread-safe. This means that the
'atomics' feature is available to accurately reflect whether or not an
object has atomics enabled.
As a drive-by tweak, this change also requires that bulk-memory be
enabled in addition to atomics in order to use shared memory. This is
because initializing shared memories requires bulk-memory operations.
Reviewers: aheejin, sbc100
Subscribers: dschuff, jgravelle-google, hiraditya, sunfish, jfb, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D79542
2020-05-07 10:33:24 +08:00
|
|
|
if (tlsUsed) {
|
|
|
|
for (auto feature : {"atomics", "bulk-memory"})
|
|
|
|
if (!allowed.count(feature))
|
|
|
|
error(StringRef("'") + feature +
|
|
|
|
"' feature must be used in order to use thread-local storage");
|
|
|
|
}
|
[WebAssembly] Implement thread-local storage (local-exec model)
Summary:
Thread local variables are placed inside a `.tdata` segment. Their symbols are
offsets from the start of the segment. The address of a thread local variable
is computed as `__tls_base` + the offset from the start of the segment.
`.tdata` segment is a passive segment and `memory.init` is used once per thread
to initialize the thread local storage.
`__tls_base` is a wasm global. Since each thread has its own wasm instance,
it is effectively thread local. Currently, `__tls_base` must be initialized
at thread startup, and so cannot be used with dynamic libraries.
`__tls_base` is to be initialized with a new linker-synthesized function,
`__wasm_init_tls`, which takes as an argument a block of memory to use as the
storage for thread locals. It then initializes the block of memory and sets
`__tls_base`. As `__wasm_init_tls` will handle the memory initialization,
the memory does not have to be zeroed.
To help allocating memory for thread-local storage, a new compiler intrinsic
is introduced: `__builtin_wasm_tls_size()`. This instrinsic function returns
the size of the thread-local storage for the current function.
The expected usage is to run something like the following upon thread startup:
__wasm_init_tls(malloc(__builtin_wasm_tls_size()));
Reviewers: tlively, aheejin, kripken, sbc100
Subscribers: dschuff, jgravelle-google, hiraditya, sunfish, jfb, cfe-commits, llvm-commits
Tags: #clang, #llvm
Differential Revision: https://reviews.llvm.org/D64537
llvm-svn: 366272
2019-07-17 06:00:45 +08:00
|
|
|
|
2019-03-26 12:11:05 +08:00
|
|
|
// Validate that used features are allowed in output
|
|
|
|
if (!inferFeatures) {
|
2019-05-31 05:57:23 +08:00
|
|
|
for (auto &feature : used.keys()) {
|
2020-01-29 03:23:46 +08:00
|
|
|
if (!allowed.count(std::string(feature)))
|
2019-05-31 05:57:23 +08:00
|
|
|
error(Twine("Target feature '") + feature + "' used by " +
|
|
|
|
used[feature] + " is not allowed.");
|
2019-03-26 12:11:05 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
[WebAssembly] Target features section
Summary:
Implements a new target features section in assembly and object files
that records what features are used, required, and disallowed in
WebAssembly objects. The linker uses this information to ensure that
all objects participating in a link are feature-compatible and records
the set of used features in the output binary for use by optimizers
and other tools later in the toolchain.
The "atomics" feature is always required or disallowed to prevent
linking code with stripped atomics into multithreaded binaries. Other
features are marked used if they are enabled globally or on any
function in a module.
Future CLs will add linker flags for ignoring feature compatibility
checks and for specifying the set of allowed features, implement using
the presence of the "atomics" feature to control the type of memory
and segments in the linked binary, and add front-end flags for
relaxing the linkage policy for atomics.
Reviewers: aheejin, sbc100, dschuff
Subscribers: jgravelle-google, hiraditya, sunfish, mgrang, jfb, jdoerfert, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D59173
llvm-svn: 356610
2019-03-21 04:26:45 +08:00
|
|
|
// Validate the required and disallowed constraints for each file
|
|
|
|
for (ObjFile *file : symtab->objectFiles) {
|
2019-05-31 05:57:23 +08:00
|
|
|
StringRef fileName(file->getName());
|
[WebAssembly] Target features section
Summary:
Implements a new target features section in assembly and object files
that records what features are used, required, and disallowed in
WebAssembly objects. The linker uses this information to ensure that
all objects participating in a link are feature-compatible and records
the set of used features in the output binary for use by optimizers
and other tools later in the toolchain.
The "atomics" feature is always required or disallowed to prevent
linking code with stripped atomics into multithreaded binaries. Other
features are marked used if they are enabled globally or on any
function in a module.
Future CLs will add linker flags for ignoring feature compatibility
checks and for specifying the set of allowed features, implement using
the presence of the "atomics" feature to control the type of memory
and segments in the linked binary, and add front-end flags for
relaxing the linkage policy for atomics.
Reviewers: aheejin, sbc100, dschuff
Subscribers: jgravelle-google, hiraditya, sunfish, mgrang, jfb, jdoerfert, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D59173
llvm-svn: 356610
2019-03-21 04:26:45 +08:00
|
|
|
SmallSet<std::string, 8> objectFeatures;
|
|
|
|
for (auto &feature : file->getWasmObj()->getTargetFeatures()) {
|
|
|
|
if (feature.Prefix == WASM_FEATURE_PREFIX_DISALLOWED)
|
|
|
|
continue;
|
|
|
|
objectFeatures.insert(feature.Name);
|
|
|
|
if (disallowed.count(feature.Name))
|
2019-05-31 05:57:23 +08:00
|
|
|
error(Twine("Target feature '") + feature.Name + "' used in " +
|
|
|
|
fileName + " is disallowed by " + disallowed[feature.Name] +
|
|
|
|
". Use --no-check-features to suppress.");
|
[WebAssembly] Target features section
Summary:
Implements a new target features section in assembly and object files
that records what features are used, required, and disallowed in
WebAssembly objects. The linker uses this information to ensure that
all objects participating in a link are feature-compatible and records
the set of used features in the output binary for use by optimizers
and other tools later in the toolchain.
The "atomics" feature is always required or disallowed to prevent
linking code with stripped atomics into multithreaded binaries. Other
features are marked used if they are enabled globally or on any
function in a module.
Future CLs will add linker flags for ignoring feature compatibility
checks and for specifying the set of allowed features, implement using
the presence of the "atomics" feature to control the type of memory
and segments in the linked binary, and add front-end flags for
relaxing the linkage policy for atomics.
Reviewers: aheejin, sbc100, dschuff
Subscribers: jgravelle-google, hiraditya, sunfish, mgrang, jfb, jdoerfert, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D59173
llvm-svn: 356610
2019-03-21 04:26:45 +08:00
|
|
|
}
|
2019-05-31 05:57:23 +08:00
|
|
|
for (auto &feature : required.keys()) {
|
2020-01-29 03:23:46 +08:00
|
|
|
if (!objectFeatures.count(std::string(feature)))
|
2019-05-31 05:57:23 +08:00
|
|
|
error(Twine("Missing target feature '") + feature + "' in " + fileName +
|
|
|
|
", required by " + required[feature] +
|
|
|
|
". Use --no-check-features to suppress.");
|
[WebAssembly] Target features section
Summary:
Implements a new target features section in assembly and object files
that records what features are used, required, and disallowed in
WebAssembly objects. The linker uses this information to ensure that
all objects participating in a link are feature-compatible and records
the set of used features in the output binary for use by optimizers
and other tools later in the toolchain.
The "atomics" feature is always required or disallowed to prevent
linking code with stripped atomics into multithreaded binaries. Other
features are marked used if they are enabled globally or on any
function in a module.
Future CLs will add linker flags for ignoring feature compatibility
checks and for specifying the set of allowed features, implement using
the presence of the "atomics" feature to control the type of memory
and segments in the linked binary, and add front-end flags for
relaxing the linkage policy for atomics.
Reviewers: aheejin, sbc100, dschuff
Subscribers: jgravelle-google, hiraditya, sunfish, mgrang, jfb, jdoerfert, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D59173
llvm-svn: 356610
2019-03-21 04:26:45 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-02 00:14:59 +08:00
|
|
|
static bool shouldImport(Symbol *sym) {
|
|
|
|
// We don't generate imports for data symbols. They however can be imported
|
|
|
|
// as GOT entries.
|
|
|
|
if (isa<DataSymbol>(sym))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (config->relocatable ||
|
|
|
|
config->unresolvedSymbols == UnresolvedPolicy::ImportFuncs)
|
|
|
|
return true;
|
|
|
|
if (config->allowUndefinedSymbols.count(sym->getName()) != 0)
|
|
|
|
return true;
|
|
|
|
if (auto *g = dyn_cast<UndefinedGlobal>(sym))
|
|
|
|
return g->importName.hasValue();
|
|
|
|
if (auto *f = dyn_cast<UndefinedFunction>(sym))
|
|
|
|
return f->importName.hasValue();
|
2021-01-05 19:08:58 +08:00
|
|
|
if (auto *t = dyn_cast<UndefinedTable>(sym))
|
|
|
|
return t->importName.hasValue();
|
2020-05-02 00:14:59 +08:00
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-11-18 02:14:09 +08:00
|
|
|
void Writer::calculateImports() {
|
2017-12-16 03:23:49 +08:00
|
|
|
for (Symbol *sym : symtab->getSymbols()) {
|
2018-02-23 13:08:53 +08:00
|
|
|
if (!sym->isUndefined())
|
|
|
|
continue;
|
|
|
|
if (sym->isWeak() && !config->relocatable)
|
2017-12-16 03:23:49 +08:00
|
|
|
continue;
|
2018-04-21 01:18:06 +08:00
|
|
|
if (!sym->isLive())
|
|
|
|
continue;
|
2018-05-31 02:07:52 +08:00
|
|
|
if (!sym->isUsedInRegularObj)
|
|
|
|
continue;
|
2020-05-02 00:14:59 +08:00
|
|
|
if (shouldImport(sym)) {
|
|
|
|
LLVM_DEBUG(dbgs() << "import: " << sym->getName() << "\n");
|
|
|
|
out.importSec->addImport(sym);
|
|
|
|
}
|
2017-11-18 02:14:09 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-19 07:40:49 +08:00
|
|
|
void Writer::calculateExports() {
|
2018-02-23 13:08:53 +08:00
|
|
|
if (config->relocatable)
|
|
|
|
return;
|
|
|
|
|
2018-05-11 02:10:34 +08:00
|
|
|
if (!config->relocatable && !config->importMemory)
|
2019-05-21 17:13:09 +08:00
|
|
|
out.exportSec->exports.push_back(
|
|
|
|
WasmExport{"memory", WASM_EXTERNAL_MEMORY, 0});
|
2018-05-11 02:10:34 +08:00
|
|
|
|
|
|
|
if (!config->relocatable && config->exportTable)
|
2019-05-21 17:13:09 +08:00
|
|
|
out.exportSec->exports.push_back(
|
|
|
|
WasmExport{functionTableName, WASM_EXTERNAL_TABLE, 0});
|
2018-05-11 02:10:34 +08:00
|
|
|
|
2019-09-25 04:52:12 +08:00
|
|
|
unsigned globalIndex =
|
|
|
|
out.importSec->getNumImportedGlobals() + out.globalSec->numGlobals();
|
2018-05-11 02:10:34 +08:00
|
|
|
|
2018-03-01 17:38:02 +08:00
|
|
|
for (Symbol *sym : symtab->getSymbols()) {
|
2018-06-29 01:04:58 +08:00
|
|
|
if (!sym->isExported())
|
2018-03-01 17:38:02 +08:00
|
|
|
continue;
|
2018-02-23 13:08:53 +08:00
|
|
|
if (!sym->isLive())
|
2018-03-01 17:38:02 +08:00
|
|
|
continue;
|
2018-02-23 13:08:53 +08:00
|
|
|
|
2018-05-11 02:10:34 +08:00
|
|
|
StringRef name = sym->getName();
|
|
|
|
WasmExport export_;
|
|
|
|
if (auto *f = dyn_cast<DefinedFunction>(sym)) {
|
2019-12-21 13:44:24 +08:00
|
|
|
if (Optional<StringRef> exportName = f->function->getExportName()) {
|
|
|
|
name = *exportName;
|
2019-11-06 02:15:56 +08:00
|
|
|
}
|
2018-05-11 02:10:34 +08:00
|
|
|
export_ = {name, WASM_EXTERNAL_FUNCTION, f->getFunctionIndex()};
|
|
|
|
} else if (auto *g = dyn_cast<DefinedGlobal>(sym)) {
|
2020-07-31 08:44:32 +08:00
|
|
|
if (g->getGlobalType()->Mutable && !g->getFile() && !g->forceExport) {
|
|
|
|
// Avoid exporting mutable globals are linker synthesized (e.g.
|
|
|
|
// __stack_pointer or __tls_base) unless they are explicitly exported
|
|
|
|
// from the command line.
|
|
|
|
// Without this check `--export-all` would cause any program using the
|
|
|
|
// stack pointer to export a mutable global even if none of the input
|
|
|
|
// files were built with the `mutable-globals` feature.
|
2018-06-07 09:27:07 +08:00
|
|
|
continue;
|
|
|
|
}
|
2018-05-11 02:10:34 +08:00
|
|
|
export_ = {name, WASM_EXTERNAL_GLOBAL, g->getGlobalIndex()};
|
2018-12-08 14:17:43 +08:00
|
|
|
} else if (auto *e = dyn_cast<DefinedEvent>(sym)) {
|
|
|
|
export_ = {name, WASM_EXTERNAL_EVENT, e->getEventIndex()};
|
2021-01-05 19:08:58 +08:00
|
|
|
} else if (auto *d = dyn_cast<DefinedData>(sym)) {
|
2019-09-25 04:52:12 +08:00
|
|
|
out.globalSec->dataAddressGlobals.push_back(d);
|
|
|
|
export_ = {name, WASM_EXTERNAL_GLOBAL, globalIndex++};
|
2021-01-05 19:08:58 +08:00
|
|
|
} else {
|
|
|
|
auto *t = cast<DefinedTable>(sym);
|
|
|
|
export_ = {name, WASM_EXTERNAL_TABLE, t->getTableNumber()};
|
2018-05-11 02:10:34 +08:00
|
|
|
}
|
|
|
|
|
2018-05-15 21:36:20 +08:00
|
|
|
LLVM_DEBUG(dbgs() << "Export: " << name << "\n");
|
2019-05-21 17:13:09 +08:00
|
|
|
out.exportSec->exports.push_back(export_);
|
2020-09-15 09:28:26 +08:00
|
|
|
out.exportSec->exportedSymbols.push_back(sym);
|
2018-03-01 17:38:02 +08:00
|
|
|
}
|
2018-02-23 13:08:53 +08:00
|
|
|
}
|
|
|
|
|
2019-05-21 17:13:09 +08:00
|
|
|
void Writer::populateSymtab() {
|
2019-05-24 21:28:27 +08:00
|
|
|
if (!config->relocatable && !config->emitRelocs)
|
2018-02-23 13:08:53 +08:00
|
|
|
return;
|
2018-01-19 07:40:49 +08:00
|
|
|
|
2019-01-31 02:55:15 +08:00
|
|
|
for (Symbol *sym : symtab->getSymbols())
|
2019-05-24 21:28:27 +08:00
|
|
|
if (sym->isUsedInRegularObj && sym->isLive())
|
2019-05-21 17:13:09 +08:00
|
|
|
out.linkingSec->addToSymtab(sym);
|
2019-01-31 02:55:15 +08:00
|
|
|
|
|
|
|
for (ObjFile *file : symtab->objectFiles) {
|
|
|
|
LLVM_DEBUG(dbgs() << "Local symtab entries: " << file->getName() << "\n");
|
|
|
|
for (Symbol *sym : file->getSymbols())
|
2019-05-24 21:28:27 +08:00
|
|
|
if (sym->isLocal() && !isa<SectionSymbol>(sym) && sym->isLive())
|
2019-05-21 17:13:09 +08:00
|
|
|
out.linkingSec->addToSymtab(sym);
|
2018-01-11 03:18:22 +08:00
|
|
|
}
|
2017-11-30 09:40:08 +08:00
|
|
|
}
|
|
|
|
|
2017-11-18 02:14:09 +08:00
|
|
|
void Writer::calculateTypes() {
|
2018-02-01 07:48:14 +08:00
|
|
|
// The output type section is the union of the following sets:
|
|
|
|
// 1. Any signature used in the TYPE relocation
|
|
|
|
// 2. The signatures of all imported functions
|
|
|
|
// 3. The signatures of all defined functions
|
2018-12-08 14:17:43 +08:00
|
|
|
// 4. The signatures of all imported events
|
|
|
|
// 5. The signatures of all defined events
|
2018-02-01 07:48:14 +08:00
|
|
|
|
2017-11-18 02:14:09 +08:00
|
|
|
for (ObjFile *file : symtab->objectFiles) {
|
2018-02-01 07:48:14 +08:00
|
|
|
ArrayRef<WasmSignature> types = file->getWasmObj()->types();
|
|
|
|
for (uint32_t i = 0; i < types.size(); i++)
|
|
|
|
if (file->typeIsUsed[i])
|
2019-05-21 17:13:09 +08:00
|
|
|
file->typeMap[i] = out.typeSec->registerType(types[i]);
|
2017-11-18 02:14:09 +08:00
|
|
|
}
|
2018-01-13 02:35:13 +08:00
|
|
|
|
2019-05-21 17:13:09 +08:00
|
|
|
for (const Symbol *sym : out.importSec->importedSymbols) {
|
2018-02-23 13:08:53 +08:00
|
|
|
if (auto *f = dyn_cast<FunctionSymbol>(sym))
|
2019-05-21 17:13:09 +08:00
|
|
|
out.typeSec->registerType(*f->signature);
|
2018-12-08 14:17:43 +08:00
|
|
|
else if (auto *e = dyn_cast<EventSymbol>(sym))
|
2019-05-21 17:13:09 +08:00
|
|
|
out.typeSec->registerType(*e->signature);
|
2018-12-08 14:17:43 +08:00
|
|
|
}
|
2018-02-01 07:48:14 +08:00
|
|
|
|
2019-05-21 17:13:09 +08:00
|
|
|
for (const InputFunction *f : out.functionSec->inputFunctions)
|
|
|
|
out.typeSec->registerType(f->signature);
|
2017-11-18 02:14:09 +08:00
|
|
|
|
2019-05-21 17:13:09 +08:00
|
|
|
for (const InputEvent *e : out.eventSec->inputEvents)
|
|
|
|
out.typeSec->registerType(e->signature);
|
2019-05-10 09:52:08 +08:00
|
|
|
}
|
|
|
|
|
2020-10-01 08:21:57 +08:00
|
|
|
// In a command-style link, create a wrapper for each exported symbol
|
|
|
|
// which calls the constructors and destructors.
|
|
|
|
void Writer::createCommandExportWrappers() {
|
|
|
|
// This logic doesn't currently support Emscripten-style PIC mode.
|
|
|
|
assert(!config->isPic);
|
|
|
|
|
|
|
|
// If there are no ctors and there's no libc `__wasm_call_dtors` to
|
|
|
|
// call, don't wrap the exports.
|
|
|
|
if (initFunctions.empty() && WasmSym::callDtors == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
std::vector<DefinedFunction *> toWrap;
|
|
|
|
|
|
|
|
for (Symbol *sym : symtab->getSymbols())
|
|
|
|
if (sym->isExported())
|
|
|
|
if (auto *f = dyn_cast<DefinedFunction>(sym))
|
|
|
|
toWrap.push_back(f);
|
|
|
|
|
|
|
|
for (auto *f : toWrap) {
|
|
|
|
auto funcNameStr = (f->getName() + ".command_export").str();
|
|
|
|
commandExportWrapperNames.push_back(funcNameStr);
|
|
|
|
const std::string &funcName = commandExportWrapperNames.back();
|
|
|
|
|
|
|
|
auto func = make<SyntheticFunction>(*f->getSignature(), funcName);
|
|
|
|
if (f->function->getExportName().hasValue())
|
|
|
|
func->setExportName(f->function->getExportName()->str());
|
|
|
|
else
|
|
|
|
func->setExportName(f->getName().str());
|
|
|
|
|
|
|
|
DefinedFunction *def =
|
|
|
|
symtab->addSyntheticFunction(funcName, f->flags, func);
|
|
|
|
def->markLive();
|
|
|
|
|
|
|
|
def->flags |= WASM_SYMBOL_EXPORTED;
|
|
|
|
def->flags &= ~WASM_SYMBOL_VISIBILITY_HIDDEN;
|
|
|
|
def->forceExport = f->forceExport;
|
|
|
|
|
|
|
|
f->flags |= WASM_SYMBOL_VISIBILITY_HIDDEN;
|
|
|
|
f->flags &= ~WASM_SYMBOL_EXPORTED;
|
|
|
|
f->forceExport = false;
|
|
|
|
|
|
|
|
out.functionSec->addFunction(func);
|
|
|
|
|
|
|
|
createCommandExportWrapper(f->getFunctionIndex(), def);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-21 17:13:09 +08:00
|
|
|
static void scanRelocations() {
|
|
|
|
for (ObjFile *file : symtab->objectFiles) {
|
|
|
|
LLVM_DEBUG(dbgs() << "scanRelocations: " << file->getName() << "\n");
|
|
|
|
for (InputChunk *chunk : file->functions)
|
|
|
|
scanRelocations(chunk);
|
|
|
|
for (InputChunk *chunk : file->segments)
|
|
|
|
scanRelocations(chunk);
|
|
|
|
for (auto &p : file->customSections)
|
|
|
|
scanRelocations(p);
|
2019-03-16 09:18:12 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-10 07:56:44 +08:00
|
|
|
void Writer::assignIndexes() {
|
2019-05-23 17:41:03 +08:00
|
|
|
// Seal the import section, since other index spaces such as function and
|
|
|
|
// global are effected by the number of imports.
|
|
|
|
out.importSec->seal();
|
2018-03-10 00:43:05 +08:00
|
|
|
|
2018-03-12 23:44:07 +08:00
|
|
|
for (InputFunction *func : symtab->syntheticFunctions)
|
2019-05-21 17:13:09 +08:00
|
|
|
out.functionSec->addFunction(func);
|
2018-03-12 23:44:07 +08:00
|
|
|
|
2018-01-09 07:39:11 +08:00
|
|
|
for (ObjFile *file : symtab->objectFiles) {
|
2018-05-15 21:36:20 +08:00
|
|
|
LLVM_DEBUG(dbgs() << "Functions: " << file->getName() << "\n");
|
2018-03-10 00:43:05 +08:00
|
|
|
for (InputFunction *func : file->functions)
|
2019-05-21 17:13:09 +08:00
|
|
|
out.functionSec->addFunction(func);
|
2017-11-18 02:14:09 +08:00
|
|
|
}
|
2018-02-23 13:08:53 +08:00
|
|
|
|
2018-03-10 00:43:05 +08:00
|
|
|
for (InputGlobal *global : symtab->syntheticGlobals)
|
2019-05-21 17:13:09 +08:00
|
|
|
out.globalSec->addGlobal(global);
|
2018-02-23 13:08:53 +08:00
|
|
|
|
|
|
|
for (ObjFile *file : symtab->objectFiles) {
|
2018-05-15 21:36:20 +08:00
|
|
|
LLVM_DEBUG(dbgs() << "Globals: " << file->getName() << "\n");
|
2018-02-23 13:08:53 +08:00
|
|
|
for (InputGlobal *global : file->globals)
|
2019-05-21 17:13:09 +08:00
|
|
|
out.globalSec->addGlobal(global);
|
2018-02-23 13:08:53 +08:00
|
|
|
}
|
2018-12-08 14:17:43 +08:00
|
|
|
|
|
|
|
for (ObjFile *file : symtab->objectFiles) {
|
|
|
|
LLVM_DEBUG(dbgs() << "Events: " << file->getName() << "\n");
|
|
|
|
for (InputEvent *event : file->events)
|
2019-05-21 17:13:09 +08:00
|
|
|
out.eventSec->addEvent(event);
|
2018-12-08 14:17:43 +08:00
|
|
|
}
|
2019-08-14 01:02:02 +08:00
|
|
|
|
2021-01-05 19:08:58 +08:00
|
|
|
for (ObjFile *file : symtab->objectFiles) {
|
|
|
|
LLVM_DEBUG(dbgs() << "Tables: " << file->getName() << "\n");
|
|
|
|
for (InputTable *table : file->tables)
|
|
|
|
out.tableSec->addTable(table);
|
|
|
|
}
|
|
|
|
|
2019-08-14 01:02:02 +08:00
|
|
|
out.globalSec->assignIndexes();
|
2017-11-18 02:14:09 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static StringRef getOutputDataSegmentName(StringRef name) {
|
[WebAssembly] Implement thread-local storage (local-exec model)
Summary:
Thread local variables are placed inside a `.tdata` segment. Their symbols are
offsets from the start of the segment. The address of a thread local variable
is computed as `__tls_base` + the offset from the start of the segment.
`.tdata` segment is a passive segment and `memory.init` is used once per thread
to initialize the thread local storage.
`__tls_base` is a wasm global. Since each thread has its own wasm instance,
it is effectively thread local. Currently, `__tls_base` must be initialized
at thread startup, and so cannot be used with dynamic libraries.
`__tls_base` is to be initialized with a new linker-synthesized function,
`__wasm_init_tls`, which takes as an argument a block of memory to use as the
storage for thread locals. It then initializes the block of memory and sets
`__tls_base`. As `__wasm_init_tls` will handle the memory initialization,
the memory does not have to be zeroed.
To help allocating memory for thread-local storage, a new compiler intrinsic
is introduced: `__builtin_wasm_tls_size()`. This instrinsic function returns
the size of the thread-local storage for the current function.
The expected usage is to run something like the following upon thread startup:
__wasm_init_tls(malloc(__builtin_wasm_tls_size()));
Reviewers: tlively, aheejin, kripken, sbc100
Subscribers: dschuff, jgravelle-google, hiraditya, sunfish, jfb, cfe-commits, llvm-commits
Tags: #clang, #llvm
Differential Revision: https://reviews.llvm.org/D64537
llvm-svn: 366272
2019-07-17 06:00:45 +08:00
|
|
|
// We only support one thread-local segment, so we must merge the segments
|
|
|
|
// despite --no-merge-data-segments.
|
|
|
|
// We also need to merge .tbss into .tdata so they share the same offsets.
|
|
|
|
if (name.startswith(".tdata") || name.startswith(".tbss"))
|
|
|
|
return ".tdata";
|
2020-11-11 09:46:52 +08:00
|
|
|
// With PIC code we currently only support a single data segment since
|
|
|
|
// we only have a single __memory_base to use as our base address.
|
|
|
|
if (config->isPic)
|
|
|
|
return ".data";
|
[WebAssembly] Add a flag to control merging data segments
Merging data segments produces smaller code sizes because each segment
has some boilerplate. Therefore, merging data segments is generally the
right approach, especially with wasm where binaries are typically
delivered over the network.
However, when analyzing wasm binaries, it can be helpful to get a
conservative picture of which functions are using which data
segments[0]. Perhaps there is a large data segment that you didn't
expect to be included in the wasm, introduced by some library you're
using, and you'd like to know which library it was. In this scenario,
merging data segments only makes the analysis worse.
Alternatively, perhaps you will remove some dead functions by-hand[1]
that can't be statically proven dead by the compiler or lld, and
removing these functions might make some data garbage collect-able, and
you'd like to run `--gc-sections` again so that this now-unused data can
be collected. If the segments were originally merged, then a single use
of the merged data segment will entrench all of the data.
[0] https://github.com/rustwasm/twiggy
[1] https://github.com/fitzgen/wasm-snip
Patch by Nick Fitzgerald!
Differential Revision: https://reviews.llvm.org/D46417
llvm-svn: 332013
2018-05-11 02:23:51 +08:00
|
|
|
if (!config->mergeDataSegments)
|
2017-11-18 02:14:09 +08:00
|
|
|
return name;
|
2018-02-28 08:57:28 +08:00
|
|
|
if (name.startswith(".text."))
|
|
|
|
return ".text";
|
|
|
|
if (name.startswith(".data."))
|
|
|
|
return ".data";
|
|
|
|
if (name.startswith(".bss."))
|
|
|
|
return ".bss";
|
2018-08-09 02:02:55 +08:00
|
|
|
if (name.startswith(".rodata."))
|
|
|
|
return ".rodata";
|
2017-11-18 02:14:09 +08:00
|
|
|
return name;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Writer::createOutputSegments() {
|
|
|
|
for (ObjFile *file : symtab->objectFiles) {
|
|
|
|
for (InputSegment *segment : file->segments) {
|
2018-02-14 04:29:38 +08:00
|
|
|
if (!segment->live)
|
2018-01-13 06:25:17 +08:00
|
|
|
continue;
|
2017-11-18 02:14:09 +08:00
|
|
|
StringRef name = getOutputDataSegmentName(segment->getName());
|
|
|
|
OutputSegment *&s = segmentMap[name];
|
|
|
|
if (s == nullptr) {
|
2018-05-15 21:36:20 +08:00
|
|
|
LLVM_DEBUG(dbgs() << "new segment: " << name << "\n");
|
2019-09-19 09:14:59 +08:00
|
|
|
s = make<OutputSegment>(name);
|
2020-11-10 09:52:39 +08:00
|
|
|
if (config->sharedMemory)
|
2019-07-04 06:04:54 +08:00
|
|
|
s->initFlags = WASM_SEGMENT_IS_PASSIVE;
|
2019-10-16 03:05:11 +08:00
|
|
|
// Exported memories are guaranteed to be zero-initialized, so no need
|
|
|
|
// to emit data segments for bss sections.
|
|
|
|
// TODO: consider initializing bss sections with memory.fill
|
|
|
|
// instructions when memory is imported and bulk-memory is available.
|
|
|
|
if (!config->importMemory && !config->relocatable &&
|
|
|
|
name.startswith(".bss"))
|
|
|
|
s->isBss = true;
|
2017-11-18 02:14:09 +08:00
|
|
|
segments.push_back(s);
|
|
|
|
}
|
|
|
|
s->addInputSegment(segment);
|
2018-05-15 21:36:20 +08:00
|
|
|
LLVM_DEBUG(dbgs() << "added data: " << name << ": " << s->size << "\n");
|
2017-11-18 02:14:09 +08:00
|
|
|
}
|
|
|
|
}
|
2019-09-19 09:14:59 +08:00
|
|
|
|
|
|
|
// Sort segments by type, placing .bss last
|
|
|
|
std::stable_sort(segments.begin(), segments.end(),
|
|
|
|
[](const OutputSegment *a, const OutputSegment *b) {
|
|
|
|
auto order = [](StringRef name) {
|
|
|
|
return StringSwitch<int>(name)
|
|
|
|
.StartsWith(".rodata", 0)
|
|
|
|
.StartsWith(".data", 1)
|
|
|
|
.StartsWith(".tdata", 2)
|
|
|
|
.StartsWith(".bss", 4)
|
|
|
|
.Default(3);
|
|
|
|
};
|
|
|
|
return order(a->name) < order(b->name);
|
|
|
|
});
|
|
|
|
|
2019-09-20 05:51:52 +08:00
|
|
|
for (size_t i = 0; i < segments.size(); ++i)
|
2019-09-19 09:14:59 +08:00
|
|
|
segments[i]->index = i;
|
2017-11-18 02:14:09 +08:00
|
|
|
}
|
|
|
|
|
2019-07-10 17:10:01 +08:00
|
|
|
static void createFunction(DefinedFunction *func, StringRef bodyContent) {
|
2019-07-04 06:04:54 +08:00
|
|
|
std::string functionBody;
|
|
|
|
{
|
|
|
|
raw_string_ostream os(functionBody);
|
|
|
|
writeUleb128(os, bodyContent.size(), "function size");
|
|
|
|
os << bodyContent;
|
|
|
|
}
|
|
|
|
ArrayRef<uint8_t> body = arrayRefFromStringRef(saver.save(functionBody));
|
|
|
|
cast<SyntheticFunction>(func->function)->setBody(body);
|
|
|
|
}
|
|
|
|
|
2020-05-22 02:33:25 +08:00
|
|
|
bool Writer::needsPassiveInitialization(const OutputSegment *segment) {
|
|
|
|
return segment->initFlags & WASM_SEGMENT_IS_PASSIVE &&
|
|
|
|
segment->name != ".tdata" && !segment->isBss;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Writer::hasPassiveInitializedSegments() {
|
|
|
|
return std::find_if(segments.begin(), segments.end(),
|
|
|
|
[this](const OutputSegment *s) {
|
|
|
|
return this->needsPassiveInitialization(s);
|
|
|
|
}) != segments.end();
|
|
|
|
}
|
|
|
|
|
2020-12-10 23:40:48 +08:00
|
|
|
void Writer::createSyntheticInitFunctions() {
|
2020-12-10 10:14:31 +08:00
|
|
|
if (config->relocatable)
|
|
|
|
return;
|
|
|
|
|
|
|
|
static WasmSignature nullSignature = {{}, {}};
|
|
|
|
|
2020-12-10 23:40:48 +08:00
|
|
|
// Passive segments are used to avoid memory being reinitialized on each
|
|
|
|
// thread's instantiation. These passive segments are initialized and
|
|
|
|
// dropped in __wasm_init_memory, which is registered as the start function
|
|
|
|
if (config->sharedMemory && hasPassiveInitializedSegments()) {
|
|
|
|
WasmSym::initMemory = symtab->addSyntheticFunction(
|
|
|
|
"__wasm_init_memory", WASM_SYMBOL_VISIBILITY_HIDDEN,
|
|
|
|
make<SyntheticFunction>(nullSignature, "__wasm_init_memory"));
|
|
|
|
WasmSym::initMemory->markLive();
|
2020-12-10 10:14:31 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (config->isPic) {
|
|
|
|
// For PIC code we create synthetic functions that apply relocations.
|
|
|
|
// These get called from __wasm_call_ctors before the user-level
|
|
|
|
// constructors.
|
|
|
|
WasmSym::applyDataRelocs = symtab->addSyntheticFunction(
|
|
|
|
"__wasm_apply_data_relocs", WASM_SYMBOL_VISIBILITY_HIDDEN,
|
|
|
|
make<SyntheticFunction>(nullSignature, "__wasm_apply_data_relocs"));
|
|
|
|
WasmSym::applyDataRelocs->markLive();
|
|
|
|
|
|
|
|
if (out.globalSec->needsRelocations()) {
|
|
|
|
WasmSym::applyGlobalRelocs = symtab->addSyntheticFunction(
|
|
|
|
"__wasm_apply_global_relocs", WASM_SYMBOL_VISIBILITY_HIDDEN,
|
|
|
|
make<SyntheticFunction>(nullSignature, "__wasm_apply_global_relocs"));
|
|
|
|
WasmSym::applyGlobalRelocs->markLive();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (WasmSym::applyGlobalRelocs && WasmSym::initMemory) {
|
|
|
|
WasmSym::startFunction = symtab->addSyntheticFunction(
|
|
|
|
"__wasm_start", WASM_SYMBOL_VISIBILITY_HIDDEN,
|
|
|
|
make<SyntheticFunction>(nullSignature, "__wasm_start"));
|
|
|
|
WasmSym::startFunction->markLive();
|
2020-12-10 23:40:48 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-04 06:04:54 +08:00
|
|
|
void Writer::createInitMemoryFunction() {
|
|
|
|
LLVM_DEBUG(dbgs() << "createInitMemoryFunction\n");
|
2020-12-10 23:40:48 +08:00
|
|
|
assert(WasmSym::initMemory);
|
2019-09-05 03:50:39 +08:00
|
|
|
assert(WasmSym::initMemoryFlag);
|
2020-12-10 23:40:48 +08:00
|
|
|
assert(hasPassiveInitializedSegments());
|
2020-12-01 06:00:34 +08:00
|
|
|
uint64_t flagAddress = WasmSym::initMemoryFlag->getVirtualAddress();
|
2020-12-04 08:51:56 +08:00
|
|
|
bool is64 = config->is64.getValueOr(false);
|
2019-07-04 06:04:54 +08:00
|
|
|
std::string bodyContent;
|
|
|
|
{
|
|
|
|
raw_string_ostream os(bodyContent);
|
2020-12-10 23:40:48 +08:00
|
|
|
// Initialize memory in a thread-safe manner. The thread that successfully
|
|
|
|
// increments the flag from 0 to 1 is is responsible for performing the
|
|
|
|
// memory initialization. Other threads go sleep on the flag until the
|
|
|
|
// first thread finishing initializing memory, increments the flag to 2,
|
|
|
|
// and wakes all the other threads. Once the flag has been set to 2,
|
|
|
|
// subsequently started threads will skip the sleep. All threads
|
|
|
|
// unconditionally drop their passive data segments once memory has been
|
|
|
|
// initialized. The generated code is as follows:
|
|
|
|
//
|
|
|
|
// (func $__wasm_init_memory
|
|
|
|
// (if
|
|
|
|
// (i32.atomic.rmw.cmpxchg align=2 offset=0
|
|
|
|
// (i32.const $__init_memory_flag)
|
|
|
|
// (i32.const 0)
|
|
|
|
// (i32.const 1)
|
|
|
|
// )
|
|
|
|
// (then
|
|
|
|
// (drop
|
|
|
|
// (i32.atomic.wait align=2 offset=0
|
|
|
|
// (i32.const $__init_memory_flag)
|
|
|
|
// (i32.const 1)
|
|
|
|
// (i32.const -1)
|
|
|
|
// )
|
|
|
|
// )
|
|
|
|
// )
|
|
|
|
// (else
|
|
|
|
// ( ... initialize data segments ... )
|
|
|
|
// (i32.atomic.store align=2 offset=0
|
|
|
|
// (i32.const $__init_memory_flag)
|
|
|
|
// (i32.const 2)
|
|
|
|
// )
|
|
|
|
// (drop
|
|
|
|
// (i32.atomic.notify align=2 offset=0
|
|
|
|
// (i32.const $__init_memory_flag)
|
|
|
|
// (i32.const -1u)
|
|
|
|
// )
|
|
|
|
// )
|
|
|
|
// )
|
|
|
|
// )
|
|
|
|
// ( ... drop data segments ... )
|
|
|
|
// )
|
|
|
|
//
|
|
|
|
// When we are building with PIC, calculate the flag location using:
|
|
|
|
//
|
|
|
|
// (global.get $__memory_base)
|
|
|
|
// (i32.const $__init_memory_flag)
|
|
|
|
// (i32.const 1)
|
|
|
|
|
2020-12-04 08:51:56 +08:00
|
|
|
// With PIC code we cache the flag address in local 0
|
|
|
|
if (config->isPic) {
|
|
|
|
writeUleb128(os, 1, "num local decls");
|
|
|
|
writeUleb128(os, 1, "local count");
|
|
|
|
writeU8(os, is64 ? WASM_TYPE_I64 : WASM_TYPE_I32, "address type");
|
|
|
|
writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET");
|
|
|
|
writeUleb128(os, WasmSym::memoryBase->getGlobalIndex(), "memory_base");
|
|
|
|
writePtrConst(os, flagAddress, is64, "flag address");
|
|
|
|
writeU8(os, WASM_OPCODE_I32_ADD, "add");
|
|
|
|
writeU8(os, WASM_OPCODE_LOCAL_SET, "local.set");
|
|
|
|
writeUleb128(os, 0, "local 0");
|
|
|
|
} else {
|
|
|
|
writeUleb128(os, 0, "num locals");
|
|
|
|
}
|
2019-07-04 06:04:54 +08:00
|
|
|
|
2020-12-10 23:40:48 +08:00
|
|
|
auto writeGetFlagAddress = [&]() {
|
|
|
|
if (config->isPic) {
|
|
|
|
writeU8(os, WASM_OPCODE_LOCAL_GET, "local.get");
|
|
|
|
writeUleb128(os, 0, "local 0");
|
|
|
|
} else {
|
|
|
|
writePtrConst(os, flagAddress, is64, "flag address");
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// Atomically check whether this is the main thread.
|
|
|
|
writeGetFlagAddress();
|
|
|
|
writeI32Const(os, 0, "expected flag value");
|
|
|
|
writeI32Const(os, 1, "flag value");
|
|
|
|
writeU8(os, WASM_OPCODE_ATOMICS_PREFIX, "atomics prefix");
|
|
|
|
writeUleb128(os, WASM_OPCODE_I32_RMW_CMPXCHG, "i32.atomic.rmw.cmpxchg");
|
|
|
|
writeMemArg(os, 2, 0);
|
|
|
|
writeU8(os, WASM_OPCODE_IF, "IF");
|
|
|
|
writeU8(os, WASM_TYPE_NORESULT, "blocktype");
|
|
|
|
|
|
|
|
// Did not increment 0, so wait for main thread to initialize memory
|
|
|
|
writeGetFlagAddress();
|
|
|
|
writeI32Const(os, 1, "expected flag value");
|
|
|
|
writeI64Const(os, -1, "timeout");
|
|
|
|
|
|
|
|
writeU8(os, WASM_OPCODE_ATOMICS_PREFIX, "atomics prefix");
|
|
|
|
writeUleb128(os, WASM_OPCODE_I32_ATOMIC_WAIT, "i32.atomic.wait");
|
|
|
|
writeMemArg(os, 2, 0);
|
|
|
|
writeU8(os, WASM_OPCODE_DROP, "drop");
|
|
|
|
|
|
|
|
writeU8(os, WASM_OPCODE_ELSE, "ELSE");
|
|
|
|
|
|
|
|
// Did increment 0, so conditionally initialize passive data segments
|
|
|
|
for (const OutputSegment *s : segments) {
|
|
|
|
if (needsPassiveInitialization(s)) {
|
|
|
|
// destination address
|
|
|
|
writePtrConst(os, s->startVA, is64, "destination address");
|
2020-12-04 08:51:56 +08:00
|
|
|
if (config->isPic) {
|
2020-12-10 23:40:48 +08:00
|
|
|
writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET");
|
|
|
|
writeUleb128(os, WasmSym::memoryBase->getGlobalIndex(),
|
|
|
|
"memory_base");
|
|
|
|
writeU8(os, WASM_OPCODE_I32_ADD, "i32.add");
|
2019-09-05 03:50:39 +08:00
|
|
|
}
|
2020-12-10 23:40:48 +08:00
|
|
|
// source segment offset
|
|
|
|
writeI32Const(os, 0, "segment offset");
|
|
|
|
// memory region size
|
|
|
|
writeI32Const(os, s->size, "memory region size");
|
|
|
|
// memory.init instruction
|
|
|
|
writeU8(os, WASM_OPCODE_MISC_PREFIX, "bulk-memory prefix");
|
|
|
|
writeUleb128(os, WASM_OPCODE_MEMORY_INIT, "memory.init");
|
|
|
|
writeUleb128(os, s->index, "segment index immediate");
|
|
|
|
writeU8(os, 0, "memory index immediate");
|
2019-09-05 03:50:39 +08:00
|
|
|
}
|
2020-12-10 23:40:48 +08:00
|
|
|
}
|
2019-09-05 03:50:39 +08:00
|
|
|
|
2020-12-10 23:40:48 +08:00
|
|
|
// Set flag to 2 to mark end of initialization
|
|
|
|
writeGetFlagAddress();
|
|
|
|
writeI32Const(os, 2, "flag value");
|
|
|
|
writeU8(os, WASM_OPCODE_ATOMICS_PREFIX, "atomics prefix");
|
|
|
|
writeUleb128(os, WASM_OPCODE_I32_ATOMIC_STORE, "i32.atomic.store");
|
|
|
|
writeMemArg(os, 2, 0);
|
|
|
|
|
|
|
|
// Notify any waiters that memory initialization is complete
|
|
|
|
writeGetFlagAddress();
|
|
|
|
writeI32Const(os, -1, "number of waiters");
|
|
|
|
writeU8(os, WASM_OPCODE_ATOMICS_PREFIX, "atomics prefix");
|
|
|
|
writeUleb128(os, WASM_OPCODE_ATOMIC_NOTIFY, "atomic.notify");
|
|
|
|
writeMemArg(os, 2, 0);
|
|
|
|
writeU8(os, WASM_OPCODE_DROP, "drop");
|
|
|
|
|
|
|
|
writeU8(os, WASM_OPCODE_END, "END");
|
|
|
|
|
|
|
|
// Unconditionally drop passive data segments
|
|
|
|
for (const OutputSegment *s : segments) {
|
|
|
|
if (needsPassiveInitialization(s)) {
|
|
|
|
// data.drop instruction
|
|
|
|
writeU8(os, WASM_OPCODE_MISC_PREFIX, "bulk-memory prefix");
|
|
|
|
writeUleb128(os, WASM_OPCODE_DATA_DROP, "data.drop");
|
|
|
|
writeUleb128(os, s->index, "segment index immediate");
|
2019-07-04 06:04:54 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
writeU8(os, WASM_OPCODE_END, "END");
|
|
|
|
}
|
|
|
|
|
2019-07-10 17:10:01 +08:00
|
|
|
createFunction(WasmSym::initMemory, bodyContent);
|
2019-07-04 06:04:54 +08:00
|
|
|
}
|
|
|
|
|
2020-12-10 10:14:31 +08:00
|
|
|
void Writer::createStartFunction() {
|
|
|
|
if (WasmSym::startFunction) {
|
|
|
|
std::string bodyContent;
|
|
|
|
{
|
|
|
|
raw_string_ostream os(bodyContent);
|
|
|
|
writeUleb128(os, 0, "num locals");
|
|
|
|
writeU8(os, WASM_OPCODE_CALL, "CALL");
|
|
|
|
writeUleb128(os, WasmSym::initMemory->getFunctionIndex(),
|
|
|
|
"function index");
|
|
|
|
writeU8(os, WASM_OPCODE_CALL, "CALL");
|
|
|
|
writeUleb128(os, WasmSym::applyGlobalRelocs->getFunctionIndex(),
|
|
|
|
"function index");
|
|
|
|
writeU8(os, WASM_OPCODE_END, "END");
|
|
|
|
}
|
|
|
|
createFunction(WasmSym::startFunction, bodyContent);
|
|
|
|
} else if (WasmSym::initMemory) {
|
|
|
|
WasmSym::startFunction = WasmSym::initMemory;
|
|
|
|
} else if (WasmSym::applyGlobalRelocs) {
|
|
|
|
WasmSym::startFunction = WasmSym::applyGlobalRelocs;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-05 02:40:51 +08:00
|
|
|
// For -shared (PIC) output, we create create a synthetic function which will
|
|
|
|
// apply any relocations to the data segments on startup. This function is
|
2019-07-04 06:04:54 +08:00
|
|
|
// called __wasm_apply_relocs and is added at the beginning of __wasm_call_ctors
|
|
|
|
// before any of the constructors run.
|
2020-12-10 10:14:31 +08:00
|
|
|
void Writer::createApplyDataRelocationsFunction() {
|
|
|
|
LLVM_DEBUG(dbgs() << "createApplyDataRelocationsFunction\n");
|
2019-04-05 02:40:51 +08:00
|
|
|
// First write the body's contents to a string.
|
|
|
|
std::string bodyContent;
|
|
|
|
{
|
|
|
|
raw_string_ostream os(bodyContent);
|
|
|
|
writeUleb128(os, 0, "num locals");
|
|
|
|
for (const OutputSegment *seg : segments)
|
|
|
|
for (const InputSegment *inSeg : seg->inputSegments)
|
|
|
|
inSeg->generateRelocationCode(os);
|
2020-10-08 05:48:37 +08:00
|
|
|
|
2019-04-05 02:40:51 +08:00
|
|
|
writeU8(os, WASM_OPCODE_END, "END");
|
|
|
|
}
|
|
|
|
|
2020-12-10 10:14:31 +08:00
|
|
|
createFunction(WasmSym::applyDataRelocs, bodyContent);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Similar to createApplyDataRelocationsFunction but generates relocation code
|
|
|
|
// fro WebAssembly globals. Because these globals are not shared between threads
|
|
|
|
// these relocation need to run on every thread.
|
|
|
|
void Writer::createApplyGlobalRelocationsFunction() {
|
|
|
|
// First write the body's contents to a string.
|
|
|
|
std::string bodyContent;
|
|
|
|
{
|
|
|
|
raw_string_ostream os(bodyContent);
|
|
|
|
writeUleb128(os, 0, "num locals");
|
|
|
|
out.globalSec->generateRelocationCode(os);
|
|
|
|
writeU8(os, WASM_OPCODE_END, "END");
|
|
|
|
}
|
|
|
|
|
|
|
|
createFunction(WasmSym::applyGlobalRelocs, bodyContent);
|
2019-04-05 02:40:51 +08:00
|
|
|
}
|
2018-01-13 02:35:13 +08:00
|
|
|
|
|
|
|
// Create synthetic "__wasm_call_ctors" function based on ctor functions
|
|
|
|
// in input object.
|
2019-04-05 02:40:51 +08:00
|
|
|
void Writer::createCallCtorsFunction() {
|
2020-10-01 08:21:57 +08:00
|
|
|
// If __wasm_call_ctors isn't referenced, there aren't any ctors, and we
|
|
|
|
// aren't calling `__wasm_apply_relocs` for Emscripten-style PIC, don't
|
|
|
|
// define the `__wasm_call_ctors` function.
|
2020-12-10 10:14:31 +08:00
|
|
|
if (!WasmSym::callCtors->isLive() && !WasmSym::applyDataRelocs &&
|
|
|
|
initFunctions.empty())
|
2019-03-02 06:35:47 +08:00
|
|
|
return;
|
|
|
|
|
2018-03-02 22:48:50 +08:00
|
|
|
// First write the body's contents to a string.
|
|
|
|
std::string bodyContent;
|
2018-01-13 02:35:13 +08:00
|
|
|
{
|
2018-03-02 22:48:50 +08:00
|
|
|
raw_string_ostream os(bodyContent);
|
2018-01-13 02:35:13 +08:00
|
|
|
writeUleb128(os, 0, "num locals");
|
2019-07-04 06:04:54 +08:00
|
|
|
|
2020-12-10 10:14:31 +08:00
|
|
|
if (WasmSym::applyDataRelocs) {
|
2019-04-05 02:40:51 +08:00
|
|
|
writeU8(os, WASM_OPCODE_CALL, "CALL");
|
2020-12-10 10:14:31 +08:00
|
|
|
writeUleb128(os, WasmSym::applyDataRelocs->getFunctionIndex(),
|
2019-04-05 02:40:51 +08:00
|
|
|
"function index");
|
|
|
|
}
|
2019-07-04 06:04:54 +08:00
|
|
|
|
|
|
|
// Call constructors
|
2018-02-23 13:08:53 +08:00
|
|
|
for (const WasmInitEntry &f : initFunctions) {
|
2019-04-05 02:40:51 +08:00
|
|
|
writeU8(os, WASM_OPCODE_CALL, "CALL");
|
2018-03-13 03:56:23 +08:00
|
|
|
writeUleb128(os, f.sym->getFunctionIndex(), "function index");
|
2020-06-17 03:23:25 +08:00
|
|
|
for (size_t i = 0; i < f.sym->signature->Returns.size(); i++) {
|
|
|
|
writeU8(os, WASM_OPCODE_DROP, "DROP");
|
|
|
|
}
|
2018-01-13 02:35:13 +08:00
|
|
|
}
|
2020-12-10 10:14:31 +08:00
|
|
|
|
2019-04-05 02:40:51 +08:00
|
|
|
writeU8(os, WASM_OPCODE_END, "END");
|
2018-01-13 02:35:13 +08:00
|
|
|
}
|
|
|
|
|
2019-07-10 17:10:01 +08:00
|
|
|
createFunction(WasmSym::callCtors, bodyContent);
|
2018-01-13 02:35:13 +08:00
|
|
|
}
|
|
|
|
|
2020-10-01 08:21:57 +08:00
|
|
|
// Create a wrapper around a function export which calls the
|
|
|
|
// static constructors and destructors.
|
|
|
|
void Writer::createCommandExportWrapper(uint32_t functionIndex,
|
|
|
|
DefinedFunction *f) {
|
|
|
|
// First write the body's contents to a string.
|
|
|
|
std::string bodyContent;
|
|
|
|
{
|
|
|
|
raw_string_ostream os(bodyContent);
|
|
|
|
writeUleb128(os, 0, "num locals");
|
|
|
|
|
|
|
|
// If we have any ctors, or we're calling `__wasm_apply_relocs` for
|
|
|
|
// Emscripten-style PIC, call `__wasm_call_ctors` which performs those
|
|
|
|
// calls.
|
2020-12-10 10:14:31 +08:00
|
|
|
if (WasmSym::callCtors->isLive()) {
|
2020-10-01 08:21:57 +08:00
|
|
|
writeU8(os, WASM_OPCODE_CALL, "CALL");
|
|
|
|
writeUleb128(os, WasmSym::callCtors->getFunctionIndex(),
|
|
|
|
"function index");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Call the user's code, leaving any return values on the operand stack.
|
|
|
|
for (size_t i = 0; i < f->signature->Params.size(); ++i) {
|
|
|
|
writeU8(os, WASM_OPCODE_LOCAL_GET, "local.get");
|
|
|
|
writeUleb128(os, i, "local index");
|
|
|
|
}
|
|
|
|
writeU8(os, WASM_OPCODE_CALL, "CALL");
|
|
|
|
writeUleb128(os, functionIndex, "function index");
|
|
|
|
|
|
|
|
// Call the function that calls the destructors.
|
|
|
|
if (DefinedFunction *callDtors = WasmSym::callDtors) {
|
|
|
|
writeU8(os, WASM_OPCODE_CALL, "CALL");
|
|
|
|
writeUleb128(os, callDtors->getFunctionIndex(), "function index");
|
|
|
|
}
|
|
|
|
|
|
|
|
// End the function, returning the return values from the user's code.
|
|
|
|
writeU8(os, WASM_OPCODE_END, "END");
|
|
|
|
}
|
|
|
|
|
|
|
|
createFunction(f, bodyContent);
|
|
|
|
}
|
|
|
|
|
[WebAssembly] Implement thread-local storage (local-exec model)
Summary:
Thread local variables are placed inside a `.tdata` segment. Their symbols are
offsets from the start of the segment. The address of a thread local variable
is computed as `__tls_base` + the offset from the start of the segment.
`.tdata` segment is a passive segment and `memory.init` is used once per thread
to initialize the thread local storage.
`__tls_base` is a wasm global. Since each thread has its own wasm instance,
it is effectively thread local. Currently, `__tls_base` must be initialized
at thread startup, and so cannot be used with dynamic libraries.
`__tls_base` is to be initialized with a new linker-synthesized function,
`__wasm_init_tls`, which takes as an argument a block of memory to use as the
storage for thread locals. It then initializes the block of memory and sets
`__tls_base`. As `__wasm_init_tls` will handle the memory initialization,
the memory does not have to be zeroed.
To help allocating memory for thread-local storage, a new compiler intrinsic
is introduced: `__builtin_wasm_tls_size()`. This instrinsic function returns
the size of the thread-local storage for the current function.
The expected usage is to run something like the following upon thread startup:
__wasm_init_tls(malloc(__builtin_wasm_tls_size()));
Reviewers: tlively, aheejin, kripken, sbc100
Subscribers: dschuff, jgravelle-google, hiraditya, sunfish, jfb, cfe-commits, llvm-commits
Tags: #clang, #llvm
Differential Revision: https://reviews.llvm.org/D64537
llvm-svn: 366272
2019-07-17 06:00:45 +08:00
|
|
|
void Writer::createInitTLSFunction() {
|
|
|
|
std::string bodyContent;
|
|
|
|
{
|
|
|
|
raw_string_ostream os(bodyContent);
|
|
|
|
|
|
|
|
OutputSegment *tlsSeg = nullptr;
|
|
|
|
for (auto *seg : segments) {
|
2019-07-19 05:18:24 +08:00
|
|
|
if (seg->name == ".tdata") {
|
[WebAssembly] Implement thread-local storage (local-exec model)
Summary:
Thread local variables are placed inside a `.tdata` segment. Their symbols are
offsets from the start of the segment. The address of a thread local variable
is computed as `__tls_base` + the offset from the start of the segment.
`.tdata` segment is a passive segment and `memory.init` is used once per thread
to initialize the thread local storage.
`__tls_base` is a wasm global. Since each thread has its own wasm instance,
it is effectively thread local. Currently, `__tls_base` must be initialized
at thread startup, and so cannot be used with dynamic libraries.
`__tls_base` is to be initialized with a new linker-synthesized function,
`__wasm_init_tls`, which takes as an argument a block of memory to use as the
storage for thread locals. It then initializes the block of memory and sets
`__tls_base`. As `__wasm_init_tls` will handle the memory initialization,
the memory does not have to be zeroed.
To help allocating memory for thread-local storage, a new compiler intrinsic
is introduced: `__builtin_wasm_tls_size()`. This instrinsic function returns
the size of the thread-local storage for the current function.
The expected usage is to run something like the following upon thread startup:
__wasm_init_tls(malloc(__builtin_wasm_tls_size()));
Reviewers: tlively, aheejin, kripken, sbc100
Subscribers: dschuff, jgravelle-google, hiraditya, sunfish, jfb, cfe-commits, llvm-commits
Tags: #clang, #llvm
Differential Revision: https://reviews.llvm.org/D64537
llvm-svn: 366272
2019-07-17 06:00:45 +08:00
|
|
|
tlsSeg = seg;
|
2019-07-19 05:18:24 +08:00
|
|
|
break;
|
|
|
|
}
|
[WebAssembly] Implement thread-local storage (local-exec model)
Summary:
Thread local variables are placed inside a `.tdata` segment. Their symbols are
offsets from the start of the segment. The address of a thread local variable
is computed as `__tls_base` + the offset from the start of the segment.
`.tdata` segment is a passive segment and `memory.init` is used once per thread
to initialize the thread local storage.
`__tls_base` is a wasm global. Since each thread has its own wasm instance,
it is effectively thread local. Currently, `__tls_base` must be initialized
at thread startup, and so cannot be used with dynamic libraries.
`__tls_base` is to be initialized with a new linker-synthesized function,
`__wasm_init_tls`, which takes as an argument a block of memory to use as the
storage for thread locals. It then initializes the block of memory and sets
`__tls_base`. As `__wasm_init_tls` will handle the memory initialization,
the memory does not have to be zeroed.
To help allocating memory for thread-local storage, a new compiler intrinsic
is introduced: `__builtin_wasm_tls_size()`. This instrinsic function returns
the size of the thread-local storage for the current function.
The expected usage is to run something like the following upon thread startup:
__wasm_init_tls(malloc(__builtin_wasm_tls_size()));
Reviewers: tlively, aheejin, kripken, sbc100
Subscribers: dschuff, jgravelle-google, hiraditya, sunfish, jfb, cfe-commits, llvm-commits
Tags: #clang, #llvm
Differential Revision: https://reviews.llvm.org/D64537
llvm-svn: 366272
2019-07-17 06:00:45 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
writeUleb128(os, 0, "num locals");
|
|
|
|
if (tlsSeg) {
|
|
|
|
writeU8(os, WASM_OPCODE_LOCAL_GET, "local.get");
|
|
|
|
writeUleb128(os, 0, "local index");
|
|
|
|
|
|
|
|
writeU8(os, WASM_OPCODE_GLOBAL_SET, "global.set");
|
|
|
|
writeUleb128(os, WasmSym::tlsBase->getGlobalIndex(), "global index");
|
|
|
|
|
2020-08-08 04:24:43 +08:00
|
|
|
// FIXME(wvo): this local needs to be I64 in wasm64, or we need an extend op.
|
[WebAssembly] Implement thread-local storage (local-exec model)
Summary:
Thread local variables are placed inside a `.tdata` segment. Their symbols are
offsets from the start of the segment. The address of a thread local variable
is computed as `__tls_base` + the offset from the start of the segment.
`.tdata` segment is a passive segment and `memory.init` is used once per thread
to initialize the thread local storage.
`__tls_base` is a wasm global. Since each thread has its own wasm instance,
it is effectively thread local. Currently, `__tls_base` must be initialized
at thread startup, and so cannot be used with dynamic libraries.
`__tls_base` is to be initialized with a new linker-synthesized function,
`__wasm_init_tls`, which takes as an argument a block of memory to use as the
storage for thread locals. It then initializes the block of memory and sets
`__tls_base`. As `__wasm_init_tls` will handle the memory initialization,
the memory does not have to be zeroed.
To help allocating memory for thread-local storage, a new compiler intrinsic
is introduced: `__builtin_wasm_tls_size()`. This instrinsic function returns
the size of the thread-local storage for the current function.
The expected usage is to run something like the following upon thread startup:
__wasm_init_tls(malloc(__builtin_wasm_tls_size()));
Reviewers: tlively, aheejin, kripken, sbc100
Subscribers: dschuff, jgravelle-google, hiraditya, sunfish, jfb, cfe-commits, llvm-commits
Tags: #clang, #llvm
Differential Revision: https://reviews.llvm.org/D64537
llvm-svn: 366272
2019-07-17 06:00:45 +08:00
|
|
|
writeU8(os, WASM_OPCODE_LOCAL_GET, "local.get");
|
|
|
|
writeUleb128(os, 0, "local index");
|
|
|
|
|
2019-09-05 03:50:39 +08:00
|
|
|
writeI32Const(os, 0, "segment offset");
|
[WebAssembly] Implement thread-local storage (local-exec model)
Summary:
Thread local variables are placed inside a `.tdata` segment. Their symbols are
offsets from the start of the segment. The address of a thread local variable
is computed as `__tls_base` + the offset from the start of the segment.
`.tdata` segment is a passive segment and `memory.init` is used once per thread
to initialize the thread local storage.
`__tls_base` is a wasm global. Since each thread has its own wasm instance,
it is effectively thread local. Currently, `__tls_base` must be initialized
at thread startup, and so cannot be used with dynamic libraries.
`__tls_base` is to be initialized with a new linker-synthesized function,
`__wasm_init_tls`, which takes as an argument a block of memory to use as the
storage for thread locals. It then initializes the block of memory and sets
`__tls_base`. As `__wasm_init_tls` will handle the memory initialization,
the memory does not have to be zeroed.
To help allocating memory for thread-local storage, a new compiler intrinsic
is introduced: `__builtin_wasm_tls_size()`. This instrinsic function returns
the size of the thread-local storage for the current function.
The expected usage is to run something like the following upon thread startup:
__wasm_init_tls(malloc(__builtin_wasm_tls_size()));
Reviewers: tlively, aheejin, kripken, sbc100
Subscribers: dschuff, jgravelle-google, hiraditya, sunfish, jfb, cfe-commits, llvm-commits
Tags: #clang, #llvm
Differential Revision: https://reviews.llvm.org/D64537
llvm-svn: 366272
2019-07-17 06:00:45 +08:00
|
|
|
|
2019-09-05 03:50:39 +08:00
|
|
|
writeI32Const(os, tlsSeg->size, "memory region size");
|
[WebAssembly] Implement thread-local storage (local-exec model)
Summary:
Thread local variables are placed inside a `.tdata` segment. Their symbols are
offsets from the start of the segment. The address of a thread local variable
is computed as `__tls_base` + the offset from the start of the segment.
`.tdata` segment is a passive segment and `memory.init` is used once per thread
to initialize the thread local storage.
`__tls_base` is a wasm global. Since each thread has its own wasm instance,
it is effectively thread local. Currently, `__tls_base` must be initialized
at thread startup, and so cannot be used with dynamic libraries.
`__tls_base` is to be initialized with a new linker-synthesized function,
`__wasm_init_tls`, which takes as an argument a block of memory to use as the
storage for thread locals. It then initializes the block of memory and sets
`__tls_base`. As `__wasm_init_tls` will handle the memory initialization,
the memory does not have to be zeroed.
To help allocating memory for thread-local storage, a new compiler intrinsic
is introduced: `__builtin_wasm_tls_size()`. This instrinsic function returns
the size of the thread-local storage for the current function.
The expected usage is to run something like the following upon thread startup:
__wasm_init_tls(malloc(__builtin_wasm_tls_size()));
Reviewers: tlively, aheejin, kripken, sbc100
Subscribers: dschuff, jgravelle-google, hiraditya, sunfish, jfb, cfe-commits, llvm-commits
Tags: #clang, #llvm
Differential Revision: https://reviews.llvm.org/D64537
llvm-svn: 366272
2019-07-17 06:00:45 +08:00
|
|
|
|
|
|
|
writeU8(os, WASM_OPCODE_MISC_PREFIX, "bulk-memory prefix");
|
|
|
|
writeUleb128(os, WASM_OPCODE_MEMORY_INIT, "MEMORY.INIT");
|
|
|
|
writeUleb128(os, tlsSeg->index, "segment index immediate");
|
|
|
|
writeU8(os, 0, "memory index immediate");
|
|
|
|
}
|
|
|
|
writeU8(os, WASM_OPCODE_END, "end function");
|
|
|
|
}
|
|
|
|
|
|
|
|
createFunction(WasmSym::initTLS, bodyContent);
|
|
|
|
}
|
|
|
|
|
2018-01-13 02:35:13 +08:00
|
|
|
// Populate InitFunctions vector with init functions from all input objects.
|
|
|
|
// This is then used either when creating the output linking section or to
|
|
|
|
// synthesize the "__wasm_call_ctors" function.
|
|
|
|
void Writer::calculateInitFunctions() {
|
2019-03-02 12:55:02 +08:00
|
|
|
if (!config->relocatable && !WasmSym::callCtors->isLive())
|
|
|
|
return;
|
|
|
|
|
2018-01-13 02:35:13 +08:00
|
|
|
for (ObjFile *file : symtab->objectFiles) {
|
|
|
|
const WasmLinkingData &l = file->getWasmObj()->linkingData();
|
2018-03-02 22:46:54 +08:00
|
|
|
for (const WasmInitFunc &f : l.InitFunctions) {
|
|
|
|
FunctionSymbol *sym = file->getFunctionSymbol(f.Symbol);
|
2019-06-07 14:00:46 +08:00
|
|
|
// comdat exclusions can cause init functions be discarded.
|
2020-10-01 11:00:04 +08:00
|
|
|
if (sym->isDiscarded() || !sym->isLive())
|
2019-06-07 14:00:46 +08:00
|
|
|
continue;
|
2020-06-17 03:23:25 +08:00
|
|
|
if (sym->signature->Params.size() != 0)
|
|
|
|
error("constructor functions cannot take arguments: " + toString(*sym));
|
2019-07-18 02:43:36 +08:00
|
|
|
LLVM_DEBUG(dbgs() << "initFunctions: " << toString(*sym) << "\n");
|
2018-03-02 22:46:54 +08:00
|
|
|
initFunctions.emplace_back(WasmInitEntry{sym, f.Priority});
|
|
|
|
}
|
2018-01-13 02:35:13 +08:00
|
|
|
}
|
2018-02-28 08:15:59 +08:00
|
|
|
|
2018-01-13 02:35:13 +08:00
|
|
|
// Sort in order of priority (lowest first) so that they are called
|
|
|
|
// in the correct order.
|
2019-04-23 10:42:06 +08:00
|
|
|
llvm::stable_sort(initFunctions,
|
|
|
|
[](const WasmInitEntry &l, const WasmInitEntry &r) {
|
|
|
|
return l.priority < r.priority;
|
|
|
|
});
|
2018-01-13 02:35:13 +08:00
|
|
|
}
|
|
|
|
|
2019-05-21 17:13:09 +08:00
|
|
|
void Writer::createSyntheticSections() {
|
|
|
|
out.dylinkSec = make<DylinkSection>();
|
|
|
|
out.typeSec = make<TypeSection>();
|
|
|
|
out.importSec = make<ImportSection>();
|
|
|
|
out.functionSec = make<FunctionSection>();
|
|
|
|
out.tableSec = make<TableSection>();
|
|
|
|
out.memorySec = make<MemorySection>();
|
|
|
|
out.eventSec = make<EventSection>();
|
2020-03-25 10:36:13 +08:00
|
|
|
out.globalSec = make<GlobalSection>();
|
2019-05-21 17:13:09 +08:00
|
|
|
out.exportSec = make<ExportSection>();
|
2020-12-10 23:40:48 +08:00
|
|
|
out.startSec = make<StartSection>();
|
2019-08-27 12:19:34 +08:00
|
|
|
out.elemSec = make<ElemSection>();
|
2019-10-16 03:05:11 +08:00
|
|
|
out.dataCountSec = make<DataCountSection>(segments);
|
2019-05-21 17:13:09 +08:00
|
|
|
out.linkingSec = make<LinkingSection>(initFunctions, segments);
|
2020-12-09 13:47:19 +08:00
|
|
|
out.nameSec = make<NameSection>(segments);
|
2019-05-21 17:13:09 +08:00
|
|
|
out.producersSec = make<ProducersSection>();
|
|
|
|
out.targetFeaturesSec = make<TargetFeaturesSection>();
|
|
|
|
}
|
|
|
|
|
2017-11-18 02:14:09 +08:00
|
|
|
void Writer::run() {
|
2018-11-15 08:37:21 +08:00
|
|
|
if (config->relocatable || config->isPic)
|
2018-02-28 07:58:03 +08:00
|
|
|
config->globalBase = 0;
|
|
|
|
|
2018-11-15 08:37:21 +08:00
|
|
|
// For PIC code the table base is assigned dynamically by the loader.
|
|
|
|
// For non-PIC, we start at 1 so that accessing table index 0 always traps.
|
2019-08-14 01:02:02 +08:00
|
|
|
if (!config->isPic) {
|
2019-08-27 12:19:34 +08:00
|
|
|
config->tableBase = 1;
|
2019-08-14 01:02:02 +08:00
|
|
|
if (WasmSym::definedTableBase)
|
2019-08-27 12:19:34 +08:00
|
|
|
WasmSym::definedTableBase->setVirtualAddress(config->tableBase);
|
2019-08-14 01:02:02 +08:00
|
|
|
}
|
2018-11-15 08:37:21 +08:00
|
|
|
|
2019-05-21 17:13:09 +08:00
|
|
|
log("-- createOutputSegments");
|
|
|
|
createOutputSegments();
|
|
|
|
log("-- createSyntheticSections");
|
|
|
|
createSyntheticSections();
|
|
|
|
log("-- populateProducers");
|
|
|
|
populateProducers();
|
2017-11-18 02:14:09 +08:00
|
|
|
log("-- calculateImports");
|
|
|
|
calculateImports();
|
2019-05-23 18:06:03 +08:00
|
|
|
log("-- layoutMemory");
|
|
|
|
layoutMemory();
|
|
|
|
|
|
|
|
if (!config->relocatable) {
|
|
|
|
// Create linker synthesized __start_SECNAME/__stop_SECNAME symbols
|
|
|
|
// This has to be done after memory layout is performed.
|
|
|
|
for (const OutputSegment *seg : segments)
|
2019-07-08 18:35:08 +08:00
|
|
|
addStartStopSymbols(seg);
|
2019-05-23 18:06:03 +08:00
|
|
|
}
|
|
|
|
|
2019-05-23 17:41:03 +08:00
|
|
|
log("-- scanRelocations");
|
|
|
|
scanRelocations();
|
2020-12-10 10:14:31 +08:00
|
|
|
log("-- createSyntheticInitFunctions");
|
|
|
|
createSyntheticInitFunctions();
|
2018-01-10 07:56:44 +08:00
|
|
|
log("-- assignIndexes");
|
|
|
|
assignIndexes();
|
2018-01-13 02:35:13 +08:00
|
|
|
log("-- calculateInitFunctions");
|
|
|
|
calculateInitFunctions();
|
2019-05-23 18:06:03 +08:00
|
|
|
|
2019-04-05 02:40:51 +08:00
|
|
|
if (!config->relocatable) {
|
2020-12-10 10:14:31 +08:00
|
|
|
// Create linker synthesized functions
|
|
|
|
if (WasmSym::applyDataRelocs)
|
|
|
|
createApplyDataRelocationsFunction();
|
|
|
|
if (WasmSym::applyGlobalRelocs)
|
|
|
|
createApplyGlobalRelocationsFunction();
|
2020-12-03 07:55:25 +08:00
|
|
|
if (WasmSym::initMemory)
|
2020-11-11 09:46:52 +08:00
|
|
|
createInitMemoryFunction();
|
2020-12-10 10:14:31 +08:00
|
|
|
createStartFunction();
|
2020-12-03 07:55:25 +08:00
|
|
|
|
2019-04-05 02:40:51 +08:00
|
|
|
createCallCtorsFunction();
|
2020-10-01 08:21:57 +08:00
|
|
|
|
|
|
|
// Create export wrappers for commands if needed.
|
|
|
|
//
|
|
|
|
// If the input contains a call to `__wasm_call_ctors`, either in one of
|
|
|
|
// the input objects or an explicit export from the command-line, we
|
|
|
|
// assume ctors and dtors are taken care of already.
|
|
|
|
if (!config->relocatable && !config->isPic &&
|
|
|
|
!WasmSym::callCtors->isUsedInRegularObj &&
|
|
|
|
!WasmSym::callCtors->isExported()) {
|
|
|
|
log("-- createCommandExportWrappers");
|
|
|
|
createCommandExportWrappers();
|
|
|
|
}
|
2019-04-05 02:40:51 +08:00
|
|
|
}
|
2019-05-23 18:06:03 +08:00
|
|
|
|
2020-12-04 08:51:56 +08:00
|
|
|
if (WasmSym::initTLS && WasmSym::initTLS->isLive())
|
[WebAssembly] Implement thread-local storage (local-exec model)
Summary:
Thread local variables are placed inside a `.tdata` segment. Their symbols are
offsets from the start of the segment. The address of a thread local variable
is computed as `__tls_base` + the offset from the start of the segment.
`.tdata` segment is a passive segment and `memory.init` is used once per thread
to initialize the thread local storage.
`__tls_base` is a wasm global. Since each thread has its own wasm instance,
it is effectively thread local. Currently, `__tls_base` must be initialized
at thread startup, and so cannot be used with dynamic libraries.
`__tls_base` is to be initialized with a new linker-synthesized function,
`__wasm_init_tls`, which takes as an argument a block of memory to use as the
storage for thread locals. It then initializes the block of memory and sets
`__tls_base`. As `__wasm_init_tls` will handle the memory initialization,
the memory does not have to be zeroed.
To help allocating memory for thread-local storage, a new compiler intrinsic
is introduced: `__builtin_wasm_tls_size()`. This instrinsic function returns
the size of the thread-local storage for the current function.
The expected usage is to run something like the following upon thread startup:
__wasm_init_tls(malloc(__builtin_wasm_tls_size()));
Reviewers: tlively, aheejin, kripken, sbc100
Subscribers: dschuff, jgravelle-google, hiraditya, sunfish, jfb, cfe-commits, llvm-commits
Tags: #clang, #llvm
Differential Revision: https://reviews.llvm.org/D64537
llvm-svn: 366272
2019-07-17 06:00:45 +08:00
|
|
|
createInitTLSFunction();
|
|
|
|
|
|
|
|
if (errorCount())
|
|
|
|
return;
|
|
|
|
|
2019-05-23 18:06:03 +08:00
|
|
|
log("-- calculateTypes");
|
|
|
|
calculateTypes();
|
2018-02-23 13:08:53 +08:00
|
|
|
log("-- calculateExports");
|
|
|
|
calculateExports();
|
2018-05-05 07:14:42 +08:00
|
|
|
log("-- calculateCustomSections");
|
|
|
|
calculateCustomSections();
|
2019-05-21 17:13:09 +08:00
|
|
|
log("-- populateSymtab");
|
|
|
|
populateSymtab();
|
2020-09-15 09:28:26 +08:00
|
|
|
log("-- populateTargetFeatures");
|
|
|
|
populateTargetFeatures();
|
2019-05-21 17:13:09 +08:00
|
|
|
log("-- addSections");
|
|
|
|
addSections();
|
2017-11-18 02:14:09 +08:00
|
|
|
|
|
|
|
if (errorHandler().verbose) {
|
2019-05-21 17:13:09 +08:00
|
|
|
log("Defined Functions: " + Twine(out.functionSec->inputFunctions.size()));
|
2019-09-25 04:52:12 +08:00
|
|
|
log("Defined Globals : " + Twine(out.globalSec->numGlobals()));
|
2019-05-21 17:13:09 +08:00
|
|
|
log("Defined Events : " + Twine(out.eventSec->inputEvents.size()));
|
2021-01-05 19:08:58 +08:00
|
|
|
log("Defined Tables : " + Twine(out.tableSec->inputTables.size()));
|
2019-07-10 17:10:01 +08:00
|
|
|
log("Function Imports : " +
|
|
|
|
Twine(out.importSec->getNumImportedFunctions()));
|
|
|
|
log("Global Imports : " + Twine(out.importSec->getNumImportedGlobals()));
|
|
|
|
log("Event Imports : " + Twine(out.importSec->getNumImportedEvents()));
|
2021-01-05 19:08:58 +08:00
|
|
|
log("Table Imports : " + Twine(out.importSec->getNumImportedTables()));
|
2017-11-18 02:14:09 +08:00
|
|
|
for (ObjFile *file : symtab->objectFiles)
|
|
|
|
file->dumpInfo();
|
|
|
|
}
|
|
|
|
|
|
|
|
createHeader();
|
2019-05-21 17:13:09 +08:00
|
|
|
log("-- finalizeSections");
|
|
|
|
finalizeSections();
|
2020-03-28 07:52:27 +08:00
|
|
|
|
|
|
|
log("-- writeMapFile");
|
|
|
|
writeMapFile(outputSections);
|
2017-11-18 02:14:09 +08:00
|
|
|
|
|
|
|
log("-- openFile");
|
|
|
|
openFile();
|
|
|
|
if (errorCount())
|
|
|
|
return;
|
|
|
|
|
|
|
|
writeHeader();
|
|
|
|
|
|
|
|
log("-- writeSections");
|
|
|
|
writeSections();
|
|
|
|
if (errorCount())
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (Error e = buffer->commit())
|
|
|
|
fatal("failed to write the output file: " + toString(std::move(e)));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Open a result file.
|
|
|
|
void Writer::openFile() {
|
|
|
|
log("writing: " + config->outputFile);
|
|
|
|
|
|
|
|
Expected<std::unique_ptr<FileOutputBuffer>> bufferOrErr =
|
|
|
|
FileOutputBuffer::create(config->outputFile, fileSize,
|
|
|
|
FileOutputBuffer::F_executable);
|
|
|
|
|
|
|
|
if (!bufferOrErr)
|
|
|
|
error("failed to open " + config->outputFile + ": " +
|
|
|
|
toString(bufferOrErr.takeError()));
|
|
|
|
else
|
|
|
|
buffer = std::move(*bufferOrErr);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Writer::createHeader() {
|
|
|
|
raw_string_ostream os(header);
|
|
|
|
writeBytes(os, WasmMagic, sizeof(WasmMagic), "wasm magic");
|
|
|
|
writeU32(os, WasmVersion, "wasm version");
|
|
|
|
os.flush();
|
|
|
|
fileSize += header.size();
|
|
|
|
}
|
|
|
|
|
2019-10-10 13:25:39 +08:00
|
|
|
void writeResult() { Writer().run(); }
|
|
|
|
|
|
|
|
} // namespace wasm
|
|
|
|
} // namespace lld
|