[WebAssmebly] Add support for defined wasm globals in MC and lld

This change add support for defined wasm globals in the .s format,
the MC layer, and wasm-ld

Currently there is no support custom initialization and all wasm
globals are initialized to zero.

Fixes: PR45742

Differential Revision: https://reviews.llvm.org/D79137
This commit is contained in:
Sam Clegg 2020-04-29 13:26:27 -07:00
parent ecd3ce0e5a
commit 0a6c4d8d2e
5 changed files with 227 additions and 14 deletions

53
lld/test/wasm/globals.s Normal file
View File

@ -0,0 +1,53 @@
# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %s
# RUN: wasm-ld %t.o -o %t.wasm
# RUN: obj2yaml %t.wasm | FileCheck %s
.globl _start
.globl read_global
.globl write_global
.globaltype foo_global, i32
.globaltype bar_global, f32
read_global:
.functype read_global () -> (i32)
global.get foo_global
end_function
write_global:
.functype write_global (i32) -> ()
local.get 0
global.set foo_global
f32.const 1.0
global.set bar_global
end_function
_start:
.functype _start () -> ()
i32.const 1
call write_global
end_function
foo_global:
bar_global:
# 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: 66560
# CHECK-NEXT: - Index: 1
# CHECK-NEXT: Type: I32
# CHECK-NEXT: Mutable: true
# CHECK-NEXT: InitExpr:
# CHECK-NEXT: Opcode: I32_CONST
# CHECK-NEXT: Value: 0
# CHECK-NEXT: - Index: 2
# CHECK-NEXT: Type: F32
# CHECK-NEXT: Mutable: true
# CHECK-NEXT: InitExpr:
# CHECK-NEXT: Opcode: F32_CONST
# CHECK-NEXT: Value: 0

View File

@ -100,6 +100,11 @@ void writeU32(raw_ostream &os, uint32_t number, const Twine &msg) {
support::endian::write(os, number, support::little);
}
void writeU64(raw_ostream &os, uint64_t number, const Twine &msg) {
debugWrite(os.tell(), msg + "[0x" + utohexstr(number) + "]");
support::endian::write(os, number, support::little);
}
void writeValueType(raw_ostream &os, ValType type, const Twine &msg) {
writeU8(os, static_cast<uint8_t>(type),
msg + "[type: " + toString(type) + "]");
@ -141,6 +146,12 @@ void writeInitExpr(raw_ostream &os, const WasmInitExpr &initExpr) {
case WASM_OPCODE_I64_CONST:
writeSleb128(os, initExpr.Value.Int64, "literal (i64)");
break;
case WASM_OPCODE_F32_CONST:
writeU32(os, initExpr.Value.Float32, "literal (f32)");
break;
case WASM_OPCODE_F64_CONST:
writeU64(os, initExpr.Value.Float64, "literal (f64)");
break;
case WASM_OPCODE_GLOBAL_GET:
writeUleb128(os, initExpr.Value.Global, "literal (global index)");
break;

View File

@ -75,10 +75,10 @@ struct WasmTable {
struct WasmInitExpr {
uint8_t Opcode;
union {
int32_t Int32;
int64_t Int64;
int32_t Float32;
int64_t Float64;
uint32_t Int32;
uint64_t Int64;
uint32_t Float32;
uint64_t Float64;
uint32_t Global;
} Value;
};

View File

@ -204,7 +204,7 @@ static void writePatchableSLEB(raw_pwrite_stream &Stream, int32_t X,
}
// Write X as a plain integer value at offset Offset in Stream.
static void writeI32(raw_pwrite_stream &Stream, uint32_t X, uint64_t Offset) {
static void patchI32(raw_pwrite_stream &Stream, uint32_t X, uint64_t Offset) {
uint8_t Buffer[4];
support::endian::write32le(Buffer, X);
Stream.pwrite((char *)Buffer, sizeof(Buffer), Offset);
@ -308,6 +308,18 @@ private:
W.OS << Str;
}
void writeI32(int32_t val) {
char Buffer[4];
support::endian::write32le(Buffer, val);
W.OS.write(Buffer, sizeof(Buffer));
}
void writeI64(int64_t val) {
char Buffer[8];
support::endian::write64le(Buffer, val);
W.OS.write(Buffer, sizeof(Buffer));
}
void writeValueType(wasm::ValType Ty) { W.OS << static_cast<char>(Ty); }
void writeTypeSection(ArrayRef<WasmSignature> Signatures);
@ -321,6 +333,7 @@ private:
ArrayRef<WasmFunction> Functions);
void writeDataSection();
void writeEventSection(ArrayRef<wasm::WasmEventType> Events);
void writeGlobalSection(ArrayRef<wasm::WasmGlobal> Globals);
void writeRelocSection(uint32_t SectionIndex, StringRef Name,
std::vector<WasmRelocationEntry> &Relocations);
void writeLinkingMetaDataSection(
@ -665,7 +678,7 @@ void WasmObjectWriter::applyRelocations(
case wasm::R_WASM_FUNCTION_OFFSET_I32:
case wasm::R_WASM_SECTION_OFFSET_I32:
case wasm::R_WASM_GLOBAL_INDEX_I32:
writeI32(Stream, Value, Offset);
patchI32(Stream, Value, Offset);
break;
case wasm::R_WASM_TABLE_INDEX_SLEB:
case wasm::R_WASM_TABLE_INDEX_REL_SLEB:
@ -777,6 +790,40 @@ void WasmObjectWriter::writeEventSection(ArrayRef<wasm::WasmEventType> Events) {
endSection(Section);
}
void WasmObjectWriter::writeGlobalSection(ArrayRef<wasm::WasmGlobal> Globals) {
if (Globals.empty())
return;
SectionBookkeeping Section;
startSection(Section, wasm::WASM_SEC_GLOBAL);
encodeULEB128(Globals.size(), W.OS);
for (const wasm::WasmGlobal &Global : Globals) {
encodeULEB128(Global.Type.Type, W.OS);
W.OS << char(Global.Type.Mutable);
W.OS << char(Global.InitExpr.Opcode);
switch (Global.Type.Type) {
case wasm::WASM_TYPE_I32:
encodeSLEB128(0, W.OS);
break;
case wasm::WASM_TYPE_I64:
encodeSLEB128(0, W.OS);
break;
case wasm::WASM_TYPE_F32:
writeI32(0);
break;
case wasm::WASM_TYPE_F64:
writeI64(0);
break;
default:
llvm_unreachable("unexpected type");
}
W.OS << char(wasm::WASM_OPCODE_END);
}
endSection(Section);
}
void WasmObjectWriter::writeExportSection(ArrayRef<wasm::WasmExport> Exports) {
if (Exports.empty())
return;
@ -1118,6 +1165,7 @@ uint64_t WasmObjectWriter::writeObject(MCAssembler &Asm,
SmallVector<wasm::WasmImport, 4> Imports;
SmallVector<wasm::WasmExport, 4> Exports;
SmallVector<wasm::WasmEventType, 1> Events;
SmallVector<wasm::WasmGlobal, 1> Globals;
SmallVector<wasm::WasmSymbolInfo, 4> SymbolInfos;
SmallVector<std::pair<uint16_t, uint32_t>, 2> InitFuncs;
std::map<StringRef, std::vector<WasmComdatEntry>> Comdats;
@ -1377,22 +1425,43 @@ uint64_t WasmObjectWriter::writeObject(MCAssembler &Asm,
} else if (WS.isGlobal()) {
// A "true" Wasm global (currently just __stack_pointer)
if (WS.isDefined())
report_fatal_error("don't yet support defined globals");
// An import; the index was assigned above
LLVM_DEBUG(dbgs() << " -> global index: "
<< WasmIndices.find(&WS)->second << "\n");
if (WS.isDefined()) {
assert(WasmIndices.count(&WS) == 0);
wasm::WasmGlobal Global;
Global.Type = WS.getGlobalType();
Global.Index = NumGlobalImports + Globals.size();
switch (Global.Type.Type) {
case wasm::WASM_TYPE_I32:
Global.InitExpr.Opcode = wasm::WASM_OPCODE_I32_CONST;
break;
case wasm::WASM_TYPE_I64:
Global.InitExpr.Opcode = wasm::WASM_OPCODE_I64_CONST;
break;
case wasm::WASM_TYPE_F32:
Global.InitExpr.Opcode = wasm::WASM_OPCODE_F32_CONST;
break;
case wasm::WASM_TYPE_F64:
Global.InitExpr.Opcode = wasm::WASM_OPCODE_F64_CONST;
break;
default:
llvm_unreachable("unexpected type");
}
WasmIndices[&WS] = Global.Index;
Globals.push_back(Global);
} else {
// An import; the index was assigned above
LLVM_DEBUG(dbgs() << " -> global index: "
<< WasmIndices.find(&WS)->second << "\n");
}
} else if (WS.isEvent()) {
// C++ exception symbol (__cpp_exception)
unsigned Index;
if (WS.isDefined()) {
assert(WasmIndices.count(&WS) == 0);
Index = NumEventImports + Events.size();
wasm::WasmEventType Event;
Event.SigIndex = getEventType(WS);
Event.Attribute = wasm::WASM_EVENT_ATTRIBUTE_EXCEPTION;
assert(WasmIndices.count(&WS) == 0);
WasmIndices[&WS] = Index;
Events.push_back(Event);
} else {
@ -1584,6 +1653,7 @@ uint64_t WasmObjectWriter::writeObject(MCAssembler &Asm,
// Skip the "table" section; we import the table instead.
// Skip the "memory" section; we import the memory instead.
writeEventSection(Events);
writeGlobalSection(Globals);
writeExportSection(Exports);
writeElemSection(TableElems);
writeDataCountSection();

View File

@ -0,0 +1,79 @@
# RUN: llvm-mc -triple=wasm32-unknown-unknown < %s | FileCheck %s
# RUN: llvm-mc -triple=wasm32-unknown-unknown -filetype=obj < %s | obj2yaml | FileCheck -check-prefix=BIN %s
# Tests creating an accessing actual wasm globals
.globl read_global
.globl write_global
.globaltype foo_global, i32
.globaltype global2, i64
.globaltype global3, f32
.globaltype global4, f64
read_global:
.functype read_global () -> (i32)
global.get foo_global
end_function
write_global:
.functype write_global (i32) -> ()
local.get 0
global.set foo_global
global.set global2
global.set global3
global.set global4
end_function
foo_global:
global2:
global3:
global4:
# CHECK: .globl read_global
# CNEXT: .globl write_global
# CHECK: .globaltype foo_global, i32
# CHECK: foo_global:
# BIN: - Type: GLOBAL
# BIN-NEXT: Globals:
# BIN-NEXT: - Index: 0
# BIN-NEXT: Type: I32
# BIN-NEXT: Mutable: true
# BIN-NEXT: InitExpr:
# BIN-NEXT: Opcode: I32_CONST
# BIN-NEXT: Value: 0
# BIN: - Type: CUSTOM
# BIN-NEXT: Name: linking
# BIN-NEXT: Version: 2
# BIN-NEXT: SymbolTable:
# BIN-NEXT: - Index: 0
# BIN-NEXT: Kind: FUNCTION
# BIN-NEXT: Name: read_global
# BIN-NEXT: Flags: [ ]
# BIN-NEXT: Function: 0
# BIN-NEXT: - Index: 1
# BIN-NEXT: Kind: FUNCTION
# BIN-NEXT: Name: write_global
# BIN-NEXT: Flags: [ ]
# BIN-NEXT: Function: 1
# BIN-NEXT: - Index: 2
# BIN-NEXT: Kind: GLOBAL
# BIN-NEXT: Name: foo_global
# BIN-NEXT: Flags: [ BINDING_LOCAL ]
# BIN-NEXT: Global: 0
# BIN-NEXT: - Index: 3
# BIN-NEXT: Kind: GLOBAL
# BIN-NEXT: Name: global2
# BIN-NEXT: Flags: [ BINDING_LOCAL ]
# BIN-NEXT: Global: 1
# BIN-NEXT: - Index: 4
# BIN-NEXT: Kind: GLOBAL
# BIN-NEXT: Name: global3
# BIN-NEXT: Flags: [ BINDING_LOCAL ]
# BIN-NEXT: Global: 2
# BIN-NEXT: - Index: 5
# BIN-NEXT: Kind: GLOBAL
# BIN-NEXT: Name: global4
# BIN-NEXT: Flags: [ BINDING_LOCAL ]
# BIN-NEXT: Global: 3