forked from OSchip/llvm-project
[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:
parent
ecd3ce0e5a
commit
0a6c4d8d2e
|
@ -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
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
Loading…
Reference in New Issue