This commit adds support for generateing the and, or and xor operations. The generated Wasm codes will be instantiated and executed in the browser.
Browsers tipycally limit the number of active Wasm instances and the instantiating Wasm modules introduces overhead. As a result, instantiating TBs that are rarely called is undesirable. To address this, the Wasm backend relies on the a forked subset of the TCI interpreter (tcg_qemu_tb_exec_tci function in tcg/wasm.c) for executing such TBs. The Wasm backend emits both Wasm and TCI instructions. TCI instructions are emitted to s->code_ptr, while the corresponding Wasm instructions are generated into a separate buffer allocated via tcg_malloc(). This buffer intends to be merged into the TB before tcg_gen_code returns. In the Wasm code, each TCG variable is mapped to a 64bit Wasm variable. Execution works by first pushing the operands into the Wasm's stack using get instructions. The result is left on the stack and this can be assigned to a variable by popping it using a set instruction. The Wasm binary format is documented at [1]. Additionally, since the Wasm instuction's index operand must be LEB128-encoded, this commit introduces an encoder function implemented following [2]. [1] https://webassembly.github.io/spec/core/binary/index.html [2] https://en.wikipedia.org/wiki/LEB128 Signed-off-by: Kohei Tokunaga <ktokunaga.m...@gmail.com> --- MAINTAINERS | 1 + tcg/wasm.c | 66 ++++++++++++++++ tcg/wasm/tcg-target.c.inc | 160 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 227 insertions(+) create mode 100644 tcg/wasm.c V2: - This commit generates both Wasm and TCI instrucitons. - Although checkpatch.pl reports the following error in tcg/wasm.c, this file is based on the TCI code so it is preserved as-is. > New file 'tcg/wasm.c' must not have license boilerplate header text, > only the SPDX-License-Identifier, unless this file was copied from > existing code already having such text. diff --git a/MAINTAINERS b/MAINTAINERS index 89e4b51e22..217bf2066c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4003,6 +4003,7 @@ WebAssembly TCG target M: Kohei Tokunaga <ktokunaga.m...@gmail.com> S: Maintained F: tcg/wasm/ +F: tcg/wasm.c Block drivers ------------- diff --git a/tcg/wasm.c b/tcg/wasm.c new file mode 100644 index 0000000000..9f3b1344d6 --- /dev/null +++ b/tcg/wasm.c @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * WebAssembly backend with forked TCI, based on tci.c + * + * Copyright (c) 2009, 2011, 2016 Stefan Weil + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "tcg/tcg.h" + +static void tci_args_rrr(uint32_t insn, TCGReg *r0, TCGReg *r1, TCGReg *r2) +{ + *r0 = extract32(insn, 8, 4); + *r1 = extract32(insn, 12, 4); + *r2 = extract32(insn, 16, 4); +} + +static uintptr_t tcg_qemu_tb_exec_tci(CPUArchState *env, const void *v_tb_ptr) +{ + const uint32_t *tb_ptr = v_tb_ptr; + tcg_target_ulong regs[TCG_TARGET_NB_REGS]; + uint64_t stack[(TCG_STATIC_CALL_ARGS_SIZE + TCG_STATIC_FRAME_SIZE) + / sizeof(uint64_t)]; + + regs[TCG_AREG0] = (tcg_target_ulong)env; + regs[TCG_REG_CALL_STACK] = (uintptr_t)stack; + + for (;;) { + uint32_t insn; + TCGOpcode opc; + TCGReg r0, r1, r2; + + insn = *tb_ptr++; + opc = extract32(insn, 0, 8); + + switch (opc) { + case INDEX_op_and: + tci_args_rrr(insn, &r0, &r1, &r2); + regs[r0] = regs[r1] & regs[r2]; + break; + case INDEX_op_or: + tci_args_rrr(insn, &r0, &r1, &r2); + regs[r0] = regs[r1] | regs[r2]; + break; + case INDEX_op_xor: + tci_args_rrr(insn, &r0, &r1, &r2); + regs[r0] = regs[r1] ^ regs[r2]; + break; + default: + g_assert_not_reached(); + } + } +} diff --git a/tcg/wasm/tcg-target.c.inc b/tcg/wasm/tcg-target.c.inc index 4bcb594360..a1757b4db7 100644 --- a/tcg/wasm/tcg-target.c.inc +++ b/tcg/wasm/tcg-target.c.inc @@ -25,6 +25,10 @@ * THE SOFTWARE. */ +#include "qemu/queue.h" + +typedef uint32_t tcg_insn_unit_tci; + static const int tcg_target_reg_alloc_order[] = { TCG_REG_R2, TCG_REG_R3, @@ -109,3 +113,159 @@ static bool patch_reloc(tcg_insn_unit *code_ptr, int type, } return false; } + +/* converts a TCG register to a wasm variable index */ +static const uint8_t tcg_target_reg_index[TCG_TARGET_NB_REGS] = { + 0, /* TCG_REG_R0 */ + 1, /* TCG_REG_R1 */ + 2, /* TCG_REG_R2 */ + 3, /* TCG_REG_R3 */ + 4, /* TCG_REG_R4 */ + 5, /* TCG_REG_R5 */ + 6, /* TCG_REG_R6 */ + 7, /* TCG_REG_R7 */ + 8, /* TCG_REG_R8 */ + 9, /* TCG_REG_R9 */ + 10, /* TCG_REG_R10 */ + 11, /* TCG_REG_R11 */ + 12, /* TCG_REG_R12 */ + 13, /* TCG_REG_R13 */ + 14, /* TCG_REG_R14 */ + 15, /* TCG_REG_R15 */ +}; + +#define REG_IDX(r) tcg_target_reg_index[r] + +typedef enum { + OPC_GLOBAL_GET = 0x23, + OPC_GLOBAL_SET = 0x24, + + OPC_I64_AND = 0x83, + OPC_I64_OR = 0x84, + OPC_I64_XOR = 0x85, +} WasmInsn; + +#define BUF_SIZE 1024 +typedef struct LinkedBufEntry { + uint8_t data[BUF_SIZE]; + uint32_t size; + QSIMPLEQ_ENTRY(LinkedBufEntry) entry; +} LinkedBufEntry; + +typedef QSIMPLEQ_HEAD(, LinkedBufEntry) LinkedBuf; + +static void linked_buf_out8(LinkedBuf *linked_buf, uint8_t v) +{ + LinkedBufEntry *buf = QSIMPLEQ_LAST(linked_buf, LinkedBufEntry, entry); + if (!buf || (buf->size == BUF_SIZE)) { + LinkedBufEntry *e = tcg_malloc(sizeof(LinkedBufEntry)); + e->size = 0; + QSIMPLEQ_INSERT_TAIL(linked_buf, e, entry); + buf = e; + } + buf->data[buf->size++] = v; +} + +static void linked_buf_out_leb128(LinkedBuf *p, uint64_t v) +{ + uint8_t b; + do { + b = v & 0x7f; + v >>= 7; + if (v != 0) { + b |= 0x80; + } + linked_buf_out8(p, b); + } while (v != 0); +} + +/* + * wasm code is generataed in the dynamically allocated buffer which + * are managed as a linked list. + */ +static __thread LinkedBuf sub_buf; + +static void init_sub_buf(void) +{ + QSIMPLEQ_INIT(&sub_buf); +} +static void tcg_wasm_out8(TCGContext *s, uint8_t v) +{ + linked_buf_out8(&sub_buf, v); +} +static void tcg_wasm_out_leb128(TCGContext *s, uint64_t v) +{ + linked_buf_out_leb128(&sub_buf, v); +} + +static void tcg_wasm_out_op(TCGContext *s, WasmInsn opc) +{ + tcg_wasm_out8(s, opc); +} +static void tcg_wasm_out_op_idx(TCGContext *s, WasmInsn opc, uint32_t idx) +{ + tcg_wasm_out8(s, opc); + tcg_wasm_out_leb128(s, idx); +} + +static void tcg_wasm_out_o1_i2( + TCGContext *s, WasmInsn opc, TCGReg ret, TCGReg arg1, TCGReg arg2) +{ + tcg_wasm_out_op_idx(s, OPC_GLOBAL_GET, REG_IDX(arg1)); + tcg_wasm_out_op_idx(s, OPC_GLOBAL_GET, REG_IDX(arg2)); + tcg_wasm_out_op(s, opc); + tcg_wasm_out_op_idx(s, OPC_GLOBAL_SET, REG_IDX(ret)); +} + +static void tcg_out_op_rrr(TCGContext *s, TCGOpcode op, + TCGReg r0, TCGReg r1, TCGReg r2) +{ + tcg_insn_unit_tci insn = 0; + + insn = deposit32(insn, 0, 8, op); + insn = deposit32(insn, 8, 4, r0); + insn = deposit32(insn, 12, 4, r1); + insn = deposit32(insn, 16, 4, r2); + tcg_out32(s, insn); +} + +static void tgen_and(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_op_rrr(s, INDEX_op_and, a0, a1, a2); + tcg_wasm_out_o1_i2(s, OPC_I64_AND, a0, a1, a2); +} + +static const TCGOutOpBinary outop_and = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_and, +}; + +static void tgen_or(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_op_rrr(s, INDEX_op_or, a0, a1, a2); + tcg_wasm_out_o1_i2(s, OPC_I64_OR, a0, a1, a2); +} + +static const TCGOutOpBinary outop_or = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_or, +}; + +static void tgen_xor(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_op_rrr(s, INDEX_op_xor, a0, a1, a2); + tcg_wasm_out_o1_i2(s, OPC_I64_XOR, a0, a1, a2); +} + +static const TCGOutOpBinary outop_xor = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_xor, +}; + +static void tcg_out_tb_start(TCGContext *s) +{ + init_sub_buf(); +} -- 2.43.0