zylin / zpugcc

50 stars 31 forks source link

Example of adding UDIV/UMOD #16

Open svofski opened 3 years ago

svofski commented 3 years ago

I ran into issue similar to the one described in #8, unsigned division is very slow because it's not using DIV instruction. For me the easiest solution was to implement two new instructions: UDIV and UMOD. Here's a patch that enables them in zpugcc. By default they are disabled for compatibility. They can be enabled with -mumod -mudiv switches.

From 52bd35da458199cabdc308508d28946b9f4d8e87 Mon Sep 17 00:00:00 2001
From: svofski <svofski@gmail.com>
Date: Wed, 2 Dec 2020 22:41:57 +0300
Subject: [PATCH] support optional udiv and umod instructions

Opcodes 32 udiv, 33 umod. They are not standard and disabled by default.
Enable them with -mudiv -mumod.
---
 toolchain/binutils/include/opcode/zpu.h |  2 ++
 toolchain/binutils/opcodes/zpu-opc.c    |  2 ++
 toolchain/gcc/gcc/config/zpu/zpu.c      | 16 ++++++++++++++++
 toolchain/gcc/gcc/config/zpu/zpu.h      | 11 +++++++++--
 4 files changed, 29 insertions(+), 2 deletions(-)

diff --git a/toolchain/binutils/include/opcode/zpu.h b/toolchain/binutils/include/opcode/zpu.h
index ae740f3d..777421c2 100755
--- a/toolchain/binutils/include/opcode/zpu.h
+++ b/toolchain/binutils/include/opcode/zpu.h
@@ -80,6 +80,8 @@ enum ZPU_OPCODE
    ZPU_pushspadd=61,
    ZPU_halfmult=62,
    ZPU_callpcrel=63,
+        ZPU_udiv=32,
+        ZPU_umod=33,

    ZPU_impcrel=0,      /* not an opcode, translates to IM instructions */

diff --git a/toolchain/binutils/opcodes/zpu-opc.c b/toolchain/binutils/opcodes/zpu-opc.c
index cf784838..97dcfa40 100755
--- a/toolchain/binutils/opcodes/zpu-opc.c
+++ b/toolchain/binutils/opcodes/zpu-opc.c
@@ -58,6 +58,8 @@ const struct zpu_opcode zpu_opcodes[]={
    {ZPU_swap,              "swap",             ADDR_IMPLIED},
    {ZPU_div,               "div",              ADDR_IMPLIED},
    {ZPU_mod,               "mod",              ADDR_IMPLIED},
+   {ZPU_udiv,              "udiv",             ADDR_IMPLIED},
+   {ZPU_umod,              "umod",             ADDR_IMPLIED},
    {ZPU_eqbranch,          "eqbranch",         ADDR_IMPLIED},
    {ZPU_neqbranch,         "neqbranch",        ADDR_IMPLIED},

diff --git a/toolchain/gcc/gcc/config/zpu/zpu.c b/toolchain/gcc/gcc/config/zpu/zpu.c
index df6c787e..1fe8bbbb 100755
--- a/toolchain/gcc/gcc/config/zpu/zpu.c
+++ b/toolchain/gcc/gcc/config/zpu/zpu.c
@@ -551,6 +551,10 @@ int zpu_binary_operator (rtx op  ATTRIBUTE_UNUSED, enum machine_mode mode ATTRIB
          return TARGET_DIV;
        case MOD:
          return TARGET_MOD;
+                case UDIV:
+                  return TARGET_UDIV;
+                case UMOD:
+                  return TARGET_UMOD;

        default:
        break;
@@ -1692,12 +1696,24 @@ static void push_operand_value(rtx *operand)
        push_operand_value(&XEXP (*operand, 0));
        zpu_asm("div", operand);
        stackOffset+=4;
+        } else if (GET_CODE(operand[0]) == UDIV)
+        {
+       push_operand_value(&XEXP (*operand, 1));
+       push_operand_value(&XEXP (*operand, 0));
+       zpu_asm("udiv", operand);
+       stackOffset+=4;
    } else if (GET_CODE(operand[0])==MOD)
    {
        push_operand_value(&XEXP (*operand, 1));
        push_operand_value(&XEXP (*operand, 0));
        zpu_asm("mod", operand);
        stackOffset+=4;
+   } else if (GET_CODE(operand[0])==UMOD)
+   {
+       push_operand_value(&XEXP (*operand, 1));
+       push_operand_value(&XEXP (*operand, 0));
+       zpu_asm("umod", operand);
+       stackOffset+=4;
    } else if (GET_CODE(operand[0])==ZERO_EXTEND)
    {
        push_operand_value(&XEXP (*operand, 0));
diff --git a/toolchain/gcc/gcc/config/zpu/zpu.h b/toolchain/gcc/gcc/config/zpu/zpu.h
index 46483a08..7e7d5b95 100755
--- a/toolchain/gcc/gcc/config/zpu/zpu.h
+++ b/toolchain/gcc/gcc/config/zpu/zpu.h
@@ -92,11 +92,13 @@ extern int target_flags;
 #define ZPU_BITSBIG (1<<24)
 #define ZPU_MEMREG (1<<25)

+#define ZPU_UDIV (1<<26)
+#define ZPU_UMOD (1<<27)

-#define TARGET_SWITCHES_DEFAULT (0x7fffffff&~ZPU_BITSBIG)
+#define TARGET_SWITCHES_DEFAULT (0x7fffffff&~(ZPU_BITSBIG|ZPU_UDIV|ZPU_UMOD))

@@ -123,7 +125,8 @@ extern int target_flags;
 #define TARGET_BYTESBIG ((target_flags & ZPU_BYTESBIG)!=0)
 #define TARGET_BITSBIG ((target_flags & ZPU_BITSBIG)!=0)
 #define TARGET_MEMREG ((target_flags & ZPU_MEMREG)!=0)
-
+#define TARGET_UDIV ((target_flags & ZPU_UDIV)!=0)
+#define TARGET_UMOD ((target_flags & ZPU_UMOD)!=0)

 #define TARGET_SWITCHES \
 { \
@@ -133,6 +136,10 @@ extern int target_flags;
     { "no-div", -ZPU_DIV, "DIV instruction" },\
     { "mod", ZPU_MOD, "MOD instruction" },\
     { "no-mod", -ZPU_MOD, "MOD instruction" },\
+    { "udiv", ZPU_UDIV, "UDIV instruction" },\
+    { "no-udiv", -ZPU_UDIV, "UDIV instruction" },\
+    { "umod", ZPU_UMOD, "UMOD instruction" },\
+    { "no-umod", -ZPU_UMOD, "UMOD instruction" },\
     { "neg", ZPU_NEG, "NEG instruction" },\
     { "no-neg", -ZPU_NEG, "NEG instruction" },\
     { "loadsp", ZPU_LOADSP, "LOADSP instruction" },\
-- 
2.25.1