1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-10-19 11:02:59 +02:00

[WebAssembly] Support weak defined symbols

Model weakly defined symbols as symbols that are both
exports and imported and marked as weak. Local references
to the symbols refer to the import but the linker can
resolve this to the weak export if not strong symbol
is found at link time.

Differential Revision: https://reviews.llvm.org/D35029

llvm-svn: 307348
This commit is contained in:
Sam Clegg 2017-07-07 02:01:29 +00:00
parent 2de601c7f8
commit b11d8eb34f
4 changed files with 116 additions and 81 deletions

View File

@ -61,7 +61,7 @@ public:
void print(raw_ostream &Out) const { void print(raw_ostream &Out) const {
Out << "Name=" << Name << ", Type=" << static_cast<int>(Type) Out << "Name=" << Name << ", Type=" << static_cast<int>(Type)
<< ", Flags=" << Flags; << ", Flags=" << Flags << " ElemIndex=" << ElementIndex;
} }
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)

View File

@ -36,8 +36,7 @@
using namespace llvm; using namespace llvm;
#undef DEBUG_TYPE #define DEBUG_TYPE "mc"
#define DEBUG_TYPE "reloc-info"
namespace { namespace {
@ -199,6 +198,7 @@ class WasmObjectWriter : public MCObjectWriter {
DenseMap<WasmFunctionType, int32_t, WasmFunctionTypeDenseMapInfo> DenseMap<WasmFunctionType, int32_t, WasmFunctionTypeDenseMapInfo>
FunctionTypeIndices; FunctionTypeIndices;
SmallVector<WasmFunctionType, 4> FunctionTypes;
// TargetObjectWriter wrappers. // TargetObjectWriter wrappers.
bool is64Bit() const { return TargetObjectWriter->is64Bit(); } bool is64Bit() const { return TargetObjectWriter->is64Bit(); }
@ -224,6 +224,7 @@ private:
SymbolIndices.clear(); SymbolIndices.clear();
IndirectSymbolIndices.clear(); IndirectSymbolIndices.clear();
FunctionTypeIndices.clear(); FunctionTypeIndices.clear();
FunctionTypes.clear();
MCObjectWriter::reset(); MCObjectWriter::reset();
} }
@ -276,6 +277,8 @@ private:
void writeRelocations(ArrayRef<WasmRelocationEntry> Relocations, void writeRelocations(ArrayRef<WasmRelocationEntry> Relocations,
uint64_t HeaderSize); uint64_t HeaderSize);
uint32_t getRelocationIndexValue(const WasmRelocationEntry &RelEntry); uint32_t getRelocationIndexValue(const WasmRelocationEntry &RelEntry);
uint32_t getFunctionType(const MCSymbolWasm& Symbol);
uint32_t registerFunctionType(const MCSymbolWasm& Symbol);
}; };
} // end anonymous namespace } // end anonymous namespace
@ -493,7 +496,7 @@ uint32_t WasmObjectWriter::getRelocationIndexValue(
case wasm::R_WEBASSEMBLY_TABLE_INDEX_SLEB: case wasm::R_WEBASSEMBLY_TABLE_INDEX_SLEB:
case wasm::R_WEBASSEMBLY_TABLE_INDEX_I32: case wasm::R_WEBASSEMBLY_TABLE_INDEX_I32:
if (!IndirectSymbolIndices.count(RelEntry.Symbol)) if (!IndirectSymbolIndices.count(RelEntry.Symbol))
report_fatal_error("symbol not found table index space:" + report_fatal_error("symbol not found table index space: " +
RelEntry.Symbol->getName()); RelEntry.Symbol->getName());
return IndirectSymbolIndices[RelEntry.Symbol]; return IndirectSymbolIndices[RelEntry.Symbol];
case wasm::R_WEBASSEMBLY_FUNCTION_INDEX_LEB: case wasm::R_WEBASSEMBLY_FUNCTION_INDEX_LEB:
@ -502,12 +505,12 @@ uint32_t WasmObjectWriter::getRelocationIndexValue(
case wasm::R_WEBASSEMBLY_GLOBAL_ADDR_SLEB: case wasm::R_WEBASSEMBLY_GLOBAL_ADDR_SLEB:
case wasm::R_WEBASSEMBLY_GLOBAL_ADDR_I32: case wasm::R_WEBASSEMBLY_GLOBAL_ADDR_I32:
if (!SymbolIndices.count(RelEntry.Symbol)) if (!SymbolIndices.count(RelEntry.Symbol))
report_fatal_error("symbol not found function/global index space:" + report_fatal_error("symbol not found function/global index space: " +
RelEntry.Symbol->getName()); RelEntry.Symbol->getName());
return SymbolIndices[RelEntry.Symbol]; return SymbolIndices[RelEntry.Symbol];
case wasm::R_WEBASSEMBLY_TYPE_INDEX_LEB: case wasm::R_WEBASSEMBLY_TYPE_INDEX_LEB:
if (!TypeIndices.count(RelEntry.Symbol)) if (!TypeIndices.count(RelEntry.Symbol))
report_fatal_error("symbol not found in type index space:" + report_fatal_error("symbol not found in type index space: " +
RelEntry.Symbol->getName()); RelEntry.Symbol->getName());
return TypeIndices[RelEntry.Symbol]; return TypeIndices[RelEntry.Symbol];
default: default:
@ -913,6 +916,38 @@ void WasmObjectWriter::writeLinkingMetaDataSection(
endSection(Section); endSection(Section);
} }
uint32_t WasmObjectWriter::getFunctionType(const MCSymbolWasm& Symbol) {
assert(Symbol.isFunction());
assert(TypeIndices.count(&Symbol));
return TypeIndices[&Symbol];
}
uint32_t WasmObjectWriter::registerFunctionType(const MCSymbolWasm& Symbol) {
assert(Symbol.isFunction());
WasmFunctionType F;
if (Symbol.isVariable()) {
const MCExpr *Expr = Symbol.getVariableValue();
auto *Inner = dyn_cast<MCSymbolRefExpr>(Expr);
const auto *ResolvedSym = cast<MCSymbolWasm>(&Inner->getSymbol());
F.Returns = ResolvedSym->getReturns();
F.Params = ResolvedSym->getParams();
} else {
F.Returns = Symbol.getReturns();
F.Params = Symbol.getParams();
}
auto Pair =
FunctionTypeIndices.insert(std::make_pair(F, FunctionTypes.size()));
if (Pair.second)
FunctionTypes.push_back(F);
TypeIndices[&Symbol] = Pair.first->second;
DEBUG(dbgs() << "registerFunctionType: " << Symbol << " new:" << Pair.second << "\n");
DEBUG(dbgs() << " -> type index: " << Pair.first->second << "\n");
return Pair.first->second;
}
void WasmObjectWriter::writeObject(MCAssembler &Asm, void WasmObjectWriter::writeObject(MCAssembler &Asm,
const MCAsmLayout &Layout) { const MCAsmLayout &Layout) {
DEBUG(dbgs() << "WasmObjectWriter::writeObject\n"); DEBUG(dbgs() << "WasmObjectWriter::writeObject\n");
@ -920,7 +955,6 @@ void WasmObjectWriter::writeObject(MCAssembler &Asm,
wasm::ValType PtrType = is64Bit() ? wasm::ValType::I64 : wasm::ValType::I32; wasm::ValType PtrType = is64Bit() ? wasm::ValType::I64 : wasm::ValType::I32;
// Collect information from the available symbols. // Collect information from the available symbols.
SmallVector<WasmFunctionType, 4> FunctionTypes;
SmallVector<WasmFunction, 4> Functions; SmallVector<WasmFunction, 4> Functions;
SmallVector<uint32_t, 4> TableElems; SmallVector<uint32_t, 4> TableElems;
SmallVector<WasmGlobal, 4> Globals; SmallVector<WasmGlobal, 4> Globals;
@ -959,42 +993,28 @@ void WasmObjectWriter::writeObject(MCAssembler &Asm,
// Populate the Imports set. // Populate the Imports set.
for (const MCSymbol &S : Asm.symbols()) { for (const MCSymbol &S : Asm.symbols()) {
// Weak aliases don't have thier own function types. const auto &WS = static_cast<const MCSymbolWasm &>(S);
if (S.isVariable())
if (WS.isTemporary())
continue; continue;
const auto &WS = static_cast<const MCSymbolWasm &>(S); if (WS.isFunction())
int32_t Type; registerFunctionType(WS);
if (WS.isFunction()) {
// Prepare the function's type, if we haven't seen it yet.
WasmFunctionType F;
F.Returns = WS.getReturns();
F.Params = WS.getParams();
auto Pair =
FunctionTypeIndices.insert(std::make_pair(F, FunctionTypes.size()));
if (Pair.second)
FunctionTypes.push_back(F);
Type = Pair.first->second;
} else {
Type = int32_t(PtrType);
}
// If the symbol is not defined in this translation unit, import it. // If the symbol is not defined in this translation unit, import it.
if (!WS.isTemporary() && !WS.isDefined(/*SetUsed=*/false)) { if (!WS.isDefined(/*SetUsed=*/false) || WS.isVariable()) {
WasmImport Import; WasmImport Import;
Import.ModuleName = WS.getModuleName(); Import.ModuleName = WS.getModuleName();
Import.FieldName = WS.getName(); Import.FieldName = WS.getName();
if (WS.isFunction()) { if (WS.isFunction()) {
Import.Kind = wasm::WASM_EXTERNAL_FUNCTION; Import.Kind = wasm::WASM_EXTERNAL_FUNCTION;
Import.Type = Type; Import.Type = getFunctionType(WS);
SymbolIndices[&WS] = NumFuncImports; SymbolIndices[&WS] = NumFuncImports;
++NumFuncImports; ++NumFuncImports;
} else { } else {
Import.Kind = wasm::WASM_EXTERNAL_GLOBAL; Import.Kind = wasm::WASM_EXTERNAL_GLOBAL;
Import.Type = Type; Import.Type = int32_t(PtrType);
SymbolIndices[&WS] = NumGlobalImports; SymbolIndices[&WS] = NumGlobalImports;
++NumGlobalImports; ++NumGlobalImports;
} }
@ -1086,10 +1106,6 @@ void WasmObjectWriter::writeObject(MCAssembler &Asm,
if (S.isTemporary() && S.getName().empty()) if (S.isTemporary() && S.getName().empty())
continue; continue;
// Variable references (weak references) are handled in a second pass
if (S.isVariable())
continue;
const auto &WS = static_cast<const MCSymbolWasm &>(S); const auto &WS = static_cast<const MCSymbolWasm &>(S);
DEBUG(dbgs() << "MCSymbol: '" << S << "'" DEBUG(dbgs() << "MCSymbol: '" << S << "'"
<< " isDefined=" << S.isDefined() << " isExternal=" << " isDefined=" << S.isDefined() << " isExternal="
@ -1101,20 +1117,12 @@ void WasmObjectWriter::writeObject(MCAssembler &Asm,
if (WS.isWeak()) if (WS.isWeak())
WeakSymbols.push_back(WS.getName()); WeakSymbols.push_back(WS.getName());
if (WS.isVariable())
continue;
unsigned Index; unsigned Index;
if (WS.isFunction()) { if (WS.isFunction()) {
// Prepare the function's type, if we haven't seen it yet.
WasmFunctionType F;
F.Returns = WS.getReturns();
F.Params = WS.getParams();
auto Pair =
FunctionTypeIndices.insert(std::make_pair(F, FunctionTypes.size()));
if (Pair.second)
FunctionTypes.push_back(F);
int32_t Type = Pair.first->second;
if (WS.isDefined(/*SetUsed=*/false)) { if (WS.isDefined(/*SetUsed=*/false)) {
if (WS.getOffset() != 0) if (WS.getOffset() != 0)
report_fatal_error( report_fatal_error(
@ -1129,21 +1137,21 @@ void WasmObjectWriter::writeObject(MCAssembler &Asm,
// Prepare the function. // Prepare the function.
WasmFunction Func; WasmFunction Func;
Func.Type = Type; Func.Type = getFunctionType(WS);
Func.Sym = &WS; Func.Sym = &WS;
SymbolIndices[&WS] = Index; SymbolIndices[&WS] = Index;
Functions.push_back(Func); Functions.push_back(Func);
} else { } else {
// Should be no such thing as weak undefined symbol
assert(!WS.isVariable());
// An import; the index was assigned above. // An import; the index was assigned above.
Index = SymbolIndices.find(&WS)->second; Index = SymbolIndices.find(&WS)->second;
} }
DEBUG(dbgs() << " -> function index: " << Index << "\n");
// If needed, prepare the function to be called indirectly. // If needed, prepare the function to be called indirectly.
if (IsAddressTaken.count(&WS)) { if (IsAddressTaken.count(&WS) != 0) {
IndirectSymbolIndices[&WS] = TableElems.size(); IndirectSymbolIndices[&WS] = TableElems.size();
DEBUG(dbgs() << " -> adding to table: " << TableElems.size() << "\n");
TableElems.push_back(Index); TableElems.push_back(Index);
} }
} else { } else {
@ -1209,11 +1217,12 @@ void WasmObjectWriter::writeObject(MCAssembler &Asm,
Global.InitialValue = DataSection.getSectionOffset(); Global.InitialValue = DataSection.getSectionOffset();
Global.ImportIndex = 0; Global.ImportIndex = 0;
SymbolIndices[&WS] = Index; SymbolIndices[&WS] = Index;
DEBUG(dbgs() << " -> global index: " << Index << "\n");
Globals.push_back(Global); Globals.push_back(Global);
} }
// If the symbol is visible outside this translation unit, export it. // If the symbol is visible outside this translation unit, export it.
if (WS.isExternal() && WS.isDefined(/*SetUsed=*/false)) { if ((WS.isExternal() && WS.isDefined(/*SetUsed=*/false))) {
WasmExport Export; WasmExport Export;
Export.FieldName = WS.getName(); Export.FieldName = WS.getName();
Export.Index = Index; Export.Index = Index;
@ -1221,26 +1230,28 @@ void WasmObjectWriter::writeObject(MCAssembler &Asm,
Export.Kind = wasm::WASM_EXTERNAL_FUNCTION; Export.Kind = wasm::WASM_EXTERNAL_FUNCTION;
else else
Export.Kind = wasm::WASM_EXTERNAL_GLOBAL; Export.Kind = wasm::WASM_EXTERNAL_GLOBAL;
DEBUG(dbgs() << " -> export " << Exports.size() << "\n");
Exports.push_back(Export); Exports.push_back(Export);
} }
} }
// Handle weak aliases // Handle weak aliases. We need to process these in a separate pass because
// we need to have processed the target of the alias before the alias itself
// and the symbols are not necessarily ordered in this way.
for (const MCSymbol &S : Asm.symbols()) { for (const MCSymbol &S : Asm.symbols()) {
if (!S.isVariable()) if (!S.isVariable())
continue; continue;
assert(S.isExternal());
assert(S.isDefined(/*SetUsed=*/false)); assert(S.isDefined(/*SetUsed=*/false));
const auto &WS = static_cast<const MCSymbolWasm &>(S); const auto &WS = static_cast<const MCSymbolWasm &>(S);
// Find the target symbol of this weak alias and export that index
// Find the target symbol of this weak alias
const MCExpr *Expr = WS.getVariableValue(); const MCExpr *Expr = WS.getVariableValue();
auto *Inner = dyn_cast<MCSymbolRefExpr>(Expr); auto *Inner = dyn_cast<MCSymbolRefExpr>(Expr);
const auto *ResolvedSym = cast<MCSymbolWasm>(&Inner->getSymbol()); const auto *ResolvedSym = cast<MCSymbolWasm>(&Inner->getSymbol());
DEBUG(dbgs() << WS.getName() << ": weak alias of '" << *ResolvedSym << "'\n");
assert(SymbolIndices.count(ResolvedSym) > 0);
uint32_t Index = SymbolIndices.find(ResolvedSym)->second; uint32_t Index = SymbolIndices.find(ResolvedSym)->second;
DEBUG(dbgs() << "Weak alias: '" << WS << "' -> '" << ResolvedSym << "' = " << Index << "\n"); DEBUG(dbgs() << " -> index:" << Index << "\n");
SymbolIndices[&WS] = Index;
WasmExport Export; WasmExport Export;
Export.FieldName = WS.getName(); Export.FieldName = WS.getName();
@ -1249,7 +1260,7 @@ void WasmObjectWriter::writeObject(MCAssembler &Asm,
Export.Kind = wasm::WASM_EXTERNAL_FUNCTION; Export.Kind = wasm::WASM_EXTERNAL_FUNCTION;
else else
Export.Kind = wasm::WASM_EXTERNAL_GLOBAL; Export.Kind = wasm::WASM_EXTERNAL_GLOBAL;
WeakSymbols.push_back(Export.FieldName); DEBUG(dbgs() << " -> export " << Exports.size() << "\n");
Exports.push_back(Export); Exports.push_back(Export);
} }
@ -1258,15 +1269,7 @@ void WasmObjectWriter::writeObject(MCAssembler &Asm,
if (Fixup.Type != wasm::R_WEBASSEMBLY_TYPE_INDEX_LEB) if (Fixup.Type != wasm::R_WEBASSEMBLY_TYPE_INDEX_LEB)
continue; continue;
WasmFunctionType F; registerFunctionType(*Fixup.Symbol);
F.Returns = Fixup.Symbol->getReturns();
F.Params = Fixup.Symbol->getParams();
auto Pair =
FunctionTypeIndices.insert(std::make_pair(F, FunctionTypes.size()));
if (Pair.second)
FunctionTypes.push_back(F);
TypeIndices[Fixup.Symbol] = Pair.first->second;
} }
// Write out the Wasm header. // Write out the Wasm header.

View File

@ -567,20 +567,16 @@ Error WasmObjectFile::parseExportSection(const uint8_t *Ptr, const uint8_t *End)
Ex.Name = readString(Ptr); Ex.Name = readString(Ptr);
Ex.Kind = readUint8(Ptr); Ex.Kind = readUint8(Ptr);
Ex.Index = readVaruint32(Ptr); Ex.Index = readVaruint32(Ptr);
WasmSymbol::SymbolType ExportType;
bool MakeSymbol = false;
switch (Ex.Kind) { switch (Ex.Kind) {
case wasm::WASM_EXTERNAL_FUNCTION: case wasm::WASM_EXTERNAL_FUNCTION:
SymbolMap.try_emplace(Ex.Name, Symbols.size()); ExportType = WasmSymbol::SymbolType::FUNCTION_EXPORT;
Symbols.emplace_back(Ex.Name, WasmSymbol::SymbolType::FUNCTION_EXPORT, MakeSymbol = true;
Sections.size(), i);
DEBUG(dbgs() << "Adding export: " << Symbols.back()
<< " sym index:" << Symbols.size() << "\n");
break; break;
case wasm::WASM_EXTERNAL_GLOBAL: case wasm::WASM_EXTERNAL_GLOBAL:
SymbolMap.try_emplace(Ex.Name, Symbols.size()); ExportType = WasmSymbol::SymbolType::GLOBAL_EXPORT;
Symbols.emplace_back(Ex.Name, WasmSymbol::SymbolType::GLOBAL_EXPORT, MakeSymbol = true;
Sections.size(), i);
DEBUG(dbgs() << "Adding export: " << Symbols.back()
<< " sym index:" << Symbols.size() << "\n");
break; break;
case wasm::WASM_EXTERNAL_MEMORY: case wasm::WASM_EXTERNAL_MEMORY:
case wasm::WASM_EXTERNAL_TABLE: case wasm::WASM_EXTERNAL_TABLE:
@ -589,6 +585,20 @@ Error WasmObjectFile::parseExportSection(const uint8_t *Ptr, const uint8_t *End)
return make_error<GenericBinaryError>( return make_error<GenericBinaryError>(
"Unexpected export kind", object_error::parse_failed); "Unexpected export kind", object_error::parse_failed);
} }
if (MakeSymbol) {
auto Pair = SymbolMap.try_emplace(Ex.Name, Symbols.size());
if (Pair.second) {
Symbols.emplace_back(Ex.Name, ExportType,
Sections.size(), i);
DEBUG(dbgs() << "Adding export: " << Symbols.back()
<< " sym index:" << Symbols.size() << "\n");
} else {
uint32_t SymIndex = Pair.first->second;
Symbols[SymIndex] = WasmSymbol(Ex.Name, ExportType, Sections.size(), i);
DEBUG(dbgs() << "Replacing existing symbol: " << Symbols[SymIndex]
<< " sym index:" << SymIndex << "\n");
}
}
Exports.push_back(Ex); Exports.push_back(Ex);
} }
if (Ptr != End) if (Ptr != End)

View File

@ -3,34 +3,56 @@
; foo_alias() function is weak alias of function foo() ; foo_alias() function is weak alias of function foo()
; Generates two exports of the same function, one of them weak ; Generates two exports of the same function, one of them weak
@foo_alias = weak hidden alias i32 (...), bitcast (i32 ()* @foo to i32 (...)*) @foo_alias = weak hidden alias i32 (), i32 ()* @foo
define hidden i32 @call_alias() #0 {
entry:
%call = call i32 @foo_alias()
ret i32 %call
}
define hidden i32 @foo() #0 { define hidden i32 @foo() #0 {
entry: entry:
ret i32 0 ret i32 0
} }
; CHECK: - Type: TYPE ; CHECK: - Type: TYPE
; CHECK-NEXT: Signatures: ; CHECK-NEXT: Signatures:
; CHECK-NEXT: - Index: 0 ; CHECK-NEXT: - Index: 0
; CHECK-NEXT: ReturnType: I32 ; CHECK-NEXT: ReturnType: I32
; CHECK-NEXT: ParamTypes: ; CHECK-NEXT: ParamTypes:
; CHECK-NEXT: - Type: FUNCTION
; CHECK-NEXT: FunctionTypes: [ 0 ] ; CHECK: - Type: IMPORT
; CHECK-NEXT: Imports:
; CHECK-NEXT: - Module: env
; CHECK-NEXT: Field: foo_alias
; CHECK-NEXT: Kind: FUNCTION
; CHECK-NEXT: SigIndex: 0
; CHECK: - Type: FUNCTION
; CHECK-NEXT: FunctionTypes: [ 0, 0 ]
; CHECK: - Type: EXPORT ; CHECK: - Type: EXPORT
; CHECK-NEXT: Exports: ; CHECK-NEXT: Exports:
; CHECK-NEXT: - Name: call_alias
; CHECK-NEXT: Kind: FUNCTION
; CHECK-NEXT: Index: 1
; CHECK-NEXT: - Name: foo ; CHECK-NEXT: - Name: foo
; CHECK-NEXT: Kind: FUNCTION ; CHECK-NEXT: Kind: FUNCTION
; CHECK-NEXT: Index: 0 ; CHECK-NEXT: Index: 2
; CHECK-NEXT: - Name: foo_alias ; CHECK-NEXT: - Name: foo_alias
; CHECK-NEXT: Kind: FUNCTION ; CHECK-NEXT: Kind: FUNCTION
; CHECK-NEXT: Index: 0 ; CHECK-NEXT: Index: 2
; CHECK: - Type: CUSTOM ; CHECK: - Type: CUSTOM
; CHECK-NEXT: Name: name ; CHECK-NEXT: Name: name
; CHECK-NEXT: FunctionNames: ; CHECK-NEXT: FunctionNames:
; CHECK-NEXT: - Index: 0 ; CHECK-NEXT: - Index: 0
; CHECK-NEXT: Name: foo_alias
; CHECK-NEXT: - Index: 1
; CHECK-NEXT: Name: call_alias
; CHECK-NEXT: - Index: 2
; CHECK-NEXT: Name: foo ; CHECK-NEXT: Name: foo
; CHECK-NEXT: - Type: CUSTOM ; CHECK-NEXT: - Type: CUSTOM
; CHECK-NEXT: Name: linking ; CHECK-NEXT: Name: linking