diff --git a/lld/test/wasm/common.s b/lld/test/wasm/common.s new file mode 100644 index 0000000000000..09a1c5d60e14a --- /dev/null +++ b/lld/test/wasm/common.s @@ -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 diff --git a/lld/wasm/Config.h b/lld/wasm/Config.h index 491bf9233b0cf..b2a5febeb2807 100644 --- a/lld/wasm/Config.h +++ b/lld/wasm/Config.h @@ -38,6 +38,7 @@ class DefinedFunction; class DefinedGlobal; class UndefinedGlobal; class TableSymbol; +class InputChunk; // For --unresolved-symbols. enum class UnresolvedPolicy { ReportError, Warn, Ignore, ImportDynamic }; @@ -146,6 +147,7 @@ struct Ctx { llvm::SmallVector syntheticFunctions; llvm::SmallVector syntheticGlobals; llvm::SmallVector syntheticTables; + llvm::SmallVector syntheticInputSegments; // linker-generated symbols struct WasmSym { diff --git a/lld/wasm/InputChunks.cpp b/lld/wasm/InputChunks.cpp index 79bac5f444405..e5662822f41b1 100644 --- a/lld/wasm/InputChunks.cpp +++ b/lld/wasm/InputChunks.cpp @@ -73,6 +73,9 @@ uint32_t InputChunk::getSize() const { } } + if (const auto *sis = dyn_cast(this)) + return sis->getSize(); + return data().size(); } @@ -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(this)) { + sis->writeTo(buf); + return; } // Copy contents @@ -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(this)) { LLVM_DEBUG(dbgs() << "getChunkOffset(merged): " << name << "\n"); diff --git a/lld/wasm/InputChunks.h b/lld/wasm/InputChunks.h index e772894d95456..4a7739bef4539 100644 --- a/lld/wasm/InputChunks.h +++ b/lld/wasm/InputChunks.h @@ -45,6 +45,7 @@ class InputChunk { Function, SyntheticFunction, Section, + SyntheticDataSegment, }; StringRef name; @@ -341,6 +342,25 @@ class SyntheticFunction : public InputFunction { void setBody(ArrayRef 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: diff --git a/lld/wasm/InputFiles.cpp b/lld/wasm/InputFiles.cpp index ad7f207259a5b..694ba5e3506fa 100644 --- a/lld/wasm/InputFiles.cpp +++ b/lld/wasm/InputFiles.cpp @@ -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; diff --git a/lld/wasm/SymbolTable.cpp b/lld/wasm/SymbolTable.cpp index 88ac54302c286..a178951890d26 100644 --- a/lld/wasm/SymbolTable.cpp +++ b/lld/wasm/SymbolTable.cpp @@ -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"); @@ -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(s, name, flags, file, size, alignment); + }; + + if (wasInserted || s->isLazy()) { + replaceSym(); + return s; + } + + checkDataType(s, file); + + if (auto *existingCommon = dyn_cast(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) { diff --git a/lld/wasm/SymbolTable.h b/lld/wasm/SymbolTable.h index 0667cedecdb4f..007aabdca0f35 100644 --- a/lld/wasm/SymbolTable.h +++ b/lld/wasm/SymbolTable.h @@ -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, diff --git a/lld/wasm/Symbols.cpp b/lld/wasm/Symbols.cpp index 9ce2a9f9f5091..d82e13b8fb2e0 100644 --- a/lld/wasm/Symbols.cpp +++ b/lld/wasm/Symbols.cpp @@ -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: diff --git a/lld/wasm/Symbols.h b/lld/wasm/Symbols.h index 47f1b3ad54ea2..1e5c85bd94954 100644 --- a/lld/wasm/Symbols.h +++ b/lld/wasm/Symbols.h @@ -59,6 +59,7 @@ class Symbol { UndefinedGlobalKind, UndefinedTableKind, UndefinedTagKind, + CommonKind, LazyKind, SharedFunctionKind, SharedDataKind, @@ -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; @@ -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: @@ -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) { @@ -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 diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp index 038a8b3f5417d..71b3a902e368c 100644 --- a/lld/wasm/Writer.cpp +++ b/lld/wasm/Writer.cpp @@ -84,6 +84,7 @@ class Writer { void calculateCustomSections(); void calculateTypes(); void createOutputSegments(); + void allocateCommonSymbols(); OutputSegment *createOutputSegment(StringRef name); void combineOutputSegments(); void layoutMemory(); @@ -1046,6 +1047,51 @@ OutputSegment *Writer::createOutputSegment(StringRef name) { return s; } +void Writer::allocateCommonSymbols() { + if (ctx.arg.relocatable) + return; + + std::vector commons; + for (Symbol *sym : symtab->symbols()) { + if (auto *c = dyn_cast(sym)) { + if (c->isLive()) { + commons.push_back(c); + } + } + } + + if (commons.empty()) + return; + + log("-- allocateCommonSymbols"); + + uint64_t size = 0; + uint32_t alignLog2 = 0; + + for (CommonSymbol *c : commons) { + alignLog2 = std::max(alignLog2, c->getAlignment()); + size = alignTo(size, 1ULL << c->getAlignment()); + size += c->getSize(); + } + + auto *commonSeg = make(".bss.common", alignLog2, 0); + commonSeg->setSize(size); + commonSeg->live = true; + ctx.syntheticInputSegments.push_back(commonSeg); + + uint64_t offset = 0; + for (CommonSymbol *c : commons) { + uint64_t size = c->getSize(); + uint32_t alignLog2 = c->getAlignment(); + offset = alignTo(offset, 1ULL << alignLog2); + log(formatv("allocateCommonSymbol: {0} size={1} align={2} offset={3}", + c->getName(), size, alignLog2, offset)); + replaceSymbol(c, c->getName(), c->flags, c->getFile(), + commonSeg, offset, size); + offset += size; + } +} + void Writer::createOutputSegments() { for (ObjFile *file : ctx.objectFiles) { for (InputChunk *segment : file->segments) { @@ -1067,6 +1113,18 @@ void Writer::createOutputSegments() { } } + // Process synthetic segments + for (InputChunk *segment : ctx.syntheticInputSegments) { + if (!segment->live) + continue; + StringRef name = getOutputDataSegmentName(*segment); + OutputSegment *s = nullptr; + if (!segmentMap.contains(name)) + segmentMap[name] = createOutputSegment(name); + s = segmentMap[name]; + s->addInputSegment(segment); + } + // Sort segments by type, placing .bss last llvm::stable_sort(segments, [](const OutputSegment *a, const OutputSegment *b) { @@ -1734,6 +1792,9 @@ void Writer::run() { if (!ctx.isPic && ctx.sym.tableBase) setGlobalPtr(cast(ctx.sym.tableBase), ctx.arg.tableBase); + log("-- allocateCommonSymbols"); + allocateCommonSymbols(); + log("-- createOutputSegments"); createOutputSegments(); log("-- createSyntheticSections"); diff --git a/llvm/include/llvm/BinaryFormat/Wasm.h b/llvm/include/llvm/BinaryFormat/Wasm.h index fa71b9b8480f1..1ab9b351a8342 100644 --- a/llvm/include/llvm/BinaryFormat/Wasm.h +++ b/llvm/include/llvm/BinaryFormat/Wasm.h @@ -251,6 +251,7 @@ const unsigned WASM_SYMBOL_VISIBILITY_MASK = 0xc; const unsigned WASM_SYMBOL_BINDING_GLOBAL = 0x0; const unsigned WASM_SYMBOL_BINDING_WEAK = 0x1; const unsigned WASM_SYMBOL_BINDING_LOCAL = 0x2; +const unsigned WASM_SYMBOL_BINDING_COMMON = 0x3; const unsigned WASM_SYMBOL_VISIBILITY_DEFAULT = 0x0; const unsigned WASM_SYMBOL_VISIBILITY_HIDDEN = 0x4; const unsigned WASM_SYMBOL_UNDEFINED = 0x10; @@ -456,6 +457,11 @@ struct WasmDataReference { uint64_t Size; }; +struct WasmCommonReference { + uint64_t Size; + uint32_t Alignment; +}; + struct WasmRelocation { uint8_t Type; // The type of the relocation. uint32_t Index; // Index into either symbol or type index space. @@ -486,6 +492,8 @@ struct WasmSymbolInfo { uint32_t ElementIndex; // For a data symbols, the address of the data relative to segment. WasmDataReference DataRef; + // For common symbols, the size and alignment. + WasmCommonReference CommonRef; }; }; diff --git a/llvm/include/llvm/ObjectYAML/WasmYAML.h b/llvm/include/llvm/ObjectYAML/WasmYAML.h index b0ab04d1d8ac3..f9c678a6a9d07 100644 --- a/llvm/include/llvm/ObjectYAML/WasmYAML.h +++ b/llvm/include/llvm/ObjectYAML/WasmYAML.h @@ -166,6 +166,7 @@ struct SymbolInfo { union { uint32_t ElementIndex; wasm::WasmDataReference DataRef; + wasm::WasmCommonReference CommonRef; }; }; diff --git a/llvm/lib/MC/MCWasmStreamer.cpp b/llvm/lib/MC/MCWasmStreamer.cpp index 8b4ae4cee3e79..73a0369b3993c 100644 --- a/llvm/lib/MC/MCWasmStreamer.cpp +++ b/llvm/lib/MC/MCWasmStreamer.cpp @@ -130,9 +130,19 @@ bool MCWasmStreamer::emitSymbolAttribute(MCSymbol *S, MCSymbolAttr Attribute) { void MCWasmStreamer::emitCommonSymbol(MCSymbol *S, uint64_t Size, Align ByteAlignment) { - getContext().reportError(getStartTokLoc(), - "common symbols are not yet implemented for Wasm: " + - S->getName()); + auto *Symbol = static_cast(S); + getAssembler().registerSymbol(*Symbol); + + if (!Symbol->isExternal()) + Symbol->setExternal(true); + + Symbol->setType(wasm::WASM_SYMBOL_TYPE_DATA); + + if (Symbol->declareCommon(Size, ByteAlignment)) + report_fatal_error(Twine("Symbol: ") + Symbol->getName() + + " redeclared as different type"); + + Symbol->setSize(MCConstantExpr::create(Size, getContext())); } void MCWasmStreamer::emitELFSize(MCSymbol *Symbol, const MCExpr *Value) { diff --git a/llvm/lib/MC/WasmObjectWriter.cpp b/llvm/lib/MC/WasmObjectWriter.cpp index 44ca69f0ff302..2c780576142a5 100644 --- a/llvm/lib/MC/WasmObjectWriter.cpp +++ b/llvm/lib/MC/WasmObjectWriter.cpp @@ -1161,9 +1161,15 @@ void WasmObjectWriter::writeLinkingMetaDataSection( case wasm::WASM_SYMBOL_TYPE_DATA: writeString(Sym.Name); if ((Sym.Flags & wasm::WASM_SYMBOL_UNDEFINED) == 0) { - encodeULEB128(Sym.DataRef.Segment, W->OS); - encodeULEB128(Sym.DataRef.Offset, W->OS); - encodeULEB128(Sym.DataRef.Size, W->OS); + if ((Sym.Flags & wasm::WASM_SYMBOL_BINDING_MASK) == + wasm::WASM_SYMBOL_BINDING_COMMON) { + encodeULEB128(Sym.CommonRef.Size, W->OS); + W->OS << char(Sym.CommonRef.Alignment); + } else { + encodeULEB128(Sym.DataRef.Segment, W->OS); + encodeULEB128(Sym.DataRef.Offset, W->OS); + encodeULEB128(Sym.DataRef.Size, W->OS); + } } break; case wasm::WASM_SYMBOL_TYPE_SECTION: { @@ -1789,9 +1795,11 @@ uint64_t WasmObjectWriter::writeOneObject(MCAssembler &Asm, Flags |= wasm::WASM_SYMBOL_BINDING_WEAK; if (WS.isHidden()) Flags |= wasm::WASM_SYMBOL_VISIBILITY_HIDDEN; - if (!WS.isExternal() && WS.isDefined()) + if (WS.isCommon()) + Flags |= wasm::WASM_SYMBOL_BINDING_COMMON; + else if (!WS.isExternal() && WS.isDefined()) Flags |= wasm::WASM_SYMBOL_BINDING_LOCAL; - if (WS.isUndefined()) + if (WS.isUndefined() && !WS.isCommon()) Flags |= wasm::WASM_SYMBOL_UNDEFINED; if (WS.isNoStrip()) { Flags |= wasm::WASM_SYMBOL_NO_STRIP; @@ -1813,6 +1821,9 @@ uint64_t WasmObjectWriter::writeOneObject(MCAssembler &Asm, if (!WS.isData()) { assert(WasmIndices.contains(&WS)); Info.ElementIndex = WasmIndices.find(&WS)->second; + } else if (WS.isCommon()) { + Info.CommonRef.Size = WS.getCommonSize(); + Info.CommonRef.Alignment = Log2(WS.getCommonAlignment().valueOrOne()); } else if (WS.isDefined()) { assert(DataLocations.contains(&WS)); Info.DataRef = DataLocations.find(&WS)->second; diff --git a/llvm/lib/Object/WasmObjectFile.cpp b/llvm/lib/Object/WasmObjectFile.cpp index 98f60bd710c7e..6ad9eca63a358 100644 --- a/llvm/lib/Object/WasmObjectFile.cpp +++ b/llvm/lib/Object/WasmObjectFile.cpp @@ -829,23 +829,34 @@ Error WasmObjectFile::parseLinkingSectionSymtab(ReadContext &Ctx) { case wasm::WASM_SYMBOL_TYPE_DATA: Info.Name = readString(Ctx); if (IsDefined) { - auto Index = readVaruint32(Ctx); - auto Offset = readVaruint64(Ctx); - auto Size = readVaruint64(Ctx); - if (!(Info.Flags & wasm::WASM_SYMBOL_ABSOLUTE)) { - if (Index >= DataSegments.size()) - return make_error( - "invalid data segment index: " + Twine(Index), - object_error::parse_failed); - size_t SegmentSize = DataSegments[Index].Data.Content.size(); - if (Offset > SegmentSize) + if ((Info.Flags & wasm::WASM_SYMBOL_BINDING_MASK) == + wasm::WASM_SYMBOL_BINDING_COMMON) { + if (Info.Flags & wasm::WASM_SYMBOL_ABSOLUTE) return make_error( - "invalid data symbol offset: `" + Info.Name + - "` (offset: " + Twine(Offset) + - " segment size: " + Twine(SegmentSize) + ")", + "common symbols cannot be absolute: " + Info.Name, object_error::parse_failed); + auto Size = readVaruint64(Ctx); + auto Alignment = readUint8(Ctx); + Info.CommonRef = wasm::WasmCommonReference{Size, Alignment}; + } else { + auto Index = readVaruint32(Ctx); + auto Offset = readVaruint64(Ctx); + auto Size = readVaruint64(Ctx); + if (!(Info.Flags & wasm::WASM_SYMBOL_ABSOLUTE)) { + if (Index >= DataSegments.size()) + return make_error( + "invalid data segment index: " + Twine(Index), + object_error::parse_failed); + size_t SegmentSize = DataSegments[Index].Data.Content.size(); + if (Offset > SegmentSize) + return make_error( + "invalid data symbol offset: `" + Info.Name + + "` (offset: " + Twine(Offset) + + " segment size: " + Twine(SegmentSize) + ")", + object_error::parse_failed); + } + Info.DataRef = wasm::WasmDataReference{Index, Offset, Size}; } - Info.DataRef = wasm::WasmDataReference{Index, Offset, Size}; } break; diff --git a/llvm/lib/ObjectYAML/WasmYAML.cpp b/llvm/lib/ObjectYAML/WasmYAML.cpp index edbcb3c6ead11..8c93d3a0763f7 100644 --- a/llvm/lib/ObjectYAML/WasmYAML.cpp +++ b/llvm/lib/ObjectYAML/WasmYAML.cpp @@ -523,11 +523,17 @@ void MappingTraits::mapping(IO &IO, IO.mapRequired("Tag", Info.ElementIndex); } else if (Info.Kind == wasm::WASM_SYMBOL_TYPE_DATA) { if ((Info.Flags & wasm::WASM_SYMBOL_UNDEFINED) == 0) { - if ((Info.Flags & wasm::WASM_SYMBOL_ABSOLUTE) == 0) { - IO.mapRequired("Segment", Info.DataRef.Segment); + if ((Info.Flags & wasm::WASM_SYMBOL_BINDING_MASK) == + wasm::WASM_SYMBOL_BINDING_COMMON) { + IO.mapRequired("Size", Info.CommonRef.Size); + IO.mapRequired("Align", Info.CommonRef.Alignment); + } else { + if ((Info.Flags & wasm::WASM_SYMBOL_ABSOLUTE) == 0) { + IO.mapRequired("Segment", Info.DataRef.Segment); + } + IO.mapOptional("Offset", Info.DataRef.Offset, 0u); + IO.mapRequired("Size", Info.DataRef.Size); } - IO.mapOptional("Offset", Info.DataRef.Offset, 0u); - IO.mapRequired("Size", Info.DataRef.Size); } } else if (Info.Kind == wasm::WASM_SYMBOL_TYPE_SECTION) { IO.mapRequired("Section", Info.ElementIndex); @@ -575,6 +581,7 @@ void ScalarBitSetTraits::bitset( // BCaseMask(BINDING_MASK, BINDING_GLOBAL); BCaseMask(BINDING_MASK, BINDING_WEAK); BCaseMask(BINDING_MASK, BINDING_LOCAL); + BCaseMask(BINDING_MASK, BINDING_COMMON); // BCaseMask(VISIBILITY_MASK, VISIBILITY_DEFAULT); BCaseMask(VISIBILITY_MASK, VISIBILITY_HIDDEN); BCaseMask(UNDEFINED, UNDEFINED); diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp index edea99e629407..b32507b7c1a10 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp @@ -180,13 +180,6 @@ MCSymbolWasm *WebAssemblyAsmPrinter::getMCSymbolForFunction( } void WebAssemblyAsmPrinter::emitGlobalVariable(const GlobalVariable *GV) { - if (GV->hasCommonLinkage()) { - OutContext.reportError(SMLoc(), - "common symbols are not yet implemented for Wasm: " + - getSymbol(GV)->getName()); - return; - } - if (!WebAssembly::isWasmVarAddressSpace(GV->getAddressSpace())) { AsmPrinter::emitGlobalVariable(GV); return; diff --git a/llvm/test/CodeGen/WebAssembly/common-error.ll b/llvm/test/CodeGen/WebAssembly/common-error.ll deleted file mode 100644 index 8fce516fbf88e..0000000000000 --- a/llvm/test/CodeGen/WebAssembly/common-error.ll +++ /dev/null @@ -1,6 +0,0 @@ -; RUN: not llc -mtriple=wasm32-unknown-unknown -filetype=asm %s -o - 2>&1 | FileCheck %s - -; CHECK: common symbols are not yet implemented for Wasm: x -; CHECK: common symbols are not yet implemented for Wasm: y -@x = common global i32 0, align 4 -@y = common global i32 0, align 4 diff --git a/llvm/test/CodeGen/WebAssembly/common.ll b/llvm/test/CodeGen/WebAssembly/common.ll new file mode 100644 index 0000000000000..ea372465f926b --- /dev/null +++ b/llvm/test/CodeGen/WebAssembly/common.ll @@ -0,0 +1,9 @@ +; RUN: llc -mtriple=wasm32-unknown-unknown -filetype=asm %s -o - | FileCheck %s + +; CHECK: .type x,@object +; CHECK-NEXT: .comm x,4,2 +; CHECK: .type y,@object +; CHECK-NEXT: .comm y,8,3 + +@x = common global i32 0, align 4 +@y = common global i64 0, align 8 diff --git a/llvm/test/MC/WebAssembly/common-error.s b/llvm/test/MC/WebAssembly/common-error.s deleted file mode 100644 index 38c8fbca352b2..0000000000000 --- a/llvm/test/MC/WebAssembly/common-error.s +++ /dev/null @@ -1,6 +0,0 @@ -# RUN: not llvm-mc -triple=wasm32-unknown-unknown -filetype=obj %s 2>&1 | FileCheck %s - -# CHECK:common-error.s:5:9: error: common symbols are not yet implemented for Wasm: x -# CHECK:common-error.s:6:9: error: common symbols are not yet implemented for Wasm: y - .comm x,4,4 - .comm y,4,4 diff --git a/llvm/test/MC/WebAssembly/common.s b/llvm/test/MC/WebAssembly/common.s new file mode 100644 index 0000000000000..9af65dbcc4a84 --- /dev/null +++ b/llvm/test/MC/WebAssembly/common.s @@ -0,0 +1,31 @@ +# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown %s -o %t.o +# RUN: obj2yaml %t.o | FileCheck %s + + .comm x,4,4 + .comm y,8,8 + + .hidden z + .comm z,16,16 + +# CHECK: - Type: CUSTOM +# CHECK-NEXT: Name: linking +# CHECK-NEXT: Version: 2 +# CHECK-NEXT: SymbolTable: +# CHECK-NEXT: - Index: 0 +# CHECK-NEXT: Kind: DATA +# CHECK-NEXT: Name: x +# CHECK-NEXT: Flags: [ BINDING_COMMON ] +# CHECK-NEXT: Size: 4 +# CHECK-NEXT: Align: 4 +# CHECK-NEXT: - Index: 1 +# CHECK-NEXT: Kind: DATA +# CHECK-NEXT: Name: y +# CHECK-NEXT: Flags: [ BINDING_COMMON ] +# CHECK-NEXT: Size: 8 +# CHECK-NEXT: Align: 8 +# CHECK-NEXT: - Index: 2 +# CHECK-NEXT: Kind: DATA +# CHECK-NEXT: Name: z +# CHECK-NEXT: Flags: [ BINDING_COMMON, VISIBILITY_HIDDEN ] +# CHECK-NEXT: Size: 16 +# CHECK-NEXT: Align: 16 diff --git a/llvm/test/Object/Wasm/common-absolute.yaml b/llvm/test/Object/Wasm/common-absolute.yaml new file mode 100644 index 0000000000000..930584aa5dc00 --- /dev/null +++ b/llvm/test/Object/Wasm/common-absolute.yaml @@ -0,0 +1,21 @@ +# RUN: yaml2obj %s | not llvm-objdump -s - 2>&1 | FileCheck %s + +# Check that a common data symbol cannot be marked as absolute. + +# CHECK: common symbols cannot be absolute: foo + +--- !WASM +FileHeader: + Version: 0x00000001 +Sections: + - Type: CUSTOM + Name: linking + Version: 2 + SymbolTable: + - Index: 0 + Kind: DATA + Name: foo + Flags: [ BINDING_COMMON, ABSOLUTE ] + Size: 4 + Align: 4 +... diff --git a/llvm/tools/llvm-readobj/WasmDumper.cpp b/llvm/tools/llvm-readobj/WasmDumper.cpp index 5d7639b0b70fd..b8f37c7f5f2d1 100644 --- a/llvm/tools/llvm-readobj/WasmDumper.cpp +++ b/llvm/tools/llvm-readobj/WasmDumper.cpp @@ -42,15 +42,11 @@ const EnumEntry WasmSectionTypes[] = { const EnumEntry WasmSymbolFlags[] = { #define ENUM_ENTRY(X) \ { #X, wasm::WASM_SYMBOL_##X } - ENUM_ENTRY(BINDING_GLOBAL), - ENUM_ENTRY(BINDING_WEAK), - ENUM_ENTRY(BINDING_LOCAL), - ENUM_ENTRY(VISIBILITY_DEFAULT), - ENUM_ENTRY(VISIBILITY_HIDDEN), - ENUM_ENTRY(UNDEFINED), - ENUM_ENTRY(EXPORTED), - ENUM_ENTRY(EXPLICIT_NAME), - ENUM_ENTRY(NO_STRIP), + ENUM_ENTRY(BINDING_GLOBAL), ENUM_ENTRY(BINDING_WEAK), + ENUM_ENTRY(BINDING_LOCAL), ENUM_ENTRY(BINDING_COMMON), + ENUM_ENTRY(VISIBILITY_DEFAULT), ENUM_ENTRY(VISIBILITY_HIDDEN), + ENUM_ENTRY(UNDEFINED), ENUM_ENTRY(EXPORTED), + ENUM_ENTRY(EXPLICIT_NAME), ENUM_ENTRY(NO_STRIP), #undef ENUM_ENTRY }; @@ -235,9 +231,15 @@ void WasmDumper::printSymbol(const SymbolRef &Sym) { if (Symbol.Info.Kind != wasm::WASM_SYMBOL_TYPE_DATA) { W.printHex("ElementIndex", Symbol.Info.ElementIndex); } else if (!(Symbol.Info.Flags & wasm::WASM_SYMBOL_UNDEFINED)) { - W.printHex("Offset", Symbol.Info.DataRef.Offset); - W.printHex("Segment", Symbol.Info.DataRef.Segment); - W.printHex("Size", Symbol.Info.DataRef.Size); + if ((Symbol.Info.Flags & wasm::WASM_SYMBOL_BINDING_MASK) == + wasm::WASM_SYMBOL_BINDING_COMMON) { + W.printHex("Size", Symbol.Info.CommonRef.Size); + W.printHex("Alignment", Symbol.Info.CommonRef.Alignment); + } else { + W.printHex("Offset", Symbol.Info.DataRef.Offset); + W.printHex("Segment", Symbol.Info.DataRef.Segment); + W.printHex("Size", Symbol.Info.DataRef.Size); + } } } diff --git a/llvm/tools/obj2yaml/wasm2yaml.cpp b/llvm/tools/obj2yaml/wasm2yaml.cpp index 7b504faa5ae4e..2db5387b1ec6c 100644 --- a/llvm/tools/obj2yaml/wasm2yaml.cpp +++ b/llvm/tools/obj2yaml/wasm2yaml.cpp @@ -135,7 +135,12 @@ WasmDumper::dumpCustomSection(const WasmSection &WasmSec) { Info.Flags = Symbol.Flags; switch (Symbol.Kind) { case wasm::WASM_SYMBOL_TYPE_DATA: - Info.DataRef = Symbol.DataRef; + if ((Symbol.Flags & wasm::WASM_SYMBOL_BINDING_MASK) == + wasm::WASM_SYMBOL_BINDING_COMMON) { + Info.CommonRef = Symbol.CommonRef; + } else { + Info.DataRef = Symbol.DataRef; + } break; case wasm::WASM_SYMBOL_TYPE_FUNCTION: case wasm::WASM_SYMBOL_TYPE_GLOBAL: