Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 111 additions & 0 deletions lld/test/wasm/common.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# RUN: split-file %s %t
# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t/common1.o %t/common1.s
# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t/common2.o %t/common2.s
# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t/main.o %t/main.s
# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t/strong.o %t/strong.s
# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t/weak.o %t/weak.s

# Test 1: Basic common symbol merging (max size and max alignment)
# RUN: wasm-ld --no-stack-first --global-base=1024 --export=foo --export=bar %t/common1.o %t/common2.o %t/main.o -o %t.wasm
# RUN: obj2yaml %t.wasm | FileCheck %s

# Test 2: Strong defined symbol takes precedence over common symbol
# RUN: wasm-ld --no-stack-first --global-base=1024 --export=foo %t/common1.o %t/strong.o %t/main.o -o %t_strong.wasm
# RUN: obj2yaml %t_strong.wasm | FileCheck %s --check-prefix=STRONG

# Test 3: Common symbol takes precedence over weak defined symbol
# RUN: wasm-ld --no-stack-first --global-base=1024 --export=foo %t/common1.o %t/weak.o %t/main.o -o %t_weak.wasm
# RUN: obj2yaml %t_weak.wasm | FileCheck %s --check-prefix=WEAK

#--- common1.s
.comm foo, 4, 2
.comm bar, 8, 3

#--- common2.s
.comm foo, 8, 3
.comm bar, 4, 2

#--- strong.s
.globl foo
.section .data.foo,"",@
foo:
.int32 42
.size foo, 4

#--- weak.s
.weak foo
.section .data.foo,"",@
foo:
.int32 99
.size foo, 4

#--- main.s
.globl _start
_start:
.functype _start () -> ()
i32.const foo
drop
i32.const bar
drop
end_function

# CHECK: - Type: GLOBAL
# CHECK-NEXT: Globals:
# CHECK-NEXT: - Index: 0
# CHECK-NEXT: Type: I32
# CHECK-NEXT: Mutable: true
# CHECK-NEXT: InitExpr:
# CHECK-NEXT: Opcode: I32_CONST
# CHECK-NEXT: Value: 66576
# CHECK-NEXT: - Index: 1
# CHECK-NEXT: Type: I32
# CHECK-NEXT: Mutable: false
# CHECK-NEXT: InitExpr:
# CHECK-NEXT: Opcode: I32_CONST
# CHECK-NEXT: Value: 1024
# CHECK-NEXT: - Index: 2
# CHECK-NEXT: Type: I32
# CHECK-NEXT: Mutable: false
# CHECK-NEXT: InitExpr:
# CHECK-NEXT: Opcode: I32_CONST
# CHECK-NEXT: Value: 1032

# STRONG: - Type: GLOBAL
# STRONG-NEXT: Globals:
# STRONG-NEXT: - Index: 0
# STRONG-NEXT: Type: I32
# STRONG-NEXT: Mutable: true
# STRONG-NEXT: InitExpr:
# STRONG-NEXT: Opcode: I32_CONST
# STRONG-NEXT: Value: 66576
# STRONG-NEXT: - Index: 1
# STRONG-NEXT: Type: I32
# STRONG-NEXT: Mutable: false
# STRONG-NEXT: InitExpr:
# STRONG-NEXT: Opcode: I32_CONST
# STRONG-NEXT: Value: 1024
# STRONG: - Type: DATA
# STRONG-NEXT: Segments:
# STRONG-NEXT: - SectionOffset: {{[0-9]+}}
# STRONG-NEXT: InitFlags: 0
# STRONG-NEXT: Offset:
# STRONG-NEXT: Opcode: I32_CONST
# STRONG-NEXT: Value: 1024
# STRONG-NEXT: Content: 2A000000

# WEAK-NOT: - SectionOffset: 3
# WEAK-NOT: Content: '63000000'
# WEAK: - Type: GLOBAL
# WEAK-NEXT: Globals:
# WEAK-NEXT: - Index: 0
# WEAK-NEXT: Type: I32
# WEAK-NEXT: Mutable: true
# WEAK-NEXT: InitExpr:
# WEAK-NEXT: Opcode: I32_CONST
# WEAK-NEXT: Value: 66576
# WEAK-NEXT: - Index: 1
# WEAK-NEXT: Type: I32
# WEAK-NEXT: Mutable: false
# WEAK-NEXT: InitExpr:
# WEAK-NEXT: Opcode: I32_CONST
# WEAK-NEXT: Value: 1024
2 changes: 2 additions & 0 deletions lld/wasm/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ class DefinedFunction;
class DefinedGlobal;
class UndefinedGlobal;
class TableSymbol;
class InputChunk;

// For --unresolved-symbols.
enum class UnresolvedPolicy { ReportError, Warn, Ignore, ImportDynamic };
Expand Down Expand Up @@ -146,6 +147,7 @@ struct Ctx {
llvm::SmallVector<InputFunction *, 0> syntheticFunctions;
llvm::SmallVector<InputGlobal *, 0> syntheticGlobals;
llvm::SmallVector<InputTable *, 0> syntheticTables;
llvm::SmallVector<InputChunk *, 0> syntheticInputSegments;

// linker-generated symbols
struct WasmSym {
Expand Down
10 changes: 10 additions & 0 deletions lld/wasm/InputChunks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ uint32_t InputChunk::getSize() const {
}
}

if (const auto *sis = dyn_cast<SyntheticInputSegment>(this))
return sis->getSize();

return data().size();
}

Expand All @@ -92,6 +95,9 @@ void InputChunk::writeTo(uint8_t *buf) const {
// Apply relocations
ms->relocate(buf + outSecOff);
return;
} else if (const auto *sis = dyn_cast<SyntheticInputSegment>(this)) {
sis->writeTo(buf);
return;
}

// Copy contents
Expand Down Expand Up @@ -386,6 +392,10 @@ void InputFunction::writeCompressed(uint8_t *buf) const {
LLVM_DEBUG(dbgs() << " total: " << (buf + chunkSize - orig) << "\n");
}

void SyntheticInputSegment::writeTo(uint8_t *buf) const {
memset(buf + outSecOff, 0, size);
}

