[WebAssembly] WASIP3 Library Call Thread Context Support#175800
[WebAssembly] WASIP3 Library Call Thread Context Support#175800TartanLlama wants to merge 159 commits into
Conversation
|
Thank you for submitting a Pull Request (PR) to the LLVM Project! This PR will be automatically labeled and the relevant teams will be notified. If you wish to, you can add reviewers by using the "Reviewers" section on this page. If this is not working for you, it is probably because you do not have write permissions for the repository. In which case you can instead tag reviewers by name in a comment by using If you have received no comments on your PR for a week, you can request a review by "ping"ing the PR by adding a comment “Ping”. The common courtesy "ping" rate is once a week. Please remember that you are asking for valuable time from other developers. If you have further questions, they may be answered by the LLVM GitHub User Guide. You can also ask questions in a comment on this PR, on the LLVM Discord or on the forums. |
alexcrichton
left a comment
There was a problem hiding this comment.
Two higher-ish level thoughts on this:
- Would it be possible to decouple the selection of how things work internally from the target name and "wasip3" suffix? These options will, I believe, be useful for experimenting on other wasm targets (e.g. even
wasm32-wasip2) and it would be useful to have knobs to turn without faking/forcing a target. My thinking is that the default behavior forwasm32-wasip3is still the same, exactly as-is in this PR, but the knobs could be further refined if so desired for power users. Effectively there'd be per-target defaults for the knobs, but the knobs could be manually overridden if needed. - What happens if objects of one ABI are mixed with objects of another ABI? For example if I were to link code compiled for
wasm32-wasip2with code forwasm32-wasip3, what would happen? Ideally I'd expect a linker-level error to be emitted with some long enough string that could be googled but probably wouldn't be descriptive in its own right. My main worry is less mixing targets and more mixing versions of LLVM by accident and ensuring that things don't silently link and then get weirdly corrupted at runtime.
|
@llvm/pr-subscribers-lld-wasm @llvm/pr-subscribers-lld Author: Sy Brand (TartanLlama) Changes(Currently in draft, as this will evolve alongside other toolchain component updates) The WebAssembly Component Model has added support for cooperative multithreading. This has been implemented in the Wasmtime engine and is part of the wider project of WASI preview 3, which is currently tracked here. These changes will require updating the way that Patch is 55.79 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/175800.diff 22 Files Affected:
diff --git a/clang/lib/Basic/Targets/WebAssembly.cpp b/clang/lib/Basic/Targets/WebAssembly.cpp
index daaefd9a1267c..1905b838e52a1 100644
--- a/clang/lib/Basic/Targets/WebAssembly.cpp
+++ b/clang/lib/Basic/Targets/WebAssembly.cpp
@@ -410,7 +410,8 @@ void WebAssemblyTargetInfo::adjust(DiagnosticsEngine &Diags, LangOptions &Opts,
// Turn off POSIXThreads and ThreadModel so that we don't predefine _REENTRANT
// or __STDCPP_THREADS__ if we will eventually end up stripping atomics
// because they are unsupported.
- if (!HasAtomics || !HasBulkMemory) {
+ if (getTriple().getOSName() != "wasip3" &&
+ (!HasAtomics || !HasBulkMemory)) {
Opts.POSIXThreads = false;
Opts.setThreadModel(LangOptions::ThreadModelKind::Single);
Opts.ThreadsafeStatics = false;
diff --git a/clang/lib/Driver/ToolChains/WebAssembly.cpp b/clang/lib/Driver/ToolChains/WebAssembly.cpp
index b5fa5760a46a0..efeadcc6556de 100644
--- a/clang/lib/Driver/ToolChains/WebAssembly.cpp
+++ b/clang/lib/Driver/ToolChains/WebAssembly.cpp
@@ -30,13 +30,14 @@ using namespace llvm::opt;
std::string WebAssembly::getMultiarchTriple(const Driver &D,
const llvm::Triple &TargetTriple,
StringRef SysRoot) const {
- return (TargetTriple.getArchName() + "-" +
- TargetTriple.getOSAndEnvironmentName()).str();
+ return (TargetTriple.getArchName() + "-" +
+ TargetTriple.getOSAndEnvironmentName())
+ .str();
}
std::string wasm::Linker::getLinkerPath(const ArgList &Args) const {
const ToolChain &ToolChain = getToolChain();
- if (const Arg* A = Args.getLastArg(options::OPT_fuse_ld_EQ)) {
+ if (const Arg *A = Args.getLastArg(options::OPT_fuse_ld_EQ)) {
StringRef UseLinker = A->getValue();
if (!UseLinker.empty()) {
if (llvm::sys::path::is_absolute(UseLinker) &&
@@ -79,6 +80,10 @@ static bool WantsPthread(const llvm::Triple &Triple, const ArgList &Args) {
return WantsPthread;
}
+static bool WantsSharedMemory(const llvm::Triple &Triple, const ArgList &Args) {
+ return WantsPthread(Triple, Args) && !TargetBuildsComponents(Triple);
+}
+
void wasm::Linker::ConstructJob(Compilation &C, const JobAction &JA,
const InputInfo &Output,
const InputInfoList &Inputs,
@@ -90,10 +95,14 @@ void wasm::Linker::ConstructJob(Compilation &C, const JobAction &JA,
ArgStringList CmdArgs;
CmdArgs.push_back("-m");
+ std::string arch;
if (ToolChain.getTriple().isArch64Bit())
- CmdArgs.push_back("wasm64");
+ arch = "wasm64";
else
- CmdArgs.push_back("wasm32");
+ arch = "wasm32";
+ if (ToolChain.getTriple().getOSName() == "wasip3")
+ arch += "-wasip3";
+ CmdArgs.push_back(Args.MakeArgString(arch));
if (Args.hasArg(options::OPT_s))
CmdArgs.push_back("--strip-all");
@@ -160,7 +169,7 @@ void wasm::Linker::ConstructJob(Compilation &C, const JobAction &JA,
AddLinkerInputs(ToolChain, Inputs, Args, CmdArgs, JA);
- if (WantsPthread(ToolChain.getTriple(), Args))
+ if (WantsSharedMemory(ToolChain.getTriple(), Args))
CmdArgs.push_back("--shared-memory");
if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs)) {
@@ -233,9 +242,9 @@ void wasm::Linker::ConstructJob(Compilation &C, const JobAction &JA,
/// Given a base library directory, append path components to form the
/// LTO directory.
static std::string AppendLTOLibDir(const std::string &Dir) {
- // The version allows the path to be keyed to the specific version of
- // LLVM in used, as the bitcode format is not stable.
- return Dir + "/llvm-lto/" LLVM_VERSION_STRING;
+ // The version allows the path to be keyed to the specific version of
+ // LLVM in used, as the bitcode format is not stable.
+ return Dir + "/llvm-lto/" LLVM_VERSION_STRING;
}
WebAssembly::WebAssembly(const Driver &D, const llvm::Triple &Triple,
@@ -508,7 +517,8 @@ void WebAssembly::AddClangSystemIncludeArgs(const ArgList &DriverArgs,
if (getTriple().getOS() != llvm::Triple::UnknownOS) {
const std::string MultiarchTriple =
getMultiarchTriple(D, getTriple(), D.SysRoot);
- addSystemInclude(DriverArgs, CC1Args, D.SysRoot + "/include/" + MultiarchTriple);
+ addSystemInclude(DriverArgs, CC1Args,
+ D.SysRoot + "/include/" + MultiarchTriple);
}
addSystemInclude(DriverArgs, CC1Args, D.SysRoot + "/include");
}
@@ -637,5 +647,6 @@ void WebAssembly::addLibStdCXXIncludePaths(
// Second add the generic one.
addSystemInclude(DriverArgs, CC1Args, LibPath + "/c++/" + Version);
// Third the backward one.
- addSystemInclude(DriverArgs, CC1Args, LibPath + "/c++/" + Version + "/backward");
+ addSystemInclude(DriverArgs, CC1Args,
+ LibPath + "/c++/" + Version + "/backward");
}
diff --git a/lld/wasm/Config.h b/lld/wasm/Config.h
index 31e08e4e248a4..d291a42da200f 100644
--- a/lld/wasm/Config.h
+++ b/lld/wasm/Config.h
@@ -35,6 +35,7 @@ class Symbol;
class DefinedData;
class GlobalSymbol;
class DefinedFunction;
+class UndefinedFunction;
class DefinedGlobal;
class UndefinedGlobal;
class TableSymbol;
@@ -50,6 +51,8 @@ enum class BuildIdKind { None, Fast, Sha1, Hexstring, Uuid };
// and such fields have the same name as the corresponding options.
// Most fields are initialized by the driver.
struct Config {
+ bool isMultithreaded() const { return sharedMemory || isWasip3; }
+
bool allowMultipleDefinition;
bool bsymbolic;
bool checkFeatures;
@@ -71,6 +74,7 @@ struct Config {
bool importTable;
bool importUndefined;
std::optional<bool> is64;
+ bool isWasip3;
bool mergeDataSegments;
bool noinhibitExec;
bool pie;
@@ -252,6 +256,14 @@ struct Ctx {
// Used as an address space for function pointers, with each function that
// is used as a function pointer being allocated a slot.
TableSymbol *indirectFunctionTable;
+
+ // __wasm_component_model_builtin_context_set_1
+ // Function used to set TLS base in component model modules.
+ UndefinedFunction *contextSet1;
+
+ // __wasm_component_model_builtin_context_get_1
+ // Function used to get TLS base in component model modules.
+ UndefinedFunction *contextGet1;
};
WasmSym sym;
diff --git a/lld/wasm/Driver.cpp b/lld/wasm/Driver.cpp
index b1e36f2ecff74..6eaacd7288f22 100644
--- a/lld/wasm/Driver.cpp
+++ b/lld/wasm/Driver.cpp
@@ -656,15 +656,16 @@ static void readConfigs(opt::InputArgList &args) {
ctx.arg.exportDynamic =
args.hasFlag(OPT_export_dynamic, OPT_no_export_dynamic, ctx.arg.shared);
- // Parse wasm32/64.
+ // Parse wasm32/64 and maybe -wasip3.
if (auto *arg = args.getLastArg(OPT_m)) {
StringRef s = arg->getValue();
- if (s == "wasm32")
+ if (s.starts_with("wasm32"))
ctx.arg.is64 = false;
- else if (s == "wasm64")
+ else if (s.starts_with("wasm64"))
ctx.arg.is64 = true;
else
error("invalid target architecture: " + s);
+ ctx.arg.isWasip3 = s.ends_with("-wasip3");
}
// --threads= takes a positive integer and provides the default value for
@@ -827,6 +828,10 @@ static void checkOptions(opt::InputArgList &args) {
if (ctx.arg.tableBase)
error("--table-base may not be used with -shared/-pie");
}
+
+ if (ctx.arg.sharedMemory && ctx.arg.isWasip3) {
+ error("--shared-memory is incompatible with the wasip3 target");
+ }
}
static const char *getReproduceOption(opt::InputArgList &args) {
@@ -885,7 +890,7 @@ static void writeWhyExtract() {
// Equivalent of demote demoteSharedAndLazySymbols() in the ELF linker
static void demoteLazySymbols() {
for (Symbol *sym : symtab->symbols()) {
- if (auto* s = dyn_cast<LazySymbol>(sym)) {
+ if (auto *s = dyn_cast<LazySymbol>(sym)) {
if (s->signature) {
LLVM_DEBUG(llvm::dbgs()
<< "demoting lazy func: " << s->getName() << "\n");
@@ -906,6 +911,18 @@ createUndefinedGlobal(StringRef name, llvm::wasm::WasmGlobalType *type) {
return sym;
}
+static UndefinedFunction *
+createUndefinedFunction(StringRef name, std::optional<StringRef> importName,
+ std::optional<StringRef> importModule,
+ WasmSignature *signature) {
+ auto *sym = cast<UndefinedFunction>(symtab->addUndefinedFunction(
+ name, importName, importModule, WASM_SYMBOL_UNDEFINED, nullptr, signature,
+ true));
+ ctx.arg.allowUndefinedSymbols.insert(sym->getName());
+ sym->isUsedInRegularObj = true;
+ return sym;
+}
+
static InputGlobal *createGlobal(StringRef name, bool isMutable) {
llvm::wasm::WasmGlobal wasmGlobal;
bool is64 = ctx.arg.is64.value_or(false);
@@ -946,11 +963,13 @@ static void createSyntheticSymbols() {
bool is64 = ctx.arg.is64.value_or(false);
+ auto stack_pointer_name =
+ ctx.arg.isWasip3 ? "__init_stack_pointer" : "__stack_pointer";
if (ctx.isPic) {
ctx.sym.stackPointer =
- createUndefinedGlobal("__stack_pointer", ctx.arg.is64.value_or(false)
- ? &mutableGlobalTypeI64
- : &mutableGlobalTypeI32);
+ createUndefinedGlobal(stack_pointer_name, ctx.arg.is64.value_or(false)
+ ? &mutableGlobalTypeI64
+ : &mutableGlobalTypeI32);
// For PIC code, we import two global variables (__memory_base and
// __table_base) from the environment and use these as the offset at
// which to load our static data and function table.
@@ -963,14 +982,15 @@ static void createSyntheticSymbols() {
ctx.sym.tableBase->markLive();
} else {
// For non-PIC code
- ctx.sym.stackPointer = createGlobalVariable("__stack_pointer", true);
+ ctx.sym.stackPointer = createGlobalVariable(stack_pointer_name, true);
ctx.sym.stackPointer->markLive();
}
- if (ctx.arg.sharedMemory) {
+ if (ctx.arg.isMultithreaded()) {
// TLS symbols are all hidden/dso-local
- ctx.sym.tlsBase =
- createGlobalVariable("__tls_base", true, WASM_SYMBOL_VISIBILITY_HIDDEN);
+ auto tls_base_name = ctx.arg.isWasip3 ? "__init_tls_base" : "__tls_base";
+ ctx.sym.tlsBase = createGlobalVariable(tls_base_name, true,
+ WASM_SYMBOL_VISIBILITY_HIDDEN);
ctx.sym.tlsSize = createGlobalVariable("__tls_size", false,
WASM_SYMBOL_VISIBILITY_HIDDEN);
ctx.sym.tlsAlign = createGlobalVariable("__tls_align", false,
@@ -979,6 +999,21 @@ static void createSyntheticSymbols() {
"__wasm_init_tls", WASM_SYMBOL_VISIBILITY_HIDDEN,
make<SyntheticFunction>(is64 ? i64ArgSignature : i32ArgSignature,
"__wasm_init_tls"));
+ if (ctx.arg.isWasip3) {
+ ctx.sym.tlsBase->markLive();
+ ctx.sym.tlsSize->markLive();
+ ctx.sym.tlsAlign->markLive();
+ static WasmSignature contextSet1Signature{{}, {ValType::I32}};
+ ctx.sym.contextSet1 = createUndefinedFunction(
+ "__wasm_component_model_builtin_context_set_1", "[context-set-1]",
+ "$root", &contextSet1Signature);
+ ctx.sym.contextSet1->markLive();
+ static WasmSignature contextGet1Signature{{ValType::I32}, {}};
+ ctx.sym.contextGet1 = createUndefinedFunction(
+ "__wasm_component_model_builtin_context_get_1", "[context-get-1]",
+ "$root", &contextGet1Signature);
+ ctx.sym.contextGet1->markLive();
+ }
}
}
@@ -1017,7 +1052,7 @@ static void createOptionalSymbols() {
//
// __tls_size and __tls_align are not needed in this case since they are only
// needed for __wasm_init_tls (which we do not create in this case).
- if (!ctx.arg.sharedMemory)
+ if (!ctx.arg.sharedMemory && !ctx.arg.isWasip3)
ctx.sym.tlsBase = createOptionalGlobal("__tls_base", false);
}
@@ -1026,15 +1061,15 @@ static void processStubLibrariesPreLTO() {
for (auto &stub_file : ctx.stubFiles) {
LLVM_DEBUG(llvm::dbgs()
<< "processing stub file: " << stub_file->getName() << "\n");
- for (auto [name, deps]: stub_file->symbolDependencies) {
- auto* sym = symtab->find(name);
+ for (auto [name, deps] : stub_file->symbolDependencies) {
+ auto *sym = symtab->find(name);
// If the symbol is not present at all (yet), or if it is present but
// undefined, then mark the dependent symbols as used by a regular
// object so they will be preserved and exported by the LTO process.
if (!sym || sym->isUndefined()) {
for (const auto dep : deps) {
- auto* needed = symtab->find(dep);
- if (needed ) {
+ auto *needed = symtab->find(dep);
+ if (needed) {
needed->isUsedInRegularObj = true;
// Like with handleLibcall we have to extract any LTO archive
// members that might need to be exported due to stub library
diff --git a/lld/wasm/Relocations.cpp b/lld/wasm/Relocations.cpp
index a3f87ea3d69c0..cb597fdeffcf3 100644
--- a/lld/wasm/Relocations.cpp
+++ b/lld/wasm/Relocations.cpp
@@ -33,7 +33,7 @@ static bool requiresGOTAccess(const Symbol *sym) {
return true;
}
-static bool allowUndefined(const Symbol* sym) {
+static bool allowUndefined(const Symbol *sym) {
// Symbols that are explicitly imported are always allowed to be undefined at
// link time.
if (sym->isImported())
@@ -125,7 +125,7 @@ void scanRelocations(InputChunk *chunk) {
// In single-threaded builds TLS is lowered away and TLS data can be
// merged with normal data and allowing TLS relocation in non-TLS
// segments.
- if (ctx.arg.sharedMemory) {
+ if (ctx.arg.isMultithreaded()) {
if (!sym->isTLS()) {
error(toString(file) + ": relocation " +
relocTypeToString(reloc.Type) +
diff --git a/lld/wasm/Symbols.cpp b/lld/wasm/Symbols.cpp
index f2040441e6257..97a9871a06308 100644
--- a/lld/wasm/Symbols.cpp
+++ b/lld/wasm/Symbols.cpp
@@ -95,7 +95,7 @@ WasmSymbolType Symbol::getWasmType() const {
}
const WasmSignature *Symbol::getSignature() const {
- if (auto* f = dyn_cast<FunctionSymbol>(this))
+ if (auto *f = dyn_cast<FunctionSymbol>(this))
return f->signature;
if (auto *t = dyn_cast<TagSymbol>(this))
return t->signature;
@@ -223,9 +223,7 @@ bool Symbol::isExportedExplicit() const {
return forceExport || flags & WASM_SYMBOL_EXPORTED;
}
-bool Symbol::isNoStrip() const {
- return flags & WASM_SYMBOL_NO_STRIP;
-}
+bool Symbol::isNoStrip() const { return flags & WASM_SYMBOL_NO_STRIP; }
uint32_t FunctionSymbol::getFunctionIndex() const {
if (const auto *u = dyn_cast<UndefinedFunction>(this))
@@ -413,7 +411,7 @@ void LazySymbol::setWeak() {
flags |= (flags & ~WASM_SYMBOL_BINDING_MASK) | WASM_SYMBOL_BINDING_WEAK;
}
-void printTraceSymbolUndefined(StringRef name, const InputFile* file) {
+void printTraceSymbolUndefined(StringRef name, const InputFile *file) {
message(toString(file) + ": reference to " + name);
}
diff --git a/lld/wasm/SyntheticSections.cpp b/lld/wasm/SyntheticSections.cpp
index ede6ac4da77b3..023c690c14354 100644
--- a/lld/wasm/SyntheticSections.cpp
+++ b/lld/wasm/SyntheticSections.cpp
@@ -466,8 +466,7 @@ void GlobalSection::addInternalGOTEntry(Symbol *sym) {
void GlobalSection::generateRelocationCode(raw_ostream &os, bool TLS) const {
assert(!ctx.arg.extendedConst);
bool is64 = ctx.arg.is64.value_or(false);
- unsigned opcode_ptr_add = is64 ? WASM_OPCODE_I64_ADD
- : WASM_OPCODE_I32_ADD;
+ unsigned opcode_ptr_add = is64 ? WASM_OPCODE_I64_ADD : WASM_OPCODE_I32_ADD;
for (const Symbol *sym : internalGotSymbols) {
if (TLS != sym->isTLS())
@@ -477,7 +476,7 @@ void GlobalSection::generateRelocationCode(raw_ostream &os, bool TLS) const {
// Get __memory_base
writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET");
if (sym->isTLS())
- writeUleb128(os, ctx.sym.tlsBase->getGlobalIndex(), "__tls_base");
+ writeGetTLSBase(ctx, os);
else
writeUleb128(os, ctx.sym.memoryBase->getGlobalIndex(), "__memory_base");
@@ -520,9 +519,9 @@ void GlobalSection::writeBody() {
// the correct runtime value during `__wasm_apply_global_relocs`.
if (!ctx.arg.extendedConst && ctx.isPic && !sym->isTLS())
mutable_ = true;
- // With multi-theadeding any TLS globals must be mutable since they get
+ // With multi-threading any TLS globals must be mutable since they get
// set during `__wasm_apply_global_tls_relocs`
- if (ctx.arg.sharedMemory && sym->isTLS())
+ if (ctx.arg.isMultithreaded() && sym->isTLS())
mutable_ = true;
}
WasmGlobalType type{itype, mutable_};
@@ -559,10 +558,11 @@ void GlobalSection::writeBody() {
} else {
WasmInitExpr initExpr;
if (auto *d = dyn_cast<DefinedData>(sym))
- // In the sharedMemory case TLS globals are set during
- // `__wasm_apply_global_tls_relocs`, but in the non-shared case
+ // In the multi-threaded case, TLS globals are set during
+ // `__wasm_apply_global_tls_relocs`, but in the non-multi-threaded case
// we know the absolute value at link time.
- initExpr = intConst(d->getVA(/*absolute=*/!ctx.arg.sharedMemory), is64);
+ initExpr =
+ intConst(d->getVA(/*absolute=*/!ctx.arg.isMultithreaded()), is64);
else if (auto *f = dyn_cast<FunctionSymbol>(sym))
initExpr = intConst(f->isStub ? 0 : f->getTableIndex(), is64);
else {
@@ -646,7 +646,7 @@ void ElemSection::writeBody() {
uint32_t tableIndex = ctx.arg.tableBase;
for (const FunctionSymbol *sym : indirectFunctions) {
assert(sym->getTableIndex() == tableIndex);
- (void) tableIndex;
+ (void)tableIndex;
writeUleb128(os, sym->getFunctionIndex(), "function index");
++tableIndex;
}
@@ -663,7 +663,7 @@ void DataCountSection::writeBody() {
}
bool DataCountSection::isNeeded() const {
- return numSegments && ctx.arg.sharedMemory;
+ return numSegments && ctx.arg.isMultithreaded();
}
void LinkingSection::writeBody() {
@@ -992,4 +992,4 @@ void BuildIdSection::writeBuildId(llvm::ArrayRef<uint8_t> buf) {
memcpy(hashPlaceholderPtr, buf.data(), hashSize);
}
-} // namespace wasm::lld
+} // namespace lld::wasm
diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp
index dfd856f2faee6..50d6449ca79a9 100644
--- a/lld/wasm/Writer.cpp
+++ b/lld/wasm/Writer.cpp
@@ -311,7 +311,8 @@ void Writer::writeBuildId() {
}
static void setGlobalPtr(DefinedGlobal *g, uint64_t memoryPtr) {
- LLVM_DEBUG(dbgs() << "setGlobalPtr " << g->getName() << " -> " << memoryPtr << "\n");
+ LLVM_DEBUG(dbgs() << "setGlobalPtr " << g->getName() << " -> " << memoryPtr
+ << "\n");
g->global->setPointerValue(memoryPtr);
}
@@ -358,7 +359,8 @@ void Writer::layoutMemory() {
placeStack();
if (ctx.arg.globalBase) {
if (ctx.arg.globalBase < memoryPtr) {
- error("--global-base cannot be less than stack size when --stack-first is used");
+ error("--global-base cannot be less than stack size when --stack-first "
+ "is used");
return;
}
memoryPtr = ctx.arg.globalBase;
@@ -382,6 +384,7 @@ void Writer::layoutMemory() {
for (OutputSegment *seg : segments) {
out.dylinkSec->memAlign = std::max(out.dylinkSec->memAlign, seg->alignment);
memoryPtr = alignTo(memoryPtr, 1ULL << seg->alignment);
+
seg->startVA = memoryPtr;
log(formatv("mem: {0,-15} offset={1,-8} size={2,-8} align={3}", seg->name,
memoryPtr, seg->size, seg->alignment));
@@ -1029,7 +1032,7 @@ static StringRef getOutputDataSegmentName(const InputChunk &seg) {
OutputSegment *Writer::createOutputSegment(StringRef name) {
LLVM_DEBUG(dbgs() << "new segment: " << name << "\n");
OutputSegment *s = make<OutputSegment>(name);
- if (ctx.arg.sharedMemory)
+ if (ctx.arg.isMultithreaded())
s->initFlags = WASM_DATA_SEGMENT_IS_PASSIVE;
if (!ctx.arg.relocatable && name.starts_with(".bss"))
s->isBss = true;
@@ -1163,14 +1166,14 @@ void Writer::createSyntheticInitFunctions() {
"__wasm_init_memory", WASM_SYMBOL_VISIBILITY_HIDDEN,
make<SyntheticFunction>(nullSignature, "__wasm_init_memory"));
ctx.sym.initMemory->mark...
[truncated]
|
|
@llvm/pr-subscribers-llvm-mc Author: Sy Brand (TartanLlama) Changes(Currently in draft, as this will evolve alongside other toolchain component updates) The WebAssembly Component Model has added support for cooperative multithreading. This has been implemented in the Wasmtime engine and is part of the wider project of WASI preview 3, which is currently tracked here. These changes will require updating the way that Patch is 55.79 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/175800.diff 22 Files Affected:
diff --git a/clang/lib/Basic/Targets/WebAssembly.cpp b/clang/lib/Basic/Targets/WebAssembly.cpp
index daaefd9a1267c..1905b838e52a1 100644
--- a/clang/lib/Basic/Targets/WebAssembly.cpp
+++ b/clang/lib/Basic/Targets/WebAssembly.cpp
@@ -410,7 +410,8 @@ void WebAssemblyTargetInfo::adjust(DiagnosticsEngine &Diags, LangOptions &Opts,
// Turn off POSIXThreads and ThreadModel so that we don't predefine _REENTRANT
// or __STDCPP_THREADS__ if we will eventually end up stripping atomics
// because they are unsupported.
- if (!HasAtomics || !HasBulkMemory) {
+ if (getTriple().getOSName() != "wasip3" &&
+ (!HasAtomics || !HasBulkMemory)) {
Opts.POSIXThreads = false;
Opts.setThreadModel(LangOptions::ThreadModelKind::Single);
Opts.ThreadsafeStatics = false;
diff --git a/clang/lib/Driver/ToolChains/WebAssembly.cpp b/clang/lib/Driver/ToolChains/WebAssembly.cpp
index b5fa5760a46a0..efeadcc6556de 100644
--- a/clang/lib/Driver/ToolChains/WebAssembly.cpp
+++ b/clang/lib/Driver/ToolChains/WebAssembly.cpp
@@ -30,13 +30,14 @@ using namespace llvm::opt;
std::string WebAssembly::getMultiarchTriple(const Driver &D,
const llvm::Triple &TargetTriple,
StringRef SysRoot) const {
- return (TargetTriple.getArchName() + "-" +
- TargetTriple.getOSAndEnvironmentName()).str();
+ return (TargetTriple.getArchName() + "-" +
+ TargetTriple.getOSAndEnvironmentName())
+ .str();
}
std::string wasm::Linker::getLinkerPath(const ArgList &Args) const {
const ToolChain &ToolChain = getToolChain();
- if (const Arg* A = Args.getLastArg(options::OPT_fuse_ld_EQ)) {
+ if (const Arg *A = Args.getLastArg(options::OPT_fuse_ld_EQ)) {
StringRef UseLinker = A->getValue();
if (!UseLinker.empty()) {
if (llvm::sys::path::is_absolute(UseLinker) &&
@@ -79,6 +80,10 @@ static bool WantsPthread(const llvm::Triple &Triple, const ArgList &Args) {
return WantsPthread;
}
+static bool WantsSharedMemory(const llvm::Triple &Triple, const ArgList &Args) {
+ return WantsPthread(Triple, Args) && !TargetBuildsComponents(Triple);
+}
+
void wasm::Linker::ConstructJob(Compilation &C, const JobAction &JA,
const InputInfo &Output,
const InputInfoList &Inputs,
@@ -90,10 +95,14 @@ void wasm::Linker::ConstructJob(Compilation &C, const JobAction &JA,
ArgStringList CmdArgs;
CmdArgs.push_back("-m");
+ std::string arch;
if (ToolChain.getTriple().isArch64Bit())
- CmdArgs.push_back("wasm64");
+ arch = "wasm64";
else
- CmdArgs.push_back("wasm32");
+ arch = "wasm32";
+ if (ToolChain.getTriple().getOSName() == "wasip3")
+ arch += "-wasip3";
+ CmdArgs.push_back(Args.MakeArgString(arch));
if (Args.hasArg(options::OPT_s))
CmdArgs.push_back("--strip-all");
@@ -160,7 +169,7 @@ void wasm::Linker::ConstructJob(Compilation &C, const JobAction &JA,
AddLinkerInputs(ToolChain, Inputs, Args, CmdArgs, JA);
- if (WantsPthread(ToolChain.getTriple(), Args))
+ if (WantsSharedMemory(ToolChain.getTriple(), Args))
CmdArgs.push_back("--shared-memory");
if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs)) {
@@ -233,9 +242,9 @@ void wasm::Linker::ConstructJob(Compilation &C, const JobAction &JA,
/// Given a base library directory, append path components to form the
/// LTO directory.
static std::string AppendLTOLibDir(const std::string &Dir) {
- // The version allows the path to be keyed to the specific version of
- // LLVM in used, as the bitcode format is not stable.
- return Dir + "/llvm-lto/" LLVM_VERSION_STRING;
+ // The version allows the path to be keyed to the specific version of
+ // LLVM in used, as the bitcode format is not stable.
+ return Dir + "/llvm-lto/" LLVM_VERSION_STRING;
}
WebAssembly::WebAssembly(const Driver &D, const llvm::Triple &Triple,
@@ -508,7 +517,8 @@ void WebAssembly::AddClangSystemIncludeArgs(const ArgList &DriverArgs,
if (getTriple().getOS() != llvm::Triple::UnknownOS) {
const std::string MultiarchTriple =
getMultiarchTriple(D, getTriple(), D.SysRoot);
- addSystemInclude(DriverArgs, CC1Args, D.SysRoot + "/include/" + MultiarchTriple);
+ addSystemInclude(DriverArgs, CC1Args,
+ D.SysRoot + "/include/" + MultiarchTriple);
}
addSystemInclude(DriverArgs, CC1Args, D.SysRoot + "/include");
}
@@ -637,5 +647,6 @@ void WebAssembly::addLibStdCXXIncludePaths(
// Second add the generic one.
addSystemInclude(DriverArgs, CC1Args, LibPath + "/c++/" + Version);
// Third the backward one.
- addSystemInclude(DriverArgs, CC1Args, LibPath + "/c++/" + Version + "/backward");
+ addSystemInclude(DriverArgs, CC1Args,
+ LibPath + "/c++/" + Version + "/backward");
}
diff --git a/lld/wasm/Config.h b/lld/wasm/Config.h
index 31e08e4e248a4..d291a42da200f 100644
--- a/lld/wasm/Config.h
+++ b/lld/wasm/Config.h
@@ -35,6 +35,7 @@ class Symbol;
class DefinedData;
class GlobalSymbol;
class DefinedFunction;
+class UndefinedFunction;
class DefinedGlobal;
class UndefinedGlobal;
class TableSymbol;
@@ -50,6 +51,8 @@ enum class BuildIdKind { None, Fast, Sha1, Hexstring, Uuid };
// and such fields have the same name as the corresponding options.
// Most fields are initialized by the driver.
struct Config {
+ bool isMultithreaded() const { return sharedMemory || isWasip3; }
+
bool allowMultipleDefinition;
bool bsymbolic;
bool checkFeatures;
@@ -71,6 +74,7 @@ struct Config {
bool importTable;
bool importUndefined;
std::optional<bool> is64;
+ bool isWasip3;
bool mergeDataSegments;
bool noinhibitExec;
bool pie;
@@ -252,6 +256,14 @@ struct Ctx {
// Used as an address space for function pointers, with each function that
// is used as a function pointer being allocated a slot.
TableSymbol *indirectFunctionTable;
+
+ // __wasm_component_model_builtin_context_set_1
+ // Function used to set TLS base in component model modules.
+ UndefinedFunction *contextSet1;
+
+ // __wasm_component_model_builtin_context_get_1
+ // Function used to get TLS base in component model modules.
+ UndefinedFunction *contextGet1;
};
WasmSym sym;
diff --git a/lld/wasm/Driver.cpp b/lld/wasm/Driver.cpp
index b1e36f2ecff74..6eaacd7288f22 100644
--- a/lld/wasm/Driver.cpp
+++ b/lld/wasm/Driver.cpp
@@ -656,15 +656,16 @@ static void readConfigs(opt::InputArgList &args) {
ctx.arg.exportDynamic =
args.hasFlag(OPT_export_dynamic, OPT_no_export_dynamic, ctx.arg.shared);
- // Parse wasm32/64.
+ // Parse wasm32/64 and maybe -wasip3.
if (auto *arg = args.getLastArg(OPT_m)) {
StringRef s = arg->getValue();
- if (s == "wasm32")
+ if (s.starts_with("wasm32"))
ctx.arg.is64 = false;
- else if (s == "wasm64")
+ else if (s.starts_with("wasm64"))
ctx.arg.is64 = true;
else
error("invalid target architecture: " + s);
+ ctx.arg.isWasip3 = s.ends_with("-wasip3");
}
// --threads= takes a positive integer and provides the default value for
@@ -827,6 +828,10 @@ static void checkOptions(opt::InputArgList &args) {
if (ctx.arg.tableBase)
error("--table-base may not be used with -shared/-pie");
}
+
+ if (ctx.arg.sharedMemory && ctx.arg.isWasip3) {
+ error("--shared-memory is incompatible with the wasip3 target");
+ }
}
static const char *getReproduceOption(opt::InputArgList &args) {
@@ -885,7 +890,7 @@ static void writeWhyExtract() {
// Equivalent of demote demoteSharedAndLazySymbols() in the ELF linker
static void demoteLazySymbols() {
for (Symbol *sym : symtab->symbols()) {
- if (auto* s = dyn_cast<LazySymbol>(sym)) {
+ if (auto *s = dyn_cast<LazySymbol>(sym)) {
if (s->signature) {
LLVM_DEBUG(llvm::dbgs()
<< "demoting lazy func: " << s->getName() << "\n");
@@ -906,6 +911,18 @@ createUndefinedGlobal(StringRef name, llvm::wasm::WasmGlobalType *type) {
return sym;
}
+static UndefinedFunction *
+createUndefinedFunction(StringRef name, std::optional<StringRef> importName,
+ std::optional<StringRef> importModule,
+ WasmSignature *signature) {
+ auto *sym = cast<UndefinedFunction>(symtab->addUndefinedFunction(
+ name, importName, importModule, WASM_SYMBOL_UNDEFINED, nullptr, signature,
+ true));
+ ctx.arg.allowUndefinedSymbols.insert(sym->getName());
+ sym->isUsedInRegularObj = true;
+ return sym;
+}
+
static InputGlobal *createGlobal(StringRef name, bool isMutable) {
llvm::wasm::WasmGlobal wasmGlobal;
bool is64 = ctx.arg.is64.value_or(false);
@@ -946,11 +963,13 @@ static void createSyntheticSymbols() {
bool is64 = ctx.arg.is64.value_or(false);
+ auto stack_pointer_name =
+ ctx.arg.isWasip3 ? "__init_stack_pointer" : "__stack_pointer";
if (ctx.isPic) {
ctx.sym.stackPointer =
- createUndefinedGlobal("__stack_pointer", ctx.arg.is64.value_or(false)
- ? &mutableGlobalTypeI64
- : &mutableGlobalTypeI32);
+ createUndefinedGlobal(stack_pointer_name, ctx.arg.is64.value_or(false)
+ ? &mutableGlobalTypeI64
+ : &mutableGlobalTypeI32);
// For PIC code, we import two global variables (__memory_base and
// __table_base) from the environment and use these as the offset at
// which to load our static data and function table.
@@ -963,14 +982,15 @@ static void createSyntheticSymbols() {
ctx.sym.tableBase->markLive();
} else {
// For non-PIC code
- ctx.sym.stackPointer = createGlobalVariable("__stack_pointer", true);
+ ctx.sym.stackPointer = createGlobalVariable(stack_pointer_name, true);
ctx.sym.stackPointer->markLive();
}
- if (ctx.arg.sharedMemory) {
+ if (ctx.arg.isMultithreaded()) {
// TLS symbols are all hidden/dso-local
- ctx.sym.tlsBase =
- createGlobalVariable("__tls_base", true, WASM_SYMBOL_VISIBILITY_HIDDEN);
+ auto tls_base_name = ctx.arg.isWasip3 ? "__init_tls_base" : "__tls_base";
+ ctx.sym.tlsBase = createGlobalVariable(tls_base_name, true,
+ WASM_SYMBOL_VISIBILITY_HIDDEN);
ctx.sym.tlsSize = createGlobalVariable("__tls_size", false,
WASM_SYMBOL_VISIBILITY_HIDDEN);
ctx.sym.tlsAlign = createGlobalVariable("__tls_align", false,
@@ -979,6 +999,21 @@ static void createSyntheticSymbols() {
"__wasm_init_tls", WASM_SYMBOL_VISIBILITY_HIDDEN,
make<SyntheticFunction>(is64 ? i64ArgSignature : i32ArgSignature,
"__wasm_init_tls"));
+ if (ctx.arg.isWasip3) {
+ ctx.sym.tlsBase->markLive();
+ ctx.sym.tlsSize->markLive();
+ ctx.sym.tlsAlign->markLive();
+ static WasmSignature contextSet1Signature{{}, {ValType::I32}};
+ ctx.sym.contextSet1 = createUndefinedFunction(
+ "__wasm_component_model_builtin_context_set_1", "[context-set-1]",
+ "$root", &contextSet1Signature);
+ ctx.sym.contextSet1->markLive();
+ static WasmSignature contextGet1Signature{{ValType::I32}, {}};
+ ctx.sym.contextGet1 = createUndefinedFunction(
+ "__wasm_component_model_builtin_context_get_1", "[context-get-1]",
+ "$root", &contextGet1Signature);
+ ctx.sym.contextGet1->markLive();
+ }
}
}
@@ -1017,7 +1052,7 @@ static void createOptionalSymbols() {
//
// __tls_size and __tls_align are not needed in this case since they are only
// needed for __wasm_init_tls (which we do not create in this case).
- if (!ctx.arg.sharedMemory)
+ if (!ctx.arg.sharedMemory && !ctx.arg.isWasip3)
ctx.sym.tlsBase = createOptionalGlobal("__tls_base", false);
}
@@ -1026,15 +1061,15 @@ static void processStubLibrariesPreLTO() {
for (auto &stub_file : ctx.stubFiles) {
LLVM_DEBUG(llvm::dbgs()
<< "processing stub file: " << stub_file->getName() << "\n");
- for (auto [name, deps]: stub_file->symbolDependencies) {
- auto* sym = symtab->find(name);
+ for (auto [name, deps] : stub_file->symbolDependencies) {
+ auto *sym = symtab->find(name);
// If the symbol is not present at all (yet), or if it is present but
// undefined, then mark the dependent symbols as used by a regular
// object so they will be preserved and exported by the LTO process.
if (!sym || sym->isUndefined()) {
for (const auto dep : deps) {
- auto* needed = symtab->find(dep);
- if (needed ) {
+ auto *needed = symtab->find(dep);
+ if (needed) {
needed->isUsedInRegularObj = true;
// Like with handleLibcall we have to extract any LTO archive
// members that might need to be exported due to stub library
diff --git a/lld/wasm/Relocations.cpp b/lld/wasm/Relocations.cpp
index a3f87ea3d69c0..cb597fdeffcf3 100644
--- a/lld/wasm/Relocations.cpp
+++ b/lld/wasm/Relocations.cpp
@@ -33,7 +33,7 @@ static bool requiresGOTAccess(const Symbol *sym) {
return true;
}
-static bool allowUndefined(const Symbol* sym) {
+static bool allowUndefined(const Symbol *sym) {
// Symbols that are explicitly imported are always allowed to be undefined at
// link time.
if (sym->isImported())
@@ -125,7 +125,7 @@ void scanRelocations(InputChunk *chunk) {
// In single-threaded builds TLS is lowered away and TLS data can be
// merged with normal data and allowing TLS relocation in non-TLS
// segments.
- if (ctx.arg.sharedMemory) {
+ if (ctx.arg.isMultithreaded()) {
if (!sym->isTLS()) {
error(toString(file) + ": relocation " +
relocTypeToString(reloc.Type) +
diff --git a/lld/wasm/Symbols.cpp b/lld/wasm/Symbols.cpp
index f2040441e6257..97a9871a06308 100644
--- a/lld/wasm/Symbols.cpp
+++ b/lld/wasm/Symbols.cpp
@@ -95,7 +95,7 @@ WasmSymbolType Symbol::getWasmType() const {
}
const WasmSignature *Symbol::getSignature() const {
- if (auto* f = dyn_cast<FunctionSymbol>(this))
+ if (auto *f = dyn_cast<FunctionSymbol>(this))
return f->signature;
if (auto *t = dyn_cast<TagSymbol>(this))
return t->signature;
@@ -223,9 +223,7 @@ bool Symbol::isExportedExplicit() const {
return forceExport || flags & WASM_SYMBOL_EXPORTED;
}
-bool Symbol::isNoStrip() const {
- return flags & WASM_SYMBOL_NO_STRIP;
-}
+bool Symbol::isNoStrip() const { return flags & WASM_SYMBOL_NO_STRIP; }
uint32_t FunctionSymbol::getFunctionIndex() const {
if (const auto *u = dyn_cast<UndefinedFunction>(this))
@@ -413,7 +411,7 @@ void LazySymbol::setWeak() {
flags |= (flags & ~WASM_SYMBOL_BINDING_MASK) | WASM_SYMBOL_BINDING_WEAK;
}
-void printTraceSymbolUndefined(StringRef name, const InputFile* file) {
+void printTraceSymbolUndefined(StringRef name, const InputFile *file) {
message(toString(file) + ": reference to " + name);
}
diff --git a/lld/wasm/SyntheticSections.cpp b/lld/wasm/SyntheticSections.cpp
index ede6ac4da77b3..023c690c14354 100644
--- a/lld/wasm/SyntheticSections.cpp
+++ b/lld/wasm/SyntheticSections.cpp
@@ -466,8 +466,7 @@ void GlobalSection::addInternalGOTEntry(Symbol *sym) {
void GlobalSection::generateRelocationCode(raw_ostream &os, bool TLS) const {
assert(!ctx.arg.extendedConst);
bool is64 = ctx.arg.is64.value_or(false);
- unsigned opcode_ptr_add = is64 ? WASM_OPCODE_I64_ADD
- : WASM_OPCODE_I32_ADD;
+ unsigned opcode_ptr_add = is64 ? WASM_OPCODE_I64_ADD : WASM_OPCODE_I32_ADD;
for (const Symbol *sym : internalGotSymbols) {
if (TLS != sym->isTLS())
@@ -477,7 +476,7 @@ void GlobalSection::generateRelocationCode(raw_ostream &os, bool TLS) const {
// Get __memory_base
writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET");
if (sym->isTLS())
- writeUleb128(os, ctx.sym.tlsBase->getGlobalIndex(), "__tls_base");
+ writeGetTLSBase(ctx, os);
else
writeUleb128(os, ctx.sym.memoryBase->getGlobalIndex(), "__memory_base");
@@ -520,9 +519,9 @@ void GlobalSection::writeBody() {
// the correct runtime value during `__wasm_apply_global_relocs`.
if (!ctx.arg.extendedConst && ctx.isPic && !sym->isTLS())
mutable_ = true;
- // With multi-theadeding any TLS globals must be mutable since they get
+ // With multi-threading any TLS globals must be mutable since they get
// set during `__wasm_apply_global_tls_relocs`
- if (ctx.arg.sharedMemory && sym->isTLS())
+ if (ctx.arg.isMultithreaded() && sym->isTLS())
mutable_ = true;
}
WasmGlobalType type{itype, mutable_};
@@ -559,10 +558,11 @@ void GlobalSection::writeBody() {
} else {
WasmInitExpr initExpr;
if (auto *d = dyn_cast<DefinedData>(sym))
- // In the sharedMemory case TLS globals are set during
- // `__wasm_apply_global_tls_relocs`, but in the non-shared case
+ // In the multi-threaded case, TLS globals are set during
+ // `__wasm_apply_global_tls_relocs`, but in the non-multi-threaded case
// we know the absolute value at link time.
- initExpr = intConst(d->getVA(/*absolute=*/!ctx.arg.sharedMemory), is64);
+ initExpr =
+ intConst(d->getVA(/*absolute=*/!ctx.arg.isMultithreaded()), is64);
else if (auto *f = dyn_cast<FunctionSymbol>(sym))
initExpr = intConst(f->isStub ? 0 : f->getTableIndex(), is64);
else {
@@ -646,7 +646,7 @@ void ElemSection::writeBody() {
uint32_t tableIndex = ctx.arg.tableBase;
for (const FunctionSymbol *sym : indirectFunctions) {
assert(sym->getTableIndex() == tableIndex);
- (void) tableIndex;
+ (void)tableIndex;
writeUleb128(os, sym->getFunctionIndex(), "function index");
++tableIndex;
}
@@ -663,7 +663,7 @@ void DataCountSection::writeBody() {
}
bool DataCountSection::isNeeded() const {
- return numSegments && ctx.arg.sharedMemory;
+ return numSegments && ctx.arg.isMultithreaded();
}
void LinkingSection::writeBody() {
@@ -992,4 +992,4 @@ void BuildIdSection::writeBuildId(llvm::ArrayRef<uint8_t> buf) {
memcpy(hashPlaceholderPtr, buf.data(), hashSize);
}
-} // namespace wasm::lld
+} // namespace lld::wasm
diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp
index dfd856f2faee6..50d6449ca79a9 100644
--- a/lld/wasm/Writer.cpp
+++ b/lld/wasm/Writer.cpp
@@ -311,7 +311,8 @@ void Writer::writeBuildId() {
}
static void setGlobalPtr(DefinedGlobal *g, uint64_t memoryPtr) {
- LLVM_DEBUG(dbgs() << "setGlobalPtr " << g->getName() << " -> " << memoryPtr << "\n");
+ LLVM_DEBUG(dbgs() << "setGlobalPtr " << g->getName() << " -> " << memoryPtr
+ << "\n");
g->global->setPointerValue(memoryPtr);
}
@@ -358,7 +359,8 @@ void Writer::layoutMemory() {
placeStack();
if (ctx.arg.globalBase) {
if (ctx.arg.globalBase < memoryPtr) {
- error("--global-base cannot be less than stack size when --stack-first is used");
+ error("--global-base cannot be less than stack size when --stack-first "
+ "is used");
return;
}
memoryPtr = ctx.arg.globalBase;
@@ -382,6 +384,7 @@ void Writer::layoutMemory() {
for (OutputSegment *seg : segments) {
out.dylinkSec->memAlign = std::max(out.dylinkSec->memAlign, seg->alignment);
memoryPtr = alignTo(memoryPtr, 1ULL << seg->alignment);
+
seg->startVA = memoryPtr;
log(formatv("mem: {0,-15} offset={1,-8} size={2,-8} align={3}", seg->name,
memoryPtr, seg->size, seg->alignment));
@@ -1029,7 +1032,7 @@ static StringRef getOutputDataSegmentName(const InputChunk &seg) {
OutputSegment *Writer::createOutputSegment(StringRef name) {
LLVM_DEBUG(dbgs() << "new segment: " << name << "\n");
OutputSegment *s = make<OutputSegment>(name);
- if (ctx.arg.sharedMemory)
+ if (ctx.arg.isMultithreaded())
s->initFlags = WASM_DATA_SEGMENT_IS_PASSIVE;
if (!ctx.arg.relocatable && name.starts_with(".bss"))
s->isBss = true;
@@ -1163,14 +1166,14 @@ void Writer::createSyntheticInitFunctions() {
"__wasm_init_memory", WASM_SYMBOL_VISIBILITY_HIDDEN,
make<SyntheticFunction>(nullSignature, "__wasm_init_memory"));
ctx.sym.initMemory->mark...
[truncated]
|
|
@llvm/pr-subscribers-backend-webassembly Author: Sy Brand (TartanLlama) Changes(Currently in draft, as this will evolve alongside other toolchain component updates) The WebAssembly Component Model has added support for cooperative multithreading. This has been implemented in the Wasmtime engine and is part of the wider project of WASI preview 3, which is currently tracked here. These changes will require updating the way that Patch is 55.79 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/175800.diff 22 Files Affected:
diff --git a/clang/lib/Basic/Targets/WebAssembly.cpp b/clang/lib/Basic/Targets/WebAssembly.cpp
index daaefd9a1267c..1905b838e52a1 100644
--- a/clang/lib/Basic/Targets/WebAssembly.cpp
+++ b/clang/lib/Basic/Targets/WebAssembly.cpp
@@ -410,7 +410,8 @@ void WebAssemblyTargetInfo::adjust(DiagnosticsEngine &Diags, LangOptions &Opts,
// Turn off POSIXThreads and ThreadModel so that we don't predefine _REENTRANT
// or __STDCPP_THREADS__ if we will eventually end up stripping atomics
// because they are unsupported.
- if (!HasAtomics || !HasBulkMemory) {
+ if (getTriple().getOSName() != "wasip3" &&
+ (!HasAtomics || !HasBulkMemory)) {
Opts.POSIXThreads = false;
Opts.setThreadModel(LangOptions::ThreadModelKind::Single);
Opts.ThreadsafeStatics = false;
diff --git a/clang/lib/Driver/ToolChains/WebAssembly.cpp b/clang/lib/Driver/ToolChains/WebAssembly.cpp
index b5fa5760a46a0..efeadcc6556de 100644
--- a/clang/lib/Driver/ToolChains/WebAssembly.cpp
+++ b/clang/lib/Driver/ToolChains/WebAssembly.cpp
@@ -30,13 +30,14 @@ using namespace llvm::opt;
std::string WebAssembly::getMultiarchTriple(const Driver &D,
const llvm::Triple &TargetTriple,
StringRef SysRoot) const {
- return (TargetTriple.getArchName() + "-" +
- TargetTriple.getOSAndEnvironmentName()).str();
+ return (TargetTriple.getArchName() + "-" +
+ TargetTriple.getOSAndEnvironmentName())
+ .str();
}
std::string wasm::Linker::getLinkerPath(const ArgList &Args) const {
const ToolChain &ToolChain = getToolChain();
- if (const Arg* A = Args.getLastArg(options::OPT_fuse_ld_EQ)) {
+ if (const Arg *A = Args.getLastArg(options::OPT_fuse_ld_EQ)) {
StringRef UseLinker = A->getValue();
if (!UseLinker.empty()) {
if (llvm::sys::path::is_absolute(UseLinker) &&
@@ -79,6 +80,10 @@ static bool WantsPthread(const llvm::Triple &Triple, const ArgList &Args) {
return WantsPthread;
}
+static bool WantsSharedMemory(const llvm::Triple &Triple, const ArgList &Args) {
+ return WantsPthread(Triple, Args) && !TargetBuildsComponents(Triple);
+}
+
void wasm::Linker::ConstructJob(Compilation &C, const JobAction &JA,
const InputInfo &Output,
const InputInfoList &Inputs,
@@ -90,10 +95,14 @@ void wasm::Linker::ConstructJob(Compilation &C, const JobAction &JA,
ArgStringList CmdArgs;
CmdArgs.push_back("-m");
+ std::string arch;
if (ToolChain.getTriple().isArch64Bit())
- CmdArgs.push_back("wasm64");
+ arch = "wasm64";
else
- CmdArgs.push_back("wasm32");
+ arch = "wasm32";
+ if (ToolChain.getTriple().getOSName() == "wasip3")
+ arch += "-wasip3";
+ CmdArgs.push_back(Args.MakeArgString(arch));
if (Args.hasArg(options::OPT_s))
CmdArgs.push_back("--strip-all");
@@ -160,7 +169,7 @@ void wasm::Linker::ConstructJob(Compilation &C, const JobAction &JA,
AddLinkerInputs(ToolChain, Inputs, Args, CmdArgs, JA);
- if (WantsPthread(ToolChain.getTriple(), Args))
+ if (WantsSharedMemory(ToolChain.getTriple(), Args))
CmdArgs.push_back("--shared-memory");
if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs)) {
@@ -233,9 +242,9 @@ void wasm::Linker::ConstructJob(Compilation &C, const JobAction &JA,
/// Given a base library directory, append path components to form the
/// LTO directory.
static std::string AppendLTOLibDir(const std::string &Dir) {
- // The version allows the path to be keyed to the specific version of
- // LLVM in used, as the bitcode format is not stable.
- return Dir + "/llvm-lto/" LLVM_VERSION_STRING;
+ // The version allows the path to be keyed to the specific version of
+ // LLVM in used, as the bitcode format is not stable.
+ return Dir + "/llvm-lto/" LLVM_VERSION_STRING;
}
WebAssembly::WebAssembly(const Driver &D, const llvm::Triple &Triple,
@@ -508,7 +517,8 @@ void WebAssembly::AddClangSystemIncludeArgs(const ArgList &DriverArgs,
if (getTriple().getOS() != llvm::Triple::UnknownOS) {
const std::string MultiarchTriple =
getMultiarchTriple(D, getTriple(), D.SysRoot);
- addSystemInclude(DriverArgs, CC1Args, D.SysRoot + "/include/" + MultiarchTriple);
+ addSystemInclude(DriverArgs, CC1Args,
+ D.SysRoot + "/include/" + MultiarchTriple);
}
addSystemInclude(DriverArgs, CC1Args, D.SysRoot + "/include");
}
@@ -637,5 +647,6 @@ void WebAssembly::addLibStdCXXIncludePaths(
// Second add the generic one.
addSystemInclude(DriverArgs, CC1Args, LibPath + "/c++/" + Version);
// Third the backward one.
- addSystemInclude(DriverArgs, CC1Args, LibPath + "/c++/" + Version + "/backward");
+ addSystemInclude(DriverArgs, CC1Args,
+ LibPath + "/c++/" + Version + "/backward");
}
diff --git a/lld/wasm/Config.h b/lld/wasm/Config.h
index 31e08e4e248a4..d291a42da200f 100644
--- a/lld/wasm/Config.h
+++ b/lld/wasm/Config.h
@@ -35,6 +35,7 @@ class Symbol;
class DefinedData;
class GlobalSymbol;
class DefinedFunction;
+class UndefinedFunction;
class DefinedGlobal;
class UndefinedGlobal;
class TableSymbol;
@@ -50,6 +51,8 @@ enum class BuildIdKind { None, Fast, Sha1, Hexstring, Uuid };
// and such fields have the same name as the corresponding options.
// Most fields are initialized by the driver.
struct Config {
+ bool isMultithreaded() const { return sharedMemory || isWasip3; }
+
bool allowMultipleDefinition;
bool bsymbolic;
bool checkFeatures;
@@ -71,6 +74,7 @@ struct Config {
bool importTable;
bool importUndefined;
std::optional<bool> is64;
+ bool isWasip3;
bool mergeDataSegments;
bool noinhibitExec;
bool pie;
@@ -252,6 +256,14 @@ struct Ctx {
// Used as an address space for function pointers, with each function that
// is used as a function pointer being allocated a slot.
TableSymbol *indirectFunctionTable;
+
+ // __wasm_component_model_builtin_context_set_1
+ // Function used to set TLS base in component model modules.
+ UndefinedFunction *contextSet1;
+
+ // __wasm_component_model_builtin_context_get_1
+ // Function used to get TLS base in component model modules.
+ UndefinedFunction *contextGet1;
};
WasmSym sym;
diff --git a/lld/wasm/Driver.cpp b/lld/wasm/Driver.cpp
index b1e36f2ecff74..6eaacd7288f22 100644
--- a/lld/wasm/Driver.cpp
+++ b/lld/wasm/Driver.cpp
@@ -656,15 +656,16 @@ static void readConfigs(opt::InputArgList &args) {
ctx.arg.exportDynamic =
args.hasFlag(OPT_export_dynamic, OPT_no_export_dynamic, ctx.arg.shared);
- // Parse wasm32/64.
+ // Parse wasm32/64 and maybe -wasip3.
if (auto *arg = args.getLastArg(OPT_m)) {
StringRef s = arg->getValue();
- if (s == "wasm32")
+ if (s.starts_with("wasm32"))
ctx.arg.is64 = false;
- else if (s == "wasm64")
+ else if (s.starts_with("wasm64"))
ctx.arg.is64 = true;
else
error("invalid target architecture: " + s);
+ ctx.arg.isWasip3 = s.ends_with("-wasip3");
}
// --threads= takes a positive integer and provides the default value for
@@ -827,6 +828,10 @@ static void checkOptions(opt::InputArgList &args) {
if (ctx.arg.tableBase)
error("--table-base may not be used with -shared/-pie");
}
+
+ if (ctx.arg.sharedMemory && ctx.arg.isWasip3) {
+ error("--shared-memory is incompatible with the wasip3 target");
+ }
}
static const char *getReproduceOption(opt::InputArgList &args) {
@@ -885,7 +890,7 @@ static void writeWhyExtract() {
// Equivalent of demote demoteSharedAndLazySymbols() in the ELF linker
static void demoteLazySymbols() {
for (Symbol *sym : symtab->symbols()) {
- if (auto* s = dyn_cast<LazySymbol>(sym)) {
+ if (auto *s = dyn_cast<LazySymbol>(sym)) {
if (s->signature) {
LLVM_DEBUG(llvm::dbgs()
<< "demoting lazy func: " << s->getName() << "\n");
@@ -906,6 +911,18 @@ createUndefinedGlobal(StringRef name, llvm::wasm::WasmGlobalType *type) {
return sym;
}
+static UndefinedFunction *
+createUndefinedFunction(StringRef name, std::optional<StringRef> importName,
+ std::optional<StringRef> importModule,
+ WasmSignature *signature) {
+ auto *sym = cast<UndefinedFunction>(symtab->addUndefinedFunction(
+ name, importName, importModule, WASM_SYMBOL_UNDEFINED, nullptr, signature,
+ true));
+ ctx.arg.allowUndefinedSymbols.insert(sym->getName());
+ sym->isUsedInRegularObj = true;
+ return sym;
+}
+
static InputGlobal *createGlobal(StringRef name, bool isMutable) {
llvm::wasm::WasmGlobal wasmGlobal;
bool is64 = ctx.arg.is64.value_or(false);
@@ -946,11 +963,13 @@ static void createSyntheticSymbols() {
bool is64 = ctx.arg.is64.value_or(false);
+ auto stack_pointer_name =
+ ctx.arg.isWasip3 ? "__init_stack_pointer" : "__stack_pointer";
if (ctx.isPic) {
ctx.sym.stackPointer =
- createUndefinedGlobal("__stack_pointer", ctx.arg.is64.value_or(false)
- ? &mutableGlobalTypeI64
- : &mutableGlobalTypeI32);
+ createUndefinedGlobal(stack_pointer_name, ctx.arg.is64.value_or(false)
+ ? &mutableGlobalTypeI64
+ : &mutableGlobalTypeI32);
// For PIC code, we import two global variables (__memory_base and
// __table_base) from the environment and use these as the offset at
// which to load our static data and function table.
@@ -963,14 +982,15 @@ static void createSyntheticSymbols() {
ctx.sym.tableBase->markLive();
} else {
// For non-PIC code
- ctx.sym.stackPointer = createGlobalVariable("__stack_pointer", true);
+ ctx.sym.stackPointer = createGlobalVariable(stack_pointer_name, true);
ctx.sym.stackPointer->markLive();
}
- if (ctx.arg.sharedMemory) {
+ if (ctx.arg.isMultithreaded()) {
// TLS symbols are all hidden/dso-local
- ctx.sym.tlsBase =
- createGlobalVariable("__tls_base", true, WASM_SYMBOL_VISIBILITY_HIDDEN);
+ auto tls_base_name = ctx.arg.isWasip3 ? "__init_tls_base" : "__tls_base";
+ ctx.sym.tlsBase = createGlobalVariable(tls_base_name, true,
+ WASM_SYMBOL_VISIBILITY_HIDDEN);
ctx.sym.tlsSize = createGlobalVariable("__tls_size", false,
WASM_SYMBOL_VISIBILITY_HIDDEN);
ctx.sym.tlsAlign = createGlobalVariable("__tls_align", false,
@@ -979,6 +999,21 @@ static void createSyntheticSymbols() {
"__wasm_init_tls", WASM_SYMBOL_VISIBILITY_HIDDEN,
make<SyntheticFunction>(is64 ? i64ArgSignature : i32ArgSignature,
"__wasm_init_tls"));
+ if (ctx.arg.isWasip3) {
+ ctx.sym.tlsBase->markLive();
+ ctx.sym.tlsSize->markLive();
+ ctx.sym.tlsAlign->markLive();
+ static WasmSignature contextSet1Signature{{}, {ValType::I32}};
+ ctx.sym.contextSet1 = createUndefinedFunction(
+ "__wasm_component_model_builtin_context_set_1", "[context-set-1]",
+ "$root", &contextSet1Signature);
+ ctx.sym.contextSet1->markLive();
+ static WasmSignature contextGet1Signature{{ValType::I32}, {}};
+ ctx.sym.contextGet1 = createUndefinedFunction(
+ "__wasm_component_model_builtin_context_get_1", "[context-get-1]",
+ "$root", &contextGet1Signature);
+ ctx.sym.contextGet1->markLive();
+ }
}
}
@@ -1017,7 +1052,7 @@ static void createOptionalSymbols() {
//
// __tls_size and __tls_align are not needed in this case since they are only
// needed for __wasm_init_tls (which we do not create in this case).
- if (!ctx.arg.sharedMemory)
+ if (!ctx.arg.sharedMemory && !ctx.arg.isWasip3)
ctx.sym.tlsBase = createOptionalGlobal("__tls_base", false);
}
@@ -1026,15 +1061,15 @@ static void processStubLibrariesPreLTO() {
for (auto &stub_file : ctx.stubFiles) {
LLVM_DEBUG(llvm::dbgs()
<< "processing stub file: " << stub_file->getName() << "\n");
- for (auto [name, deps]: stub_file->symbolDependencies) {
- auto* sym = symtab->find(name);
+ for (auto [name, deps] : stub_file->symbolDependencies) {
+ auto *sym = symtab->find(name);
// If the symbol is not present at all (yet), or if it is present but
// undefined, then mark the dependent symbols as used by a regular
// object so they will be preserved and exported by the LTO process.
if (!sym || sym->isUndefined()) {
for (const auto dep : deps) {
- auto* needed = symtab->find(dep);
- if (needed ) {
+ auto *needed = symtab->find(dep);
+ if (needed) {
needed->isUsedInRegularObj = true;
// Like with handleLibcall we have to extract any LTO archive
// members that might need to be exported due to stub library
diff --git a/lld/wasm/Relocations.cpp b/lld/wasm/Relocations.cpp
index a3f87ea3d69c0..cb597fdeffcf3 100644
--- a/lld/wasm/Relocations.cpp
+++ b/lld/wasm/Relocations.cpp
@@ -33,7 +33,7 @@ static bool requiresGOTAccess(const Symbol *sym) {
return true;
}
-static bool allowUndefined(const Symbol* sym) {
+static bool allowUndefined(const Symbol *sym) {
// Symbols that are explicitly imported are always allowed to be undefined at
// link time.
if (sym->isImported())
@@ -125,7 +125,7 @@ void scanRelocations(InputChunk *chunk) {
// In single-threaded builds TLS is lowered away and TLS data can be
// merged with normal data and allowing TLS relocation in non-TLS
// segments.
- if (ctx.arg.sharedMemory) {
+ if (ctx.arg.isMultithreaded()) {
if (!sym->isTLS()) {
error(toString(file) + ": relocation " +
relocTypeToString(reloc.Type) +
diff --git a/lld/wasm/Symbols.cpp b/lld/wasm/Symbols.cpp
index f2040441e6257..97a9871a06308 100644
--- a/lld/wasm/Symbols.cpp
+++ b/lld/wasm/Symbols.cpp
@@ -95,7 +95,7 @@ WasmSymbolType Symbol::getWasmType() const {
}
const WasmSignature *Symbol::getSignature() const {
- if (auto* f = dyn_cast<FunctionSymbol>(this))
+ if (auto *f = dyn_cast<FunctionSymbol>(this))
return f->signature;
if (auto *t = dyn_cast<TagSymbol>(this))
return t->signature;
@@ -223,9 +223,7 @@ bool Symbol::isExportedExplicit() const {
return forceExport || flags & WASM_SYMBOL_EXPORTED;
}
-bool Symbol::isNoStrip() const {
- return flags & WASM_SYMBOL_NO_STRIP;
-}
+bool Symbol::isNoStrip() const { return flags & WASM_SYMBOL_NO_STRIP; }
uint32_t FunctionSymbol::getFunctionIndex() const {
if (const auto *u = dyn_cast<UndefinedFunction>(this))
@@ -413,7 +411,7 @@ void LazySymbol::setWeak() {
flags |= (flags & ~WASM_SYMBOL_BINDING_MASK) | WASM_SYMBOL_BINDING_WEAK;
}
-void printTraceSymbolUndefined(StringRef name, const InputFile* file) {
+void printTraceSymbolUndefined(StringRef name, const InputFile *file) {
message(toString(file) + ": reference to " + name);
}
diff --git a/lld/wasm/SyntheticSections.cpp b/lld/wasm/SyntheticSections.cpp
index ede6ac4da77b3..023c690c14354 100644
--- a/lld/wasm/SyntheticSections.cpp
+++ b/lld/wasm/SyntheticSections.cpp
@@ -466,8 +466,7 @@ void GlobalSection::addInternalGOTEntry(Symbol *sym) {
void GlobalSection::generateRelocationCode(raw_ostream &os, bool TLS) const {
assert(!ctx.arg.extendedConst);
bool is64 = ctx.arg.is64.value_or(false);
- unsigned opcode_ptr_add = is64 ? WASM_OPCODE_I64_ADD
- : WASM_OPCODE_I32_ADD;
+ unsigned opcode_ptr_add = is64 ? WASM_OPCODE_I64_ADD : WASM_OPCODE_I32_ADD;
for (const Symbol *sym : internalGotSymbols) {
if (TLS != sym->isTLS())
@@ -477,7 +476,7 @@ void GlobalSection::generateRelocationCode(raw_ostream &os, bool TLS) const {
// Get __memory_base
writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET");
if (sym->isTLS())
- writeUleb128(os, ctx.sym.tlsBase->getGlobalIndex(), "__tls_base");
+ writeGetTLSBase(ctx, os);
else
writeUleb128(os, ctx.sym.memoryBase->getGlobalIndex(), "__memory_base");
@@ -520,9 +519,9 @@ void GlobalSection::writeBody() {
// the correct runtime value during `__wasm_apply_global_relocs`.
if (!ctx.arg.extendedConst && ctx.isPic && !sym->isTLS())
mutable_ = true;
- // With multi-theadeding any TLS globals must be mutable since they get
+ // With multi-threading any TLS globals must be mutable since they get
// set during `__wasm_apply_global_tls_relocs`
- if (ctx.arg.sharedMemory && sym->isTLS())
+ if (ctx.arg.isMultithreaded() && sym->isTLS())
mutable_ = true;
}
WasmGlobalType type{itype, mutable_};
@@ -559,10 +558,11 @@ void GlobalSection::writeBody() {
} else {
WasmInitExpr initExpr;
if (auto *d = dyn_cast<DefinedData>(sym))
- // In the sharedMemory case TLS globals are set during
- // `__wasm_apply_global_tls_relocs`, but in the non-shared case
+ // In the multi-threaded case, TLS globals are set during
+ // `__wasm_apply_global_tls_relocs`, but in the non-multi-threaded case
// we know the absolute value at link time.
- initExpr = intConst(d->getVA(/*absolute=*/!ctx.arg.sharedMemory), is64);
+ initExpr =
+ intConst(d->getVA(/*absolute=*/!ctx.arg.isMultithreaded()), is64);
else if (auto *f = dyn_cast<FunctionSymbol>(sym))
initExpr = intConst(f->isStub ? 0 : f->getTableIndex(), is64);
else {
@@ -646,7 +646,7 @@ void ElemSection::writeBody() {
uint32_t tableIndex = ctx.arg.tableBase;
for (const FunctionSymbol *sym : indirectFunctions) {
assert(sym->getTableIndex() == tableIndex);
- (void) tableIndex;
+ (void)tableIndex;
writeUleb128(os, sym->getFunctionIndex(), "function index");
++tableIndex;
}
@@ -663,7 +663,7 @@ void DataCountSection::writeBody() {
}
bool DataCountSection::isNeeded() const {
- return numSegments && ctx.arg.sharedMemory;
+ return numSegments && ctx.arg.isMultithreaded();
}
void LinkingSection::writeBody() {
@@ -992,4 +992,4 @@ void BuildIdSection::writeBuildId(llvm::ArrayRef<uint8_t> buf) {
memcpy(hashPlaceholderPtr, buf.data(), hashSize);
}
-} // namespace wasm::lld
+} // namespace lld::wasm
diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp
index dfd856f2faee6..50d6449ca79a9 100644
--- a/lld/wasm/Writer.cpp
+++ b/lld/wasm/Writer.cpp
@@ -311,7 +311,8 @@ void Writer::writeBuildId() {
}
static void setGlobalPtr(DefinedGlobal *g, uint64_t memoryPtr) {
- LLVM_DEBUG(dbgs() << "setGlobalPtr " << g->getName() << " -> " << memoryPtr << "\n");
+ LLVM_DEBUG(dbgs() << "setGlobalPtr " << g->getName() << " -> " << memoryPtr
+ << "\n");
g->global->setPointerValue(memoryPtr);
}
@@ -358,7 +359,8 @@ void Writer::layoutMemory() {
placeStack();
if (ctx.arg.globalBase) {
if (ctx.arg.globalBase < memoryPtr) {
- error("--global-base cannot be less than stack size when --stack-first is used");
+ error("--global-base cannot be less than stack size when --stack-first "
+ "is used");
return;
}
memoryPtr = ctx.arg.globalBase;
@@ -382,6 +384,7 @@ void Writer::layoutMemory() {
for (OutputSegment *seg : segments) {
out.dylinkSec->memAlign = std::max(out.dylinkSec->memAlign, seg->alignment);
memoryPtr = alignTo(memoryPtr, 1ULL << seg->alignment);
+
seg->startVA = memoryPtr;
log(formatv("mem: {0,-15} offset={1,-8} size={2,-8} align={3}", seg->name,
memoryPtr, seg->size, seg->alignment));
@@ -1029,7 +1032,7 @@ static StringRef getOutputDataSegmentName(const InputChunk &seg) {
OutputSegment *Writer::createOutputSegment(StringRef name) {
LLVM_DEBUG(dbgs() << "new segment: " << name << "\n");
OutputSegment *s = make<OutputSegment>(name);
- if (ctx.arg.sharedMemory)
+ if (ctx.arg.isMultithreaded())
s->initFlags = WASM_DATA_SEGMENT_IS_PASSIVE;
if (!ctx.arg.relocatable && name.starts_with(".bss"))
s->isBss = true;
@@ -1163,14 +1166,14 @@ void Writer::createSyntheticInitFunctions() {
"__wasm_init_memory", WASM_SYMBOL_VISIBILITY_HIDDEN,
make<SyntheticFunction>(nullSignature, "__wasm_init_memory"));
ctx.sym.initMemory->mark...
[truncated]
|
|
@alexcrichton I've factored out the WASIP3 changes into feature flags and linker options like |
|
@alexcrichton I've made it so that if you try to link an object file compiled with/without the |
|
I'll progressively add tests to this, but the core functionality is ready for review |
|
@sbc100 I think this is ready for another review |
| if (HasWideArithmetic) | ||
| Builder.defineMacro("__wasm_wide_arithmetic__"); | ||
| if (HasLibcallThreadContext) | ||
| Builder.defineMacro("__wasm_libcall_thread_context__"); |
There was a problem hiding this comment.
I wonder again if we really need this new macro.
Now that this is no longer a wasm feature I think there is even more reason to not do this. Given that even the target triple information is not exposed as builtin macrso I don't see why we should give this special status.
If wasi-libc really needs to know in the source code it can define do something like -target=wasip3 -DUSE_WASIP3
There was a problem hiding this comment.
I think since we've decided that this can just be the OS ABI (i.e. the wasip3 OS implies use of the libcall thread context ABI), any code that cares should be able to use the __wasip3__ macro that gets defined when we target that OS.
(in other words, target triple information typically is exposed as a builtin macro, e.g. here).
There was a problem hiding this comment.
This is still useful as it will enable us to merge code into wasi-libc's WASIP3 target that will work for both ABIs while the codebase transitions.
There was a problem hiding this comment.
I also don't really like having this in with the wasm features, but I'm sympathetic to the need to transition to the final ABI for wasip3. Is it just libraries in wasi-sdk that would need this transition? Maybe we could leave this in LLVM for a release and then remove it (or how long would it need to be?)
There was a problem hiding this comment.
Yeah, everything else should just be a recompile, I can only forsee libc needing code changes
| // RUN: -target wasm64-wasip3 \ | ||
| // RUN: | FileCheck %s -check-prefix=LIBCALL-THREAD-CONTEXT | ||
|
|
||
| // LIBCALL-THREAD-CONTEXT: #define __wasm_libcall_thread_context__ 1{{$}} |
There was a problem hiding this comment.
This can be removed too if we remove the macro.
| llvm::SmallDenseMap<StringRef, OutputSegment *> segmentMap; | ||
| }; | ||
|
|
||
| void writeSetTLSBase(const Ctx &ctx, raw_ostream &os) { |
There was a problem hiding this comment.
Would it make sense to just call this writeTLSBase?
There was a problem hiding this comment.
Technically we're writing out code to set the TLS base, so this name is more accurate even if it's a bit of an unwieldly name. I could go either way though.
| if (feature.Prefix == WASM_FEATURE_PREFIX_DISALLOWED) | ||
| continue; | ||
| objectFeatures.insert(feature.Name); | ||
| if (disallowed.contains(feature.Name)) |
There was a problem hiding this comment.
Now that we a not using a wasm feature should all these changes be reverted?
There was a problem hiding this comment.
I've reverted the part referencing the feature, but left the check for a better error message when mixing thread context ABIs
|
|
||
| # CHECK: GlobalNames: | ||
| # CHECK-NEXT: - Index: 0 | ||
| # CHECK-NEXT: Name: __init_stack_pointer |
There was a problem hiding this comment.
I'm still not quite clear on how these newly-named versions of the old globals are still needed/used.
We we document that part somewhere? Maybe it is and I missed it?
There was a problem hiding this comment.
They're used to have a known-good stack and TLS area in case they are needed by task initialization: https://github.com/TartanLlama/wasi-libc/blob/sy/coop-threading/libc-bottom-half/sources/__wasm_init_task.s
I'm planning to submit a PR to the tooling conventions repo when this patch is merged, but I can make a draft one now if you like
sbc100
left a comment
There was a problem hiding this comment.
LGTM % a couple last nits.
| // info is present so we can allocate a local for DWARF to reference. | ||
| bool NeedsSPForDebug = | ||
| MF.getFunction().getSubprogram() && | ||
| MF.getSubtarget<WebAssemblySubtarget>().hasLibcallThreadContext(); |
There was a problem hiding this comment.
Is there a test that covers this part of the change?
| IntPtrType = SignedLong; | ||
| } | ||
| if (T.getOS() == llvm::Triple::WASIp3) | ||
| HasLibcallThreadContext = true; |
There was a problem hiding this comment.
This logic seems to be repeated in WebAssemblySubtarget.cpp. Is the expected/unavoidable?
There was a problem hiding this comment.
I think this is expected, because both Clang and LLVM need to default the behaviour based on the triple
The WebAssembly Component Model has added support for cooperative multithreading. This has been implemented in the Wasmtime engine and is part of the wider project of WASI preview 3, which is currently tracked here.
These changes will require updating the way that
__stack_pointerand__tls_basework purely for a newwasm32-wasip3target; other targets will not be touched. Specifically, rather than using a Wasm global for tracking the stack pointer and TLS base, the newcontext.get/setcomponent model builtin functions will be used (the intention being that runtimes will need to aggressively optimize these calls into single load/stores). For justification on this choice rather than switching out the global at context-switch boundaries, see this comment and this comment.I guard the new behaviour behind a new
component-model-thread-contextfeature, which is automatically enabled for WASIP3 targets. The linker determines what thread context ABI to use based on the presence of this feature.Includes #182896