diff --git a/lld/test/wasm/globals.s b/lld/test/wasm/globals.s new file mode 100644 --- /dev/null +++ b/lld/test/wasm/globals.s @@ -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 diff --git a/lld/wasm/WriterUtils.cpp b/lld/wasm/WriterUtils.cpp --- a/lld/wasm/WriterUtils.cpp +++ b/lld/wasm/WriterUtils.cpp @@ -100,6 +100,11 @@ 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(type), msg + "[type: " + toString(type) + "]"); @@ -141,6 +146,12 @@ 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; diff --git a/llvm/include/llvm/BinaryFormat/Wasm.h b/llvm/include/llvm/BinaryFormat/Wasm.h --- a/llvm/include/llvm/BinaryFormat/Wasm.h +++ b/llvm/include/llvm/BinaryFormat/Wasm.h @@ -75,10 +75,10 @@ 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; }; diff --git a/llvm/lib/MC/WasmObjectWriter.cpp b/llvm/lib/MC/WasmObjectWriter.cpp --- a/llvm/lib/MC/WasmObjectWriter.cpp +++ b/llvm/lib/MC/WasmObjectWriter.cpp @@ -204,7 +204,7 @@ } // 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 @@ 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(Ty); } void writeTypeSection(ArrayRef Signatures); @@ -321,6 +333,7 @@ ArrayRef Functions); void writeDataSection(); void writeEventSection(ArrayRef Events); + void writeGlobalSection(ArrayRef Globals); void writeRelocSection(uint32_t SectionIndex, StringRef Name, std::vector &Relocations); void writeLinkingMetaDataSection( @@ -665,7 +678,7 @@ 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 @@ endSection(Section); } +void WasmObjectWriter::writeGlobalSection(ArrayRef 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 Exports) { if (Exports.empty()) return; @@ -1118,6 +1165,7 @@ SmallVector Imports; SmallVector Exports; SmallVector Events; + SmallVector Globals; SmallVector SymbolInfos; SmallVector, 2> InitFuncs; std::map> Comdats; @@ -1377,22 +1425,43 @@ } 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 @@ // 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(); diff --git a/llvm/test/MC/WebAssembly/globals.s b/llvm/test/MC/WebAssembly/globals.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/WebAssembly/globals.s @@ -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