uint64_t InputChunk::getChunkOffset(uint64_t offset) const {
if (const auto *ms = dyn_cast<MergeInputChunk>(this)) {
LLVM_DEBUG(dbgs() << "getChunkOffset(merged): " << name << "\n");
Expand Down
20 changes: 20 additions & 0 deletions lld/wasm/InputChunks.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ class InputChunk {
Function,
SyntheticFunction,
Section,
SyntheticDataSegment,
};

StringRef name;
Expand Down Expand Up @@ -341,6 +342,25 @@ class SyntheticFunction : public InputFunction {
void setBody(ArrayRef<uint8_t> body) { rawData = body; }
};

class SyntheticInputSegment : public InputChunk {
public:
SyntheticInputSegment(StringRef name, uint32_t alignment, uint32_t flags)
: InputChunk(nullptr, InputChunk::SyntheticDataSegment, name, alignment,
flags) {}

static bool classof(const InputChunk *c) {
return c->kind() == SyntheticDataSegment;
}

void setSize(uint64_t s) { size = s; }
uint32_t getSize() const { return size; }

void writeTo(uint8_t *buf) const;

private:
uint64_t size = 0;
};

// Represents a single Wasm Section within an input file.
class InputSection : public InputChunk {
public:
Expand Down
6 changes: 6 additions & 0 deletions lld/wasm/InputFiles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -675,6 +675,12 @@ Symbol *ObjFile::createDefined(const WasmSymbol &sym) {
return symtab->addDefinedFunction(name, flags, this, func);
}
case WASM_SYMBOL_TYPE_DATA: {
if ((flags & WASM_SYMBOL_BINDING_MASK) == WASM_SYMBOL_BINDING_COMMON) {
assert(!sym.isBindingLocal());
auto size = sym.Info.CommonRef.Size;
auto alignment = sym.Info.CommonRef.Alignment;
return symtab->addCommon(name, flags, this, size, alignment);
}
InputChunk *seg = segments[sym.Info.DataRef.Segment];
auto offset = sym.Info.DataRef.Offset;
auto size = sym.Info.DataRef.Size;
Expand Down
57 changes: 57 additions & 0 deletions lld/wasm/SymbolTable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,18 @@ static bool shouldReplace(const Symbol *existing, InputFile *newFile,
return true;
}

// If existing symbol is common, it can be overridden by a strong definition.
if (existing->isCommon()) {
if ((newFlags & WASM_SYMBOL_BINDING_MASK) == WASM_SYMBOL_BINDING_WEAK) {
LLVM_DEBUG(
dbgs() << "existing common symbol takes precedence over new weak\n");
return false;
}
LLVM_DEBUG(
dbgs() << "replacing existing common symbol with strong definition\n");
return true;
}

// Now we have two defined symbols. If the new one is weak, we can ignore it.
if ((newFlags & WASM_SYMBOL_BINDING_MASK) == WASM_SYMBOL_BINDING_WEAK) {
LLVM_DEBUG(dbgs() << "existing symbol takes precedence\n");
Expand Down Expand Up @@ -496,6 +508,51 @@ Symbol *SymbolTable::addSharedData(StringRef name, uint32_t flags,
return s;
}

Symbol *SymbolTable::addCommon(StringRef name, uint32_t flags, InputFile *file,
uint64_t size, uint32_t alignment) {
LLVM_DEBUG(dbgs() << "addCommon: " << name << " size:" << size
<< " align:" << alignment << "\n");
auto val = insert(name, file);
Symbol *s = val.first;
bool wasInserted = val.second;

auto replaceSym = [&]() {
replaceSymbol<CommonSymbol>(s, name, flags, file, size, alignment);
};

if (wasInserted || s->isLazy()) {
replaceSym();
return s;
}

checkDataType(s, file);

if (auto *existingCommon = dyn_cast<CommonSymbol>(s)) {
uint64_t newSize = std::max(existingCommon->getSize(), size);
uint32_t newAlign = std::max(existingCommon->getAlignment(), alignment);
existingCommon->setCommon(newSize, newAlign);
existingCommon->flags |= flags & WASM_SYMBOL_NO_STRIP;
return s;
}

if (s->isDefined()) {
if (s->isWeak()) {
LLVM_DEBUG(
dbgs() << "replacing existing weak defined symbol with common\n");
replaceSym();
} else {
LLVM_DEBUG(
dbgs()
<< "existing strong defined symbol takes precedence over common\n");
}
return s;
}

LLVM_DEBUG(dbgs() << "resolving existing undefined symbol with common\n");
replaceSym();
return s;
}

Symbol *SymbolTable::addDefinedFunction(StringRef name, uint32_t flags,
InputFile *file,
InputFunction *function) {
Expand Down
2 changes: 2 additions & 0 deletions lld/wasm/SymbolTable.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ class SymbolTable {
Symbol *addSharedData(StringRef name, uint32_t flags, InputFile *file);
Symbol *addSharedTag(StringRef name, uint32_t flags, InputFile *file,
const WasmSignature *sig);
Symbol *addCommon(StringRef name, uint32_t flags, InputFile *file,
uint64_t size, uint32_t alignment);
Symbol *addDefinedFunction(StringRef name, uint32_t flags, InputFile *file,
InputFunction *function);
Symbol *addDefinedData(StringRef name, uint32_t flags, InputFile *file,
Expand Down
2 changes: 2 additions & 0 deletions lld/wasm/Symbols.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ std::string toString(wasm::Symbol::Kind kind) {
return "UndefinedTable";
case wasm::Symbol::UndefinedTagKind:
return "UndefinedTag";
case wasm::Symbol::CommonKind:
return "CommonSymbol";
case wasm::Symbol::LazyKind:
return "LazyKind";
case wasm::Symbol::SectionKind:
Expand Down
27 changes: 26 additions & 1 deletion lld/wasm/Symbols.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ class Symbol {
UndefinedGlobalKind,
UndefinedTableKind,
UndefinedTagKind,
CommonKind,
LazyKind,
SharedFunctionKind,
SharedDataKind,
Expand All @@ -77,6 +78,7 @@ class Symbol {
}

bool isLazy() const { return symbolKind == LazyKind; }
bool isCommon() const { return symbolKind == CommonKind; }
bool isShared() const {
return symbolKind == SharedFunctionKind || symbolKind == SharedDataKind ||
symbolKind == SharedTagKind;
Expand Down Expand Up @@ -294,7 +296,7 @@ class DataSymbol : public Symbol {
public:
static bool classof(const Symbol *s) {
return s->kind() == DefinedDataKind || s->kind() == UndefinedDataKind ||
s->kind() == SharedDataKind;
s->kind() == SharedDataKind || s->kind() == CommonKind;
}

protected:
Expand Down Expand Up @@ -349,6 +351,28 @@ class UndefinedData : public DataSymbol {
}
};

class CommonSymbol : public DataSymbol {
public:
CommonSymbol(StringRef name, uint32_t flags, InputFile *file, uint64_t size,
uint32_t alignment)
: DataSymbol(name, CommonKind, flags, file), size(size),
alignment(alignment) {}

static bool classof(const Symbol *s) { return s->kind() == CommonKind; }

uint64_t getSize() const { return size; }
uint32_t getAlignment() const { return alignment; }

void setCommon(uint64_t s, uint32_t a) {
size = s;
alignment = a;
}

private:
uint64_t size;
uint32_t alignment;
};

class GlobalSymbol : public Symbol {
public:
static bool classof(const Symbol *s) {
Expand Down Expand Up @@ -566,6 +590,7 @@ union SymbolUnion {
alignas(SectionSymbol) char k[sizeof(SectionSymbol)];
alignas(SharedFunctionSymbol) char l[sizeof(SharedFunctionSymbol)];
alignas(SharedTagSymbol) char m[sizeof(SharedTagSymbol)];
alignas(CommonSymbol) char n[sizeof(CommonSymbol)];
};

// It is important to keep the size of SymbolUnion small for performance and
Expand Down
Loading