[PATCH 18/35] tcg/wasm: Add bswap instructions

Kohei Tokunaga posted 35 patches 2 months, 3 weeks ago
Maintainers: "Alex Bennée" <alex.bennee@linaro.org>, "Philippe Mathieu-Daudé" <philmd@linaro.org>, Thomas Huth <thuth@redhat.com>, Paolo Bonzini <pbonzini@redhat.com>, Richard Henderson <richard.henderson@linaro.org>, "Marc-André Lureau" <marcandre.lureau@redhat.com>, "Daniel P. Berrangé" <berrange@redhat.com>, WANG Xuerui <git@xen0n.name>, Aurelien Jarno <aurelien@aurel32.net>, Huacai Chen <chenhuacai@kernel.org>, Jiaxun Yang <jiaxun.yang@flygoat.com>, Aleksandar Rikalo <arikalo@gmail.com>, Palmer Dabbelt <palmer@dabbelt.com>, Alistair Francis <Alistair.Francis@wdc.com>, Stefan Weil <sw@weilnetz.de>, Kohei Tokunaga <ktokunaga.mail@gmail.com>
There is a newer version of this series
[PATCH 18/35] tcg/wasm: Add bswap instructions
Posted by Kohei Tokunaga 2 months, 3 weeks ago
This commit introduces Wasm module local variables[1] assigned from the
index 0. These variables are used as temporary storage during calculations.

[1] https://webassembly.github.io/spec/core/binary/instructions.html#variable-instructions

Signed-off-by: Kohei Tokunaga <ktokunaga.mail@gmail.com>
---
 tcg/wasm/tcg-target.c.inc | 117 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 117 insertions(+)

diff --git a/tcg/wasm/tcg-target.c.inc b/tcg/wasm/tcg-target.c.inc
index afaea76d5c..7f4ec250ff 100644
--- a/tcg/wasm/tcg-target.c.inc
+++ b/tcg/wasm/tcg-target.c.inc
@@ -116,10 +116,16 @@ static const uint8_t tcg_target_reg_index[TCG_TARGET_NB_REGS] = {
 
 #define REG_IDX(r) tcg_target_reg_index[r]
 
+/* Temporary local variables */
+#define TMP32_LOCAL_0_IDX 0
+#define TMP64_LOCAL_0_IDX 1
+
 typedef enum {
     OPC_IF = 0x04,
     OPC_ELSE = 0x05,
     OPC_END = 0x0b,
+    OPC_LOCAL_GET = 0x20,
+    OPC_LOCAL_SET = 0x21,
     OPC_GLOBAL_GET = 0x23,
     OPC_GLOBAL_SET = 0x24,
 
@@ -163,9 +169,12 @@ typedef enum {
     OPC_I64_GE_U = 0x5a,
 
     OPC_I32_ADD = 0x6a,
+    OPC_I32_AND = 0x71,
+    OPC_I32_OR = 0x72,
     OPC_I32_SHL = 0x74,
     OPC_I32_SHR_S = 0x75,
     OPC_I32_SHR_U = 0x76,
+    OPC_I32_ROTR = 0x78,
 
     OPC_I64_ADD = 0x7c,
     OPC_I64_SUB = 0x7d,
@@ -176,6 +185,7 @@ typedef enum {
     OPC_I64_SHL = 0x86,
     OPC_I64_SHR_S = 0x87,
     OPC_I64_SHR_U = 0x88,
+    OPC_I64_ROTR = 0x8a,
 
     OPC_I32_WRAP_I64 = 0xa7,
     OPC_I64_EXTEND_I32_S = 0xac,
@@ -524,6 +534,110 @@ static void tcg_wasm_out_sextract(TCGContext *s, TCGReg dest, TCGReg arg1,
     tcg_wasm_out_op_idx(s, OPC_GLOBAL_SET, REG_IDX(dest));
 }
 
+static void tcg_wasm_out_bswap64(
+    TCGContext *s, TCGReg dest, TCGReg src)
+{
+    tcg_wasm_out_op_idx(s, OPC_GLOBAL_GET, REG_IDX(src));
+    tcg_wasm_out_op_const(s, OPC_I64_CONST, 32);
+    tcg_wasm_out_op(s, OPC_I64_ROTR);
+    tcg_wasm_out_op_idx(s, OPC_LOCAL_SET, TMP64_LOCAL_0_IDX);
+
+    tcg_wasm_out_op_idx(s, OPC_LOCAL_GET, TMP64_LOCAL_0_IDX);
+    tcg_wasm_out_op_const(s, OPC_I64_CONST, 0xff000000ff000000);
+    tcg_wasm_out_op(s, OPC_I64_AND);
+    tcg_wasm_out_op_const(s, OPC_I64_CONST, 24);
+    tcg_wasm_out_op(s, OPC_I64_SHR_U);
+
+    tcg_wasm_out_op_idx(s, OPC_LOCAL_GET, TMP64_LOCAL_0_IDX);
+    tcg_wasm_out_op_const(s, OPC_I64_CONST, 0x00ff000000ff0000);
+    tcg_wasm_out_op(s, OPC_I64_AND);
+    tcg_wasm_out_op_const(s, OPC_I64_CONST, 8);
+    tcg_wasm_out_op(s, OPC_I64_SHR_U);
+
+    tcg_wasm_out_op(s, OPC_I64_OR);
+
+    tcg_wasm_out_op_idx(s, OPC_LOCAL_GET, TMP64_LOCAL_0_IDX);
+    tcg_wasm_out_op_const(s, OPC_I64_CONST, 0x0000ff000000ff00);
+    tcg_wasm_out_op(s, OPC_I64_AND);
+    tcg_wasm_out_op_const(s, OPC_I64_CONST, 8);
+    tcg_wasm_out_op(s, OPC_I64_SHL);
+
+    tcg_wasm_out_op_idx(s, OPC_LOCAL_GET, TMP64_LOCAL_0_IDX);
+    tcg_wasm_out_op_const(s, OPC_I64_CONST, 0x000000ff000000ff);
+    tcg_wasm_out_op(s, OPC_I64_AND);
+    tcg_wasm_out_op_const(s, OPC_I64_CONST, 24);
+    tcg_wasm_out_op(s, OPC_I64_SHL);
+
+    tcg_wasm_out_op(s, OPC_I64_OR);
+
+    tcg_wasm_out_op(s, OPC_I64_OR);
+    tcg_wasm_out_op_idx(s, OPC_GLOBAL_SET, REG_IDX(dest));
+}
+
+static void tcg_wasm_out_bswap32(
+    TCGContext *s, TCGReg dest, TCGReg src, int flags)
+{
+    tcg_wasm_out_op_idx(s, OPC_GLOBAL_GET, REG_IDX(src));
+    tcg_wasm_out_op(s, OPC_I32_WRAP_I64);
+    tcg_wasm_out_op_idx(s, OPC_LOCAL_SET, TMP32_LOCAL_0_IDX);
+
+    tcg_wasm_out_op_idx(s, OPC_LOCAL_GET, TMP32_LOCAL_0_IDX);
+    tcg_wasm_out_op_const(s, OPC_I32_CONST, 16);
+    tcg_wasm_out_op(s, OPC_I32_ROTR);
+    tcg_wasm_out_op_idx(s, OPC_LOCAL_SET, TMP32_LOCAL_0_IDX);
+
+    tcg_wasm_out_op_idx(s, OPC_LOCAL_GET, TMP32_LOCAL_0_IDX);
+    tcg_wasm_out_op_const(s, OPC_I32_CONST, 0xff00ff00);
+    tcg_wasm_out_op(s, OPC_I32_AND);
+    tcg_wasm_out_op_const(s, OPC_I32_CONST, 8);
+    tcg_wasm_out_op(s, OPC_I32_SHR_U);
+
+    tcg_wasm_out_op_idx(s, OPC_LOCAL_GET, TMP32_LOCAL_0_IDX);
+    tcg_wasm_out_op_const(s, OPC_I32_CONST, 0x00ff00ff);
+    tcg_wasm_out_op(s, OPC_I32_AND);
+    tcg_wasm_out_op_const(s, OPC_I32_CONST, 8);
+    tcg_wasm_out_op(s, OPC_I32_SHL);
+
+    tcg_wasm_out_op(s, OPC_I32_OR);
+    if (flags & TCG_BSWAP_OS) {
+        tcg_wasm_out_op(s, OPC_I64_EXTEND_I32_S);
+    } else {
+        tcg_wasm_out_op(s, OPC_I64_EXTEND_I32_U);
+    }
+    tcg_wasm_out_op_idx(s, OPC_GLOBAL_SET, REG_IDX(dest));
+}
+
+static void tcg_wasm_out_bswap16(
+    TCGContext *s, TCGReg dest, TCGReg src, int flags)
+{
+    tcg_wasm_out_op_idx(s, OPC_GLOBAL_GET, REG_IDX(src));
+    tcg_wasm_out_op(s, OPC_I32_WRAP_I64);
+    tcg_wasm_out_op_idx(s, OPC_LOCAL_SET, TMP32_LOCAL_0_IDX);
+
+    tcg_wasm_out_op_idx(s, OPC_LOCAL_GET, TMP32_LOCAL_0_IDX);
+    tcg_wasm_out_op_const(s, OPC_I32_CONST, 8);
+    tcg_wasm_out_op(s, OPC_I32_ROTR);
+    tcg_wasm_out_op_idx(s, OPC_LOCAL_SET, TMP32_LOCAL_0_IDX);
+
+    tcg_wasm_out_op_idx(s, OPC_LOCAL_GET, TMP32_LOCAL_0_IDX);
+    tcg_wasm_out_op_const(s, OPC_I32_CONST, 0x000000ff);
+    tcg_wasm_out_op(s, OPC_I32_AND);
+
+    tcg_wasm_out_op_idx(s, OPC_LOCAL_GET, TMP32_LOCAL_0_IDX);
+    tcg_wasm_out_op_const(s, OPC_I32_CONST, 0xff000000);
+    tcg_wasm_out_op(s, OPC_I32_AND);
+    tcg_wasm_out_op_const(s, OPC_I32_CONST, 16);
+    if (flags & TCG_BSWAP_OS) {
+        tcg_wasm_out_op(s, OPC_I32_SHR_S);
+    } else {
+        tcg_wasm_out_op(s, OPC_I32_SHR_U);
+    }
+
+    tcg_wasm_out_op(s, OPC_I32_OR);
+    tcg_wasm_out_op(s, OPC_I64_EXTEND_I32_U);
+    tcg_wasm_out_op_idx(s, OPC_GLOBAL_SET, REG_IDX(dest));
+}
+
 static void tcg_wasm_out_mov(TCGContext *s, TCGReg ret, TCGReg arg)
 {
     tcg_wasm_out_op_idx(s, OPC_GLOBAL_GET, REG_IDX(arg));
@@ -1448,6 +1562,7 @@ static void tgen_bswap16(TCGContext *s, TCGType type,
     if (flags & TCG_BSWAP_OS) {
         tcg_tci_out_sextract(s, TCG_TYPE_REG, a0, a0, 0, 16);
     }
+    tcg_wasm_out_bswap16(s, a0, a1, flags);
 }
 
 static const TCGOutOpBswap outop_bswap16 = {
@@ -1462,6 +1577,7 @@ static void tgen_bswap32(TCGContext *s, TCGType type,
     if (flags & TCG_BSWAP_OS) {
         tcg_tci_out_sextract(s, TCG_TYPE_REG, a0, a0, 0, 32);
     }
+    tcg_wasm_out_bswap32(s, a0, a1, flags);
 }
 
 static const TCGOutOpBswap outop_bswap32 = {
@@ -1473,6 +1589,7 @@ static const TCGOutOpBswap outop_bswap32 = {
 static void tgen_bswap64(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1)
 {
     tcg_out_op_rr(s, INDEX_op_bswap64, a0, a1);
+    tcg_wasm_out_bswap64(s, a0, a1);
 }
 
 static const TCGOutOpUnary outop_bswap64 = {
-- 
2.43.0
Re: [PATCH 18/35] tcg/wasm: Add bswap instructions
Posted by Richard Henderson 2 months, 3 weeks ago
On 8/20/25 04:21, Kohei Tokunaga wrote:
> +static void tcg_wasm_out_bswap64(
> +    TCGContext *s, TCGReg dest, TCGReg src)
> +{
> +    tcg_wasm_out_op_idx(s, OPC_GLOBAL_GET, REG_IDX(src));
> +    tcg_wasm_out_op_const(s, OPC_I64_CONST, 32);
> +    tcg_wasm_out_op(s, OPC_I64_ROTR);
> +    tcg_wasm_out_op_idx(s, OPC_LOCAL_SET, TMP64_LOCAL_0_IDX);
> +
> +    tcg_wasm_out_op_idx(s, OPC_LOCAL_GET, TMP64_LOCAL_0_IDX);
> +    tcg_wasm_out_op_const(s, OPC_I64_CONST, 0xff000000ff000000);
> +    tcg_wasm_out_op(s, OPC_I64_AND);
> +    tcg_wasm_out_op_const(s, OPC_I64_CONST, 24);
> +    tcg_wasm_out_op(s, OPC_I64_SHR_U);
> +
> +    tcg_wasm_out_op_idx(s, OPC_LOCAL_GET, TMP64_LOCAL_0_IDX);
> +    tcg_wasm_out_op_const(s, OPC_I64_CONST, 0x00ff000000ff0000);
> +    tcg_wasm_out_op(s, OPC_I64_AND);
> +    tcg_wasm_out_op_const(s, OPC_I64_CONST, 8);
> +    tcg_wasm_out_op(s, OPC_I64_SHR_U);
> +
> +    tcg_wasm_out_op(s, OPC_I64_OR);
> +
> +    tcg_wasm_out_op_idx(s, OPC_LOCAL_GET, TMP64_LOCAL_0_IDX);
> +    tcg_wasm_out_op_const(s, OPC_I64_CONST, 0x0000ff000000ff00);
> +    tcg_wasm_out_op(s, OPC_I64_AND);
> +    tcg_wasm_out_op_const(s, OPC_I64_CONST, 8);
> +    tcg_wasm_out_op(s, OPC_I64_SHL);
> +
> +    tcg_wasm_out_op_idx(s, OPC_LOCAL_GET, TMP64_LOCAL_0_IDX);
> +    tcg_wasm_out_op_const(s, OPC_I64_CONST, 0x000000ff000000ff);
> +    tcg_wasm_out_op(s, OPC_I64_AND);
> +    tcg_wasm_out_op_const(s, OPC_I64_CONST, 24);
> +    tcg_wasm_out_op(s, OPC_I64_SHL);
> +
> +    tcg_wasm_out_op(s, OPC_I64_OR);
> +
> +    tcg_wasm_out_op(s, OPC_I64_OR);
> +    tcg_wasm_out_op_idx(s, OPC_GLOBAL_SET, REG_IDX(dest));
> +}

Is this any better than the default expansion?


r~
Re: [PATCH 18/35] tcg/wasm: Add bswap instructions
Posted by Kohei Tokunaga 2 months, 3 weeks ago
Hi Richard,

> On 8/20/25 04:21, Kohei Tokunaga wrote:
> > +static void tcg_wasm_out_bswap64(
> > +    TCGContext *s, TCGReg dest, TCGReg src)
> > +{
> > +    tcg_wasm_out_op_idx(s, OPC_GLOBAL_GET, REG_IDX(src));
> > +    tcg_wasm_out_op_const(s, OPC_I64_CONST, 32);
> > +    tcg_wasm_out_op(s, OPC_I64_ROTR);
> > +    tcg_wasm_out_op_idx(s, OPC_LOCAL_SET, TMP64_LOCAL_0_IDX);
> > +
> > +    tcg_wasm_out_op_idx(s, OPC_LOCAL_GET, TMP64_LOCAL_0_IDX);
> > +    tcg_wasm_out_op_const(s, OPC_I64_CONST, 0xff000000ff000000);
> > +    tcg_wasm_out_op(s, OPC_I64_AND);
> > +    tcg_wasm_out_op_const(s, OPC_I64_CONST, 24);
> > +    tcg_wasm_out_op(s, OPC_I64_SHR_U);
> > +
> > +    tcg_wasm_out_op_idx(s, OPC_LOCAL_GET, TMP64_LOCAL_0_IDX);
> > +    tcg_wasm_out_op_const(s, OPC_I64_CONST, 0x00ff000000ff0000);
> > +    tcg_wasm_out_op(s, OPC_I64_AND);
> > +    tcg_wasm_out_op_const(s, OPC_I64_CONST, 8);
> > +    tcg_wasm_out_op(s, OPC_I64_SHR_U);
> > +
> > +    tcg_wasm_out_op(s, OPC_I64_OR);
> > +
> > +    tcg_wasm_out_op_idx(s, OPC_LOCAL_GET, TMP64_LOCAL_0_IDX);
> > +    tcg_wasm_out_op_const(s, OPC_I64_CONST, 0x0000ff000000ff00);
> > +    tcg_wasm_out_op(s, OPC_I64_AND);
> > +    tcg_wasm_out_op_const(s, OPC_I64_CONST, 8);
> > +    tcg_wasm_out_op(s, OPC_I64_SHL);
> > +
> > +    tcg_wasm_out_op_idx(s, OPC_LOCAL_GET, TMP64_LOCAL_0_IDX);
> > +    tcg_wasm_out_op_const(s, OPC_I64_CONST, 0x000000ff000000ff);
> > +    tcg_wasm_out_op(s, OPC_I64_AND);
> > +    tcg_wasm_out_op_const(s, OPC_I64_CONST, 24);
> > +    tcg_wasm_out_op(s, OPC_I64_SHL);
> > +
> > +    tcg_wasm_out_op(s, OPC_I64_OR);
> > +
> > +    tcg_wasm_out_op(s, OPC_I64_OR);
> > +    tcg_wasm_out_op_idx(s, OPC_GLOBAL_SET, REG_IDX(dest));
> > +}
>
> Is this any better than the default expansion?

Thank you for pointing this out. I think it can rely on the the default
expansion, so I'll remove it in the next version of the patch series.

Regards,
Kohei