diff --git a/llvm/include/llvm/BinaryFormat/WasmRelocs.def b/llvm/include/llvm/BinaryFormat/WasmRelocs.def --- a/llvm/include/llvm/BinaryFormat/WasmRelocs.def +++ b/llvm/include/llvm/BinaryFormat/WasmRelocs.def @@ -22,3 +22,4 @@ WASM_RELOC(R_WASM_MEMORY_ADDR_REL_SLEB64, 17) WASM_RELOC(R_WASM_TABLE_INDEX_SLEB64, 18) WASM_RELOC(R_WASM_TABLE_INDEX_I64, 19) +WASM_RELOC(R_WASM_TABLE_NUMBER_LEB, 20) diff --git a/llvm/include/llvm/Object/Wasm.h b/llvm/include/llvm/Object/Wasm.h --- a/llvm/include/llvm/Object/Wasm.h +++ b/llvm/include/llvm/Object/Wasm.h @@ -51,6 +51,8 @@ return Info.Kind == wasm::WASM_SYMBOL_TYPE_FUNCTION; } + bool isTypeTable() const { return Info.Kind == wasm::WASM_SYMBOL_TYPE_TABLE; } + bool isTypeData() const { return Info.Kind == wasm::WASM_SYMBOL_TYPE_DATA; } bool isTypeGlobal() const { @@ -222,6 +224,7 @@ bool isValidEventIndex(uint32_t Index) const; bool isDefinedEventIndex(uint32_t Index) const; bool isValidFunctionSymbol(uint32_t Index) const; + bool isValidTableSymbol(uint32_t Index) const; bool isValidGlobalSymbol(uint32_t Index) const; bool isValidEventSymbol(uint32_t Index) const; bool isValidDataSymbol(uint32_t Index) const; 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 @@ -559,6 +559,7 @@ case wasm::R_WASM_GLOBAL_INDEX_LEB: case wasm::R_WASM_GLOBAL_INDEX_I32: case wasm::R_WASM_EVENT_INDEX_LEB: + case wasm::R_WASM_TABLE_NUMBER_LEB: // Provisional value is function/global/event Wasm index assert(WasmIndices.count(RelEntry.Symbol) > 0 && "symbol not found in wasm index space"); return WasmIndices[RelEntry.Symbol]; @@ -663,6 +664,7 @@ case wasm::R_WASM_GLOBAL_INDEX_LEB: case wasm::R_WASM_MEMORY_ADDR_LEB: case wasm::R_WASM_EVENT_INDEX_LEB: + case wasm::R_WASM_TABLE_NUMBER_LEB: writePatchableLEB<5>(Stream, Value, Offset); break; case wasm::R_WASM_MEMORY_ADDR_LEB64: diff --git a/llvm/lib/Object/RelocationResolver.cpp b/llvm/lib/Object/RelocationResolver.cpp --- a/llvm/lib/Object/RelocationResolver.cpp +++ b/llvm/lib/Object/RelocationResolver.cpp @@ -552,6 +552,7 @@ case wasm::R_WASM_SECTION_OFFSET_I32: case wasm::R_WASM_EVENT_INDEX_LEB: case wasm::R_WASM_GLOBAL_INDEX_I32: + case wasm::R_WASM_TABLE_NUMBER_LEB: return true; default: return false; @@ -585,6 +586,7 @@ case wasm::R_WASM_SECTION_OFFSET_I32: case wasm::R_WASM_EVENT_INDEX_LEB: case wasm::R_WASM_GLOBAL_INDEX_I32: + case wasm::R_WASM_TABLE_NUMBER_LEB: // For wasm section, its offset at 0 -- ignoring Value return A; default: diff --git a/llvm/lib/Object/WasmObjectFile.cpp b/llvm/lib/Object/WasmObjectFile.cpp --- a/llvm/lib/Object/WasmObjectFile.cpp +++ b/llvm/lib/Object/WasmObjectFile.cpp @@ -822,6 +822,11 @@ return make_error("Bad relocation function index", object_error::parse_failed); break; + case wasm::R_WASM_TABLE_NUMBER_LEB: + if (!isValidTableSymbol(Reloc.Index)) + return make_error("Bad relocation table index", + object_error::parse_failed); + break; case wasm::R_WASM_TYPE_INDEX_LEB: if (Reloc.Index >= Signatures.size()) return make_error("Bad relocation type index", @@ -1181,6 +1186,10 @@ return Index < Symbols.size() && Symbols[Index].isTypeFunction(); } +bool WasmObjectFile::isValidTableSymbol(uint32_t Index) const { + return Index < Symbols.size() && Symbols[Index].isTypeTable(); +} + bool WasmObjectFile::isValidGlobalSymbol(uint32_t Index) const { return Index < Symbols.size() && Symbols[Index].isTypeGlobal(); } diff --git a/llvm/lib/Target/WebAssembly/Disassembler/WebAssemblyDisassembler.cpp b/llvm/lib/Target/WebAssembly/Disassembler/WebAssemblyDisassembler.cpp --- a/llvm/lib/Target/WebAssembly/Disassembler/WebAssemblyDisassembler.cpp +++ b/llvm/lib/Target/WebAssembly/Disassembler/WebAssemblyDisassembler.cpp @@ -198,6 +198,7 @@ case WebAssembly::OPERAND_LOCAL: case WebAssembly::OPERAND_GLOBAL: case WebAssembly::OPERAND_FUNCTION32: + case WebAssembly::OPERAND_TABLE32: case WebAssembly::OPERAND_OFFSET32: case WebAssembly::OPERAND_OFFSET64: case WebAssembly::OPERAND_P2ALIGN: diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp --- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp @@ -151,6 +151,7 @@ PaddedSize = 10; break; case WebAssembly::OPERAND_FUNCTION32: + case WebAssembly::OPERAND_TABLE32: case WebAssembly::OPERAND_OFFSET32: case WebAssembly::OPERAND_SIGNATURE: case WebAssembly::OPERAND_TYPEINDEX: diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h --- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h @@ -76,6 +76,10 @@ OPERAND_EVENT, /// A list of branch targets for br_list. OPERAND_BRLIST, + /// 32-bit unsigned table indices. + OPERAND_TABLE32, + /// Reference type. + OPERAND_REFTYPE, }; } // end namespace WebAssembly diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp --- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp @@ -102,6 +102,8 @@ return wasm::R_WASM_FUNCTION_INDEX_LEB; if (SymA.isEvent()) return wasm::R_WASM_EVENT_INDEX_LEB; + if (SymA.isTable()) + return wasm::R_WASM_TABLE_NUMBER_LEB; return wasm::R_WASM_MEMORY_ADDR_LEB; case WebAssembly::fixup_uleb128_i64: assert(SymA.isData()); diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td --- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td +++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td @@ -163,6 +163,12 @@ let OperandType = "OPERAND_FUNCTION32" in def function32_op : Operand; +let OperandType = "OPERAND_TABLE32" in +def table32_op : Operand; + +let OperandType = "OPERAND_REFTYPE" in +def reftype_op : Operand; + let OperandType = "OPERAND_OFFSET32" in def offset32_op : Operand; @@ -365,3 +371,4 @@ include "WebAssemblyInstrSIMD.td" include "WebAssemblyInstrRef.td" include "WebAssemblyInstrBulkMemory.td" +include "WebAssemblyInstrTable.td" diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrTable.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrTable.td new file mode 100644 --- /dev/null +++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrTable.td @@ -0,0 +1,65 @@ +// WebAssemblyInstrTable.td - WebAssembly Table codegen support -*- tablegen -*- +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// WebAssembly Table operand code-gen constructs. +/// +//===----------------------------------------------------------------------===// + +// Instructions that handle tables + +defm TABLE_GET : I<(outs reftype_op:$res), (ins table32_op:$table), + (outs), (ins table32_op:$table), + [], + "table.get\t$res, $table", + "table.get\t$table", + 0x25>, + Requires<[HasReferenceTypes]>; + +defm TABLE_SET : I<(outs), (ins table32_op:$table, reftype_op:$val, i32imm_op:$i), + (outs), (ins table32_op:$table), + [], + "table.set\t$table, $val, $i", + "table.set\t$table", + 0x26>, + Requires<[HasReferenceTypes]>; + +defm TABLE_GROW : I<(outs i32imm_op:$sz), (ins table32_op:$table, i32imm_op:$n, reftype_op:$val), + (outs), (ins table32_op:$table), + [], + "table.grow\t$sz, $table, $n, $val", + "table.grow\t$table", + 0xfc0f>, + Requires<[HasReferenceTypes]>; + +defm TABLE_SIZE : I<(outs i32imm_op:$sz), (ins table32_op:$table), + (outs), (ins table32_op:$table), + [], + "table.size\t$sz, $table", + "table.size\t$table", + 0xfc10>, + Requires<[HasReferenceTypes]>; + +defm TABLE_FILL : I<(outs), (ins table32_op:$table, i32imm_op:$n, reftype_op:$val, i32imm_op:$i), + (outs), (ins table32_op:$table), + [], + "table.fill\t$table, $n, $val, $i", + "table.fill\t$table", + 0xfc11>, + Requires<[HasReferenceTypes]>; + + +defm TABLE_COPY : I<(outs), (ins table32_op:$table1, table32_op:$table2, i32imm_op:$n, i32imm_op:$s, i32imm_op:$d), + (outs), (ins table32_op:$table1, table32_op:$table2), + [], + "table.copy\t$table1, $table2, $n, $s, $d", + "table.copy\t$table1, $table2", + 0xfc0e>, + Requires<[HasReferenceTypes]>; + + diff --git a/llvm/test/MC/WebAssembly/tables.s b/llvm/test/MC/WebAssembly/tables.s --- a/llvm/test/MC/WebAssembly/tables.s +++ b/llvm/test/MC/WebAssembly/tables.s @@ -1,19 +1,123 @@ -# 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 +# RUN: llvm-mc -triple=wasm32-unknown-unknown -mattr=+reference-types < %s | FileCheck %s +# RUN: llvm-mc -triple=wasm32-unknown-unknown -mattr=+reference-types -filetype=obj < %s | obj2yaml | FileCheck -check-prefix=BIN %s -# Test creating several empty tables +# Creating several empty tables -.tabletype foo, externref + .tabletype foo, externref foo: bar: -.tabletype bar, funcref + .tabletype bar, funcref -# CHECK: .tabletype foo, externref +table1: + .tabletype table1, funcref +table2: + .tabletype table2, funcref + +# Table instructions + +copy_tables: + .functype copy_tables (i32, i32) -> () + local.get 0 + local.get 1 + table.size table1 + table.copy table1, table2 + end_function + +table_get: + .functype table_get (i32) -> (externref) + local.get 0 + table.get foo + end_function + +table_set: + .functype table_set (i32, externref) -> () + local.get 0 + local.get 1 + table.set foo + end_function + +table_grow: + .functype table_grow (i32) -> (i32) + i32.const 0 + table.get foo + local.get 0 + table.grow foo + local.get 0 + i32.add + end_function + +table_fill: + .functype table_fill (i32, i32) -> () + local.get 0 + i32.const 0 + table.get table1 + local.get 1 + table.fill table1 + end_function + + +# CHECK: .tabletype foo, externref # CHECK: foo: # CHECK: bar: -# CHECK-NEXT: .tabletype bar, funcref +# CHECK-NEXT: .tabletype bar, funcref + +# CHECK: table1: +# CHECK-NEXT: .tabletype table1, funcref + +# CHECK: table2: +# CHECK-NEXT: .tabletype table2, funcref + +# CHECK: copy_tables: +# CHECK-NEXT: .functype copy_tables (i32, i32) -> () +# CHECK-NEXT: local.get 0 +# CHECK-NEXT: local.get 1 +# CHECK-NEXT: table.size table1 +# CHECK-NEXT: table.copy table1, table2 +# CHECK-NEXT: end_function +# CHECK-NEXT:.Ltmp0: +# CHECK-NEXR: .size copy_tables, .Ltmp0-copy_tables + +# CHECK: table_get: +# CHECK-NEXT: .functype table_get (i32) -> (externref) +# CHECK-NEXT: local.get 0 +# CHECK-NEXT: table.get foo +# CHECK-NEXT: end_function +# CHECK-NEXT: .Ltmp1: +# CHECK-NEXT: .size table_get, .Ltmp1-table_get + +# CHECK: table_set: +# CHECK-NEXT: .functype table_set (i32, externref) -> () +# CHECK-NEXT: local.get 0 +# CHECK-NEXT: local.get 1 +# CHECK-NEXT: table.set foo +# CHECK-NEXT: end_function +# CHECK-NEXT: .Ltmp2: +# CHECK-NEXT: .size table_set, .Ltmp2-table_set + +# CHECK: table_grow: +# CHECK-NEXT: .functype table_grow (i32) -> (i32) +# CHECK-NEXT: i32.const 0 +# CHECK-NEXT: table.get foo +# CHECK-NEXT: local.get 0 +# CHECK-NEXT: table.grow foo +# CHECK-NEXT: local.get 0 +# CHECK-NEXT: i32.add +# CHECK-NEXT: end_function +# CHECK-NEXT: .Ltmp3: +# CHECK-NEXT: .size table_grow, .Ltmp3-table_grow + +# CHECK: table_fill: +# CHECK-NEXT: .functype table_fill (i32, i32) -> () +# CHECK-NEXT: local.get 0 +# CHECK-NEXT: i32.const 0 +# CHECK-NEXT: table.get table1 +# CHECK-NEXT: local.get 1 +# CHECK-NEXT: table.fill table1 +# CHECK-NEXT: end_function +# CHECK-NEXT: .Ltmp4: +# CHECK-NEXT: .size table_fill, .Ltmp4-table_fill # BIN: - Type: TABLE # BIN-NEXT: Tables: @@ -26,6 +130,52 @@ # BIN-NEXT: Limits: # BIN-NEXT: Initial: 0x00000000 +# BIN: - Type: CODE +# BIN-NEXT: Relocations: +# BIN-NEXT: - Type: R_WASM_TABLE_NUMBER_LEB +# BIN-NEXT: Index: 2 +# BIN-NEXT: Offset: 0x00000009 +# BIN-NEXT: - Type: R_WASM_TABLE_NUMBER_LEB +# BIN-NEXT: Index: 2 +# BIN-NEXT: Offset: 0x00000010 +# BIN-NEXT: - Type: R_WASM_TABLE_NUMBER_LEB +# BIN-NEXT: Index: 3 +# BIN-NEXT: Offset: 0x00000015 +# BIN-NEXT: - Type: R_WASM_TABLE_NUMBER_LEB +# BIN-NEXT: Index: 0 +# BIN-NEXT: Offset: 0x00000020 +# BIN-NEXT: - Type: R_WASM_TABLE_NUMBER_LEB +# BIN-NEXT: Index: 0 +# BIN-NEXT: Offset: 0x0000002D +# BIN-NEXT: - Type: R_WASM_TABLE_NUMBER_LEB +# BIN-NEXT: Index: 0 +# BIN-NEXT: Offset: 0x00000038 +# BIN-NEXT: - Type: R_WASM_TABLE_NUMBER_LEB +# BIN-NEXT: Index: 0 +# BIN-NEXT: Offset: 0x00000041 +# BIN-NEXT: - Type: R_WASM_TABLE_NUMBER_LEB +# BIN-NEXT: Index: 2 +# BIN-NEXT: Offset: 0x00000051 +# BIN-NEXT: - Type: R_WASM_TABLE_NUMBER_LEB +# BIN-NEXT: Index: 2 +# BIN-NEXT: Offset: 0x0000005A +# BIN-NEXT: Functions: +# BIN-NEXT: - Index: 0 +# BIN-NEXT: Locals: [] +# BIN-NEXT: Body: 20002001FC108380808000FC0E838080800084808080000B +# BIN-NEXT: - Index: 1 +# BIN-NEXT: Locals: [] +# BIN-NEXT: Body: 20002581808080000B +# BIN-NEXT: - Index: 2 +# BIN-NEXT: Locals: [] +# BIN-NEXT: Body: 200020012681808080000B +# BIN-NEXT: - Index: 3 +# BIN-NEXT: Locals: [] +# BIN-NEXT: Body: 41002581808080002000FC0F818080800020006A0B +# BIN-NEXT: - Index: 4 +# BIN-NEXT: Locals: [] +# BIN-NEXT: Body: 200041002583808080002001FC1183808080000B + # BIN: - Type: CUSTOM # BIN-NEXT: Name: linking # BIN-NEXT: Version: 2