From 09851d3e147189b202631a5b492203a9356fe065 Mon Sep 17 00:00:00 2001 From: TexasOct Date: Mon, 8 Apr 2024 23:48:56 +0800 Subject: [PATCH] add riscv jit support backport --- add-riscv-jit-backport.patch | 3511 ++++++++++++++++++++++++++++++++++ pcre.spec | 19 +- 2 files changed, 3526 insertions(+), 4 deletions(-) create mode 100644 add-riscv-jit-backport.patch diff --git a/add-riscv-jit-backport.patch b/add-riscv-jit-backport.patch new file mode 100644 index 0000000..b720e04 --- /dev/null +++ b/add-riscv-jit-backport.patch @@ -0,0 +1,3511 @@ +diff --git a/pcre_jit_compile.c b/pcre_jit_compile.c +index 4dcf8fc..129a554 100644 +--- a/pcre_jit_compile.c ++++ b/pcre_jit_compile.c +@@ -567,6 +567,10 @@ the start pointers when the end of the capturing group has not yet reached. */ + sljit_emit_op_flags(compiler, (op), (dst), (dstw), (type)) + #define GET_LOCAL_BASE(dst, dstw, offset) \ + sljit_get_local_base(compiler, (dst), (dstw), (offset)) ++#ifndef SLJIT_EMIT_MEM_UPDATE ++#define SLJIT_EMIT_MEM_UPDATE(compiler, type, reg, mem, memw) \ ++ sljit_emit_mem(compiler, type, reg, mem, memw) ++#endif + + #define READ_CHAR_MAX 0x7fffffff + +@@ -2394,12 +2398,12 @@ if (length < 8) + } + else + { +- if (sljit_emit_mem(compiler, SLJIT_MOV | SLJIT_MEM_SUPP | SLJIT_MEM_STORE | SLJIT_MEM_PRE, SLJIT_R0, SLJIT_MEM1(SLJIT_R1), sizeof(sljit_sw)) == SLJIT_SUCCESS) ++ if (SLJIT_EMIT_MEM_UPDATE(compiler, SLJIT_MOV | SLJIT_MEM_SUPP | SLJIT_MEM_STORE | SLJIT_MEM_PRE, SLJIT_R0, SLJIT_MEM1(SLJIT_R1), sizeof(sljit_sw)) == SLJIT_SUCCESS) + { + GET_LOCAL_BASE(SLJIT_R1, 0, OVECTOR_START); + OP1(SLJIT_MOV, SLJIT_R2, 0, SLJIT_IMM, length - 1); + loop = LABEL(); +- sljit_emit_mem(compiler, SLJIT_MOV | SLJIT_MEM_STORE | SLJIT_MEM_PRE, SLJIT_R0, SLJIT_MEM1(SLJIT_R1), sizeof(sljit_sw)); ++ SLJIT_EMIT_MEM_UPDATE(compiler, SLJIT_MOV | SLJIT_MEM_STORE | SLJIT_MEM_PRE, SLJIT_R0, SLJIT_MEM1(SLJIT_R1), sizeof(sljit_sw)); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_R2, 0, SLJIT_R2, 0, SLJIT_IMM, 1); + JUMPTO(SLJIT_NOT_ZERO, loop); + } +@@ -2445,12 +2449,12 @@ if (length < 8) + } + else + { +- if (sljit_emit_mem(compiler, SLJIT_MOV | SLJIT_MEM_SUPP | SLJIT_MEM_STORE | SLJIT_MEM_PRE, TMP1, SLJIT_MEM1(TMP2), sizeof(sljit_sw)) == SLJIT_SUCCESS) ++ if (SLJIT_EMIT_MEM_UPDATE(compiler, SLJIT_MOV | SLJIT_MEM_SUPP | SLJIT_MEM_STORE | SLJIT_MEM_PRE, TMP1, SLJIT_MEM1(TMP2), sizeof(sljit_sw)) == SLJIT_SUCCESS) + { + GET_LOCAL_BASE(TMP2, 0, OVECTOR_START + sizeof(sljit_sw)); + OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_IMM, length - 2); + loop = LABEL(); +- sljit_emit_mem(compiler, SLJIT_MOV | SLJIT_MEM_STORE | SLJIT_MEM_PRE, TMP1, SLJIT_MEM1(TMP2), sizeof(sljit_sw)); ++ SLJIT_EMIT_MEM_UPDATE(compiler, SLJIT_MOV | SLJIT_MEM_STORE | SLJIT_MEM_PRE, TMP1, SLJIT_MEM1(TMP2), sizeof(sljit_sw)); + OP2(SLJIT_SUB | SLJIT_SET_Z, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, 1); + JUMPTO(SLJIT_NOT_ZERO, loop); + } +@@ -2520,21 +2524,23 @@ if (common->mark_ptr != 0) + OP2(SLJIT_SUB, SLJIT_R2, 0, SLJIT_MEM1(SLJIT_R0), SLJIT_OFFSETOF(jit_arguments, offsets), SLJIT_IMM, sizeof(int)); + OP1(SLJIT_MOV, SLJIT_R0, 0, SLJIT_MEM1(SLJIT_R0), SLJIT_OFFSETOF(jit_arguments, begin)); + +-has_pre = sljit_emit_mem(compiler, SLJIT_MOV | SLJIT_MEM_SUPP | SLJIT_MEM_PRE, SLJIT_S1, SLJIT_MEM1(SLJIT_S0), sizeof(sljit_sw)) == SLJIT_SUCCESS; ++has_pre = SLJIT_EMIT_MEM_UPDATE(compiler, SLJIT_MOV | SLJIT_MEM_SUPP | SLJIT_MEM_PRE, SLJIT_S1, SLJIT_MEM1(SLJIT_S0), sizeof(sljit_sw)) == SLJIT_SUCCESS; + GET_LOCAL_BASE(SLJIT_S0, 0, OVECTOR_START - (has_pre ? sizeof(sljit_sw) : 0)); + + /* Unlikely, but possible */ + early_quit = CMP(SLJIT_EQUAL, SLJIT_R1, 0, SLJIT_IMM, 0); + loop = LABEL(); + ++ + if (has_pre) +- sljit_emit_mem(compiler, SLJIT_MOV | SLJIT_MEM_PRE, SLJIT_S1, SLJIT_MEM1(SLJIT_S0), sizeof(sljit_sw)); ++ SLJIT_EMIT_MEM_UPDATE(compiler, SLJIT_MOV | SLJIT_MEM_PRE, SLJIT_S1, SLJIT_MEM1(SLJIT_S0), sizeof(sljit_sw)); + else + { + OP1(SLJIT_MOV, SLJIT_S1, 0, SLJIT_MEM1(SLJIT_S0), 0); + OP2(SLJIT_ADD, SLJIT_S0, 0, SLJIT_S0, 0, SLJIT_IMM, sizeof(sljit_sw)); + } + ++ + OP2(SLJIT_ADD, SLJIT_R2, 0, SLJIT_R2, 0, SLJIT_IMM, sizeof(int)); + OP2(SLJIT_SUB, SLJIT_S1, 0, SLJIT_S1, 0, SLJIT_R0, 0); + /* Copy the integer value to the output buffer */ +@@ -2550,14 +2556,14 @@ JUMPHERE(early_quit); + /* Calculate the return value, which is the maximum ovector value. */ + if (topbracket > 1) + { +- if (sljit_emit_mem(compiler, SLJIT_MOV | SLJIT_MEM_SUPP | SLJIT_MEM_PRE, SLJIT_R2, SLJIT_MEM1(SLJIT_R0), -(2 * (sljit_sw)sizeof(sljit_sw))) == SLJIT_SUCCESS) ++ if (SLJIT_EMIT_MEM_UPDATE(compiler, SLJIT_MOV | SLJIT_MEM_SUPP | SLJIT_MEM_PRE, SLJIT_R2, SLJIT_MEM1(SLJIT_R0), -(2 * (sljit_sw)sizeof(sljit_sw))) == SLJIT_SUCCESS) + { + GET_LOCAL_BASE(SLJIT_R0, 0, OVECTOR_START + topbracket * 2 * sizeof(sljit_sw)); + OP1(SLJIT_MOV, SLJIT_R1, 0, SLJIT_IMM, topbracket + 1); + + /* OVECTOR(0) is never equal to SLJIT_S2. */ + loop = LABEL(); +- sljit_emit_mem(compiler, SLJIT_MOV | SLJIT_MEM_PRE, SLJIT_R2, SLJIT_MEM1(SLJIT_R0), -(2 * (sljit_sw)sizeof(sljit_sw))); ++ SLJIT_EMIT_MEM_UPDATE(compiler, SLJIT_MOV | SLJIT_MEM_PRE, SLJIT_R2, SLJIT_MEM1(SLJIT_R0), -(2 * (sljit_sw)sizeof(sljit_sw))); + OP2(SLJIT_SUB, SLJIT_R1, 0, SLJIT_R1, 0, SLJIT_IMM, 1); + CMPTO(SLJIT_EQUAL, SLJIT_R2, 0, SLJIT_S2, 0, loop); + } +@@ -5261,11 +5267,11 @@ if (char1_reg == STR_END) + OP1(SLJIT_MOV, RETURN_ADDR, 0, char2_reg, 0); + } + +-if (sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_SUPP | SLJIT_MEM_POST, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)) == SLJIT_SUCCESS) ++if (SLJIT_EMIT_MEM_UPDATE(compiler, MOV_UCHAR | SLJIT_MEM_SUPP | SLJIT_MEM_POST, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)) == SLJIT_SUCCESS) + { + label = LABEL(); +- sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_POST, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)); +- sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_POST, char2_reg, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); ++ SLJIT_EMIT_MEM_UPDATE(compiler, MOV_UCHAR | SLJIT_MEM_POST, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)); ++ SLJIT_EMIT_MEM_UPDATE(compiler, MOV_UCHAR | SLJIT_MEM_POST, char2_reg, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); + jump = CMP(SLJIT_NOT_EQUAL, char1_reg, 0, char2_reg, 0); + OP2(SLJIT_SUB | SLJIT_SET_Z, TMP2, 0, TMP2, 0, SLJIT_IMM, IN_UCHARS(1)); + JUMPTO(SLJIT_NOT_ZERO, label); +@@ -5273,14 +5279,14 @@ if (sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_SUPP | SLJIT_MEM_POST, char1_ + JUMPHERE(jump); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0); + } +-else if (sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_SUPP | SLJIT_MEM_PRE, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)) == SLJIT_SUCCESS) ++else if (SLJIT_EMIT_MEM_UPDATE(compiler, MOV_UCHAR | SLJIT_MEM_SUPP | SLJIT_MEM_PRE, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)) == SLJIT_SUCCESS) + { + OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(1)); + OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); + + label = LABEL(); +- sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_PRE, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)); +- sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_PRE, char2_reg, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); ++ SLJIT_EMIT_MEM_UPDATE(compiler, MOV_UCHAR | SLJIT_MEM_PRE, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)); ++ SLJIT_EMIT_MEM_UPDATE(compiler, MOV_UCHAR | SLJIT_MEM_PRE, char2_reg, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); + jump = CMP(SLJIT_NOT_EQUAL, char1_reg, 0, char2_reg, 0); + OP2(SLJIT_SUB | SLJIT_SET_Z, TMP2, 0, TMP2, 0, SLJIT_IMM, IN_UCHARS(1)); + JUMPTO(SLJIT_NOT_ZERO, label); +@@ -5334,9 +5340,9 @@ else + lcc_table = TMP3; + } + +-if (sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_SUPP | SLJIT_MEM_POST, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)) == SLJIT_SUCCESS) ++if (SLJIT_EMIT_MEM_UPDATE(compiler, MOV_UCHAR | SLJIT_MEM_SUPP | SLJIT_MEM_POST, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)) == SLJIT_SUCCESS) + opt_type = 1; +-else if (sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_SUPP | SLJIT_MEM_PRE, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)) == SLJIT_SUCCESS) ++else if (SLJIT_EMIT_MEM_UPDATE(compiler, MOV_UCHAR | SLJIT_MEM_SUPP | SLJIT_MEM_PRE, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)) == SLJIT_SUCCESS) + opt_type = 2; + + sljit_emit_fast_enter(compiler, SLJIT_MEM1(SLJIT_SP), LOCALS0); +@@ -5355,8 +5361,8 @@ OP1(SLJIT_MOV, lcc_table, 0, SLJIT_IMM, common->lcc); + if (opt_type == 1) + { + label = LABEL(); +- sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_POST, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)); +- sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_POST, char2_reg, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); ++ SLJIT_EMIT_MEM_UPDATE(compiler, MOV_UCHAR | SLJIT_MEM_POST, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)); ++ SLJIT_EMIT_MEM_UPDATE(compiler, MOV_UCHAR | SLJIT_MEM_POST, char2_reg, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); + } + else if (opt_type == 2) + { +@@ -5364,8 +5370,8 @@ else if (opt_type == 2) + OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); + + label = LABEL(); +- sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_PRE, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)); +- sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_PRE, char2_reg, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); ++ SLJIT_EMIT_MEM_UPDATE(compiler, MOV_UCHAR | SLJIT_MEM_PRE, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)); ++ SLJIT_EMIT_MEM_UPDATE(compiler, MOV_UCHAR | SLJIT_MEM_PRE, char2_reg, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); + } + else + { +diff --git a/sljit/sljitConfig.h b/sljit/sljitConfig.h +index d54b5e6..380b3c9 100644 +--- a/sljit/sljitConfig.h ++++ b/sljit/sljitConfig.h +@@ -49,6 +49,8 @@ + /* #define SLJIT_CONFIG_PPC_64 1 */ + /* #define SLJIT_CONFIG_MIPS_32 1 */ + /* #define SLJIT_CONFIG_MIPS_64 1 */ ++/* #define SLJIT_CONFIG_RISCV_32 1 */ ++/* #define SLJIT_CONFIG_RISCV_64 1 */ + /* #define SLJIT_CONFIG_SPARC_32 1 */ + /* #define SLJIT_CONFIG_TILEGX 1 */ + +@@ -120,7 +122,7 @@ + /* Return with error when an invalid argument is passed. */ + #ifndef SLJIT_ARGUMENT_CHECKS + /* Disabled by default */ +-#define SLJIT_ARGUMENT_CHECKS 0 ++#define SLJIT_ARGUMENT_CHECKS 1 + #endif + + /* Debug checks (assertions, etc.). */ +diff --git a/sljit/sljitConfigInternal.h b/sljit/sljitConfigInternal.h +index acba9da..3e233a4 100644 +--- a/sljit/sljitConfigInternal.h ++++ b/sljit/sljitConfigInternal.h +@@ -85,8 +85,10 @@ + || (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) \ + || (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64) \ + || (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32) \ +- || (defined SLJIT_CONFIG_TILEGX && SLJIT_CONFIG_TILEGX) \ +- || (defined SLJIT_CONFIG_AUTO && SLJIT_CONFIG_AUTO) \ ++ || (defined SLJIT_CONFIG_TILEGX && SLJIT_CONFIG_TILEGX) \ ++ || (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) \ ++ || (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64) \ ++ || (defined SLJIT_CONFIG_AUTO && SLJIT_CONFIG_AUTO) \ + || (defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED)) + #error "An architecture must be selected" + #endif +@@ -103,7 +105,9 @@ + + (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) \ + + (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64) \ + + (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32) \ +- + (defined SLJIT_CONFIG_AUTO && SLJIT_CONFIG_AUTO) \ ++ + (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) \ ++ + (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64) \ ++ + (defined SLJIT_CONFIG_AUTO && SLJIT_CONFIG_AUTO) \ + + (defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED) >= 2 + #error "Multiple architectures are selected" + #endif +@@ -138,6 +142,10 @@ + #define SLJIT_CONFIG_MIPS_32 1 + #elif defined(__mips64) + #define SLJIT_CONFIG_MIPS_64 1 ++#elif defined (__riscv_xlen) && (__riscv_xlen == 32) ++#define SLJIT_CONFIG_RISCV_32 1 ++#elif defined (__riscv_xlen) && (__riscv_xlen == 64) ++#define SLJIT_CONFIG_RISCV_64 1 + #elif defined(__sparc__) || defined(__sparc) + #define SLJIT_CONFIG_SPARC_32 1 + #elif defined(__tilegx__) +@@ -187,6 +195,8 @@ + #define SLJIT_CONFIG_PPC 1 + #elif (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) || (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64) + #define SLJIT_CONFIG_MIPS 1 ++#elif (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) || (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64) ++#define SLJIT_CONFIG_RISCV 1 + #elif (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32) || (defined SLJIT_CONFIG_SPARC_64 && SLJIT_CONFIG_SPARC_64) + #define SLJIT_CONFIG_SPARC 1 + #endif +@@ -377,6 +387,7 @@ typedef long int sljit_sw; + && !(defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64) \ + && !(defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) \ + && !(defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64) \ ++ && !(defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64) \ + && !(defined SLJIT_CONFIG_TILEGX && SLJIT_CONFIG_TILEGX) + #define SLJIT_32BIT_ARCHITECTURE 1 + #define SLJIT_WORD_SHIFT 2 +@@ -636,6 +647,21 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_sw sljit_exec_offset(void* ptr); + #define SLJIT_LOCALS_OFFSET_BASE 0 + #endif + ++#elif (defined SLJIT_CONFIG_RISCV && SLJIT_CONFIG_RISCV) ++ ++#define SLJIT_NUMBER_OF_REGISTERS 23 ++#define SLJIT_NUMBER_OF_SAVED_REGISTERS 12 ++#define SLJIT_NUMBER_OF_TEMPORARY_REGISTERS 5 ++#define SLJIT_NUMBER_OF_FLOAT_REGISTERS 30 ++#define SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS 12 ++#define SLJIT_NUMBER_OF_TEMPORARY_FLOAT_REGISTERS 2 ++#define SLJIT_TMP_DEST_REG SLJIT_TMP_R1 ++#define SLJIT_TMP_MEM_REG SLJIT_TMP_R1 ++#define SLJIT_TMP_DEST_FREG SLJIT_TMP_FR0 ++#define SLJIT_LOCALS_OFFSET_BASE 0 ++#define SLJIT_MASKED_SHIFT 1 ++#define SLJIT_MASKED_SHIFT32 1 ++ + #elif (defined SLJIT_CONFIG_SPARC && SLJIT_CONFIG_SPARC) + + #define SLJIT_NUMBER_OF_REGISTERS 18 +diff --git a/sljit/sljitLir.c b/sljit/sljitLir.c +index 9bab0c3..032303c 100644 +--- a/sljit/sljitLir.c ++++ b/sljit/sljitLir.c +@@ -91,6 +91,8 @@ + + #if !(defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED) + ++#define SSIZE_OF(type) ((sljit_s32)sizeof(sljit_ ## type)) ++ + #define VARIABLE_FLAG_SHIFT (10) + #define VARIABLE_FLAG_MASK (0x3f << VARIABLE_FLAG_SHIFT) + #define GET_FLAG_TYPE(op) ((op) >> VARIABLE_FLAG_SHIFT) +@@ -130,6 +132,18 @@ + /* When reg can be unused. */ + #define SLOW_IS_REG(reg) ((reg) > 0 && (reg) <= REG_MASK) + ++/* Mask for argument types. */ ++#define SLJIT_ARG_MASK 0x7 ++#define SLJIT_ARG_FULL_MASK (SLJIT_ARG_MASK | SLJIT_ARG_TYPE_SCRATCH_REG) ++ ++/* Mask for sljit_emit_enter. */ ++#define SLJIT_KEPT_SAVEDS_COUNT(options) ((options) & 0x3) ++ ++/* Mask for register pairs. */ ++#define REG_PAIR_MASK 0x7f00 ++#define REG_PAIR_FIRST(reg) ((reg) & 0x7f) ++#define REG_PAIR_SECOND(reg) ((reg) >> 8) ++ + /* Mask for argument types. */ + #define SLJIT_DEF_MASK ((1 << SLJIT_DEF_SHIFT) - 1) + +@@ -252,6 +266,26 @@ + # define FCC_IS_SET (1 << 24) + #endif + ++#if (defined SLJIT_CONFIG_RISCV && SLJIT_CONFIG_RISCV) ++# define IS_COND 0x004 ++# define IS_CALL 0x008 ++ ++# define PATCH_B 0x010 ++# define PATCH_J 0x020 ++ ++#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64) ++# define PATCH_REL32 0x040 ++# define PATCH_ABS32 0x080 ++# define PATCH_ABS44 0x100 ++# define PATCH_ABS52 0x200 ++# define JUMP_SIZE_SHIFT 58 ++# define JUMP_MAX_SIZE ((sljit_uw)6) ++#else /* !SLJIT_CONFIG_RISCV_64 */ ++# define JUMP_SIZE_SHIFT 26 ++# define JUMP_MAX_SIZE ((sljit_uw)2) ++#endif /* SLJIT_CONFIG_RISCV_64 */ ++#endif /* SLJIT_CONFIG_RISCV */ ++ + /* Stack management. */ + + #define GET_SAVED_REGISTERS_SIZE(scratches, saveds, extra) \ +@@ -259,6 +293,10 @@ + (saveds < SLJIT_NUMBER_OF_SAVED_REGISTERS ? saveds : SLJIT_NUMBER_OF_SAVED_REGISTERS) + \ + extra) * sizeof(sljit_sw)) + ++#define GET_SAVED_FLOAT_REGISTERS_SIZE(fscratches, fsaveds, type) \ ++ (((fscratches < SLJIT_NUMBER_OF_SCRATCH_FLOAT_REGISTERS ? 0 : (fscratches - SLJIT_NUMBER_OF_SCRATCH_FLOAT_REGISTERS)) + \ ++ (fsaveds)) * SSIZE_OF(type)) ++ + #define ADJUST_LOCAL_OFFSET(p, i) \ + if ((p) == (SLJIT_MEM1(SLJIT_SP))) \ + (i) += SLJIT_LOCALS_OFFSET; +@@ -280,6 +318,10 @@ + + #endif + ++#ifndef SLJIT_UPDATE_WX_FLAGS ++#define SLJIT_UPDATE_WX_FLAGS(from, to, enable_exec) ++#endif ++ + #if (defined SLJIT_PROT_EXECUTABLE_ALLOCATOR && SLJIT_PROT_EXECUTABLE_ALLOCATOR) + #define SLJIT_ADD_EXEC_OFFSET(ptr, exec_offset) ((sljit_u8 *)(ptr) + (exec_offset)) + #else +@@ -984,9 +1026,27 @@ static char* call_arg_names[] = { + /* Arch dependent */ + /* --------------------------------------------------------------------- */ + ++#if (defined SLJIT_CONFIG_RISCV && SLJIT_CONFIG_RISCV) ++SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem_update(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 reg, sljit_s32 mem, sljit_sw memw) ++{ ++ SLJIT_UNUSED_ARG(compiler); ++ SLJIT_UNUSED_ARG(type); ++ SLJIT_UNUSED_ARG(reg); ++ SLJIT_UNUSED_ARG(mem); ++ SLJIT_UNUSED_ARG(memw); ++ SLJIT_UNREACHABLE(); ++ return SLJIT_ERR_UNSUPPORTED; ++} ++ ++#define SLJIT_EMIT_MEM_UPDATE(compiler, type, reg, mem, memw) \ ++ sljit_emit_mem_update(compiler, type, reg, mem, memw) ++#endif ++ + #if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) \ + || (defined SLJIT_VERBOSE && SLJIT_VERBOSE) + ++#define SLJIT_SKIP_CHECKS(compiler) (compiler)->skip_checks = 1 ++ + static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_generate_code(struct sljit_compiler *compiler) + { + #if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) +@@ -1110,6 +1170,37 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_set_context(struct sljit_compi + CHECK_RETURN_OK; + } + ++static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_return_void(struct sljit_compiler *compiler) ++{ ++ if (SLJIT_UNLIKELY(compiler->skip_checks)) { ++ compiler->skip_checks = 0; ++ CHECK_RETURN_OK; ++ } ++ ++#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) ++ if (SLJIT_UNLIKELY(!!compiler->verbose)) { ++ fprintf(compiler->verbose, " return_void\n"); ++ } ++#endif ++ CHECK_RETURN_OK; ++} ++ ++static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_return_to(struct sljit_compiler *compiler, ++ sljit_s32 src, sljit_sw srcw) ++{ ++#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) ++ FUNCTION_CHECK_SRC(src, srcw); ++#endif ++#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) ++ if (SLJIT_UNLIKELY(!!compiler->verbose)) { ++ fprintf(compiler->verbose, " return_to "); ++ sljit_verbose_param(compiler, src, srcw); ++ fprintf(compiler->verbose, "\n"); ++ } ++#endif ++ CHECK_RETURN_OK; ++} ++ + static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_return(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 src, sljit_sw srcw) + { + #if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) +@@ -1256,6 +1347,9 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_op1(struct sljit_compiler + } + + static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_op2(struct sljit_compiler *compiler, sljit_s32 op, ++#if defined(SLJIT_CONFIG_RISCV) && SLJIT_CONFIG_RISCV ++ sljit_s32 unset, ++#endif + sljit_s32 dst, sljit_sw dstw, + sljit_s32 src1, sljit_sw src1w, + sljit_s32 src2, sljit_sw src2w) +@@ -1303,8 +1397,15 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_op2(struct sljit_compiler + SLJIT_UNREACHABLE(); + break; + } +- +- FUNCTION_CHECK_DST(dst, dstw, 1); ++//#if defined(SLJIT_CONFIG_RISCV) && SLJIT_CONFIG_RISCV ++// if (unset) { ++// CHECK_ARGUMENT(HAS_FLAGS(op)); ++// } else { ++// FUNCTION_CHECK_DST(dst, dstw, 0); ++// } ++//#else ++ FUNCTION_CHECK_DST(dst, dstw, 1); ++//#endif + FUNCTION_CHECK_SRC(src1, src1w); + FUNCTION_CHECK_SRC(src2, src2w); + compiler->last_flags = GET_FLAG_TYPE(op) | (op & (SLJIT_I32_OP | SLJIT_SET_Z)); +@@ -1536,6 +1637,7 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_fop2(struct sljit_compile + CHECK_RETURN_OK; + } + ++ + static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_label(struct sljit_compiler *compiler) + { + SLJIT_UNUSED_ARG(compiler); +@@ -1991,32 +2093,11 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_put_label(struct sljit_co + ADJUST_LOCAL_OFFSET(dst, dstw); \ + ADJUST_LOCAL_OFFSET(src, srcw); + +-static SLJIT_INLINE sljit_s32 emit_mov_before_return(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 src, sljit_sw srcw) +-{ +- /* Return if don't need to do anything. */ +- if (op == SLJIT_UNUSED) +- return SLJIT_SUCCESS; +- +-#if (defined SLJIT_64BIT_ARCHITECTURE && SLJIT_64BIT_ARCHITECTURE) +- /* At the moment the pointer size is always equal to sljit_sw. May be changed in the future. */ +- if (src == SLJIT_RETURN_REG && (op == SLJIT_MOV || op == SLJIT_MOV_P)) +- return SLJIT_SUCCESS; +-#else +- if (src == SLJIT_RETURN_REG && (op == SLJIT_MOV || op == SLJIT_MOV_U32 || op == SLJIT_MOV_S32 || op == SLJIT_MOV_P)) +- return SLJIT_SUCCESS; +-#endif +- +-#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) \ +- || (defined SLJIT_VERBOSE && SLJIT_VERBOSE) +- compiler->skip_checks = 1; +-#endif +- return sljit_emit_op1(compiler, op, SLJIT_RETURN_REG, 0, src, srcw); +-} +- + #if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86) \ + || (defined SLJIT_CONFIG_PPC && SLJIT_CONFIG_PPC) \ + || (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32) \ +- || ((defined SLJIT_CONFIG_MIPS && SLJIT_CONFIG_MIPS) && !(defined SLJIT_MIPS_R1 && SLJIT_MIPS_R1)) ++ || ((defined SLJIT_CONFIG_MIPS && SLJIT_CONFIG_MIPS) && !(defined SLJIT_MIPS_R1 && SLJIT_MIPS_R1)) \ ++ || (defined SLJIT_CONFIG_RISCV && SLJIT_CONFIG_RISCV) \ + + static SLJIT_INLINE sljit_s32 sljit_emit_cmov_generic(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 dst_reg, +@@ -2091,13 +2172,15 @@ static SLJIT_INLINE sljit_s32 sljit_emit_cmov_generic(struct sljit_compiler *com + # include "sljitNativePPC_common.c" + #elif (defined SLJIT_CONFIG_MIPS && SLJIT_CONFIG_MIPS) + # include "sljitNativeMIPS_common.c" ++#elif (defined SLJIT_CONFIG_RISCV && SLJIT_CONFIG_RISCV) ++# include "sljitNativeRISCV_common.c" + #elif (defined SLJIT_CONFIG_SPARC && SLJIT_CONFIG_SPARC) + # include "sljitNativeSPARC_common.c" + #elif (defined SLJIT_CONFIG_TILEGX && SLJIT_CONFIG_TILEGX) + # include "sljitNativeTILEGX_64.c" + #endif + +-#if !(defined SLJIT_CONFIG_MIPS && SLJIT_CONFIG_MIPS) ++#if !(defined SLJIT_CONFIG_MIPS && SLJIT_CONFIG_MIPS) && !(defined SLJIT_CONFIG_RISCV && SLJIT_CONFIG_RISCV) + + SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_cmp(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 src1, sljit_sw src1w, +@@ -2204,7 +2287,8 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_fcmp(struct sljit_compile + + #if !(defined SLJIT_CONFIG_ARM_32 && SLJIT_CONFIG_ARM_32) \ + && !(defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64) \ +- && !(defined SLJIT_CONFIG_PPC && SLJIT_CONFIG_PPC) ++ && !(defined SLJIT_CONFIG_PPC && SLJIT_CONFIG_PPC) \ ++ && !(defined SLJIT_CONFIG_RISCV && SLJIT_CONFIG_RISCV) + + SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 reg, +diff --git a/sljit/sljitLir.h b/sljit/sljitLir.h +index 836d25c..8688e5c 100644 +--- a/sljit/sljitLir.h ++++ b/sljit/sljitLir.h +@@ -253,6 +253,11 @@ of sljitConfigInternal.h */ + /* Argument type definitions */ + /* --------------------------------------------------------------------- */ + ++/* The following flag is only allowed for the integer arguments of ++ sljit_emit_enter. When the flag is set, the integer argument is ++ stored in a scratch register instead of a saved register. */ ++#define SLJIT_ARG_TYPE_SCRATCH_REG 0x8 ++ + /* Argument type definitions. + Used by SLJIT_[DEF_]ARGx and SLJIT_[DEF]_RET macros. */ + +@@ -441,6 +446,12 @@ struct sljit_compiler { + sljit_sw cache_argw; + #endif + ++#if (defined SLJIT_CONFIG_RISCV && SLJIT_CONFIG_RISCV) ++ /* Temporary fields. */ ++ sljit_s32 cache_arg; ++ sljit_sw cache_argw; ++#endif /* SLJIT_CONFIG_RISCV */ ++ + #if (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32) + sljit_s32 delay_slot; + sljit_s32 cache_arg; +@@ -461,8 +472,8 @@ struct sljit_compiler { + /* Flags specified by the last arithmetic instruction. + It contains the type of the variable flag. */ + sljit_s32 last_flags; +- /* Local size passed to the functions. */ +- sljit_s32 logical_local_size; ++ /* Local size passed to entry functions. */ ++ sljit_s32 logical_local_size; + #endif + + #if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) \ +diff --git a/sljit/sljitNativeRISCV_32.c b/sljit/sljitNativeRISCV_32.c +new file mode 100644 +index 0000000..a65f8f3 +--- /dev/null ++++ b/sljit/sljitNativeRISCV_32.c +@@ -0,0 +1,142 @@ ++/* ++ * Stack-less Just-In-Time compiler ++ * ++ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without modification, are ++ * permitted provided that the following conditions are met: ++ * ++ * 1. Redistributions of source code must retain the above copyright notice, this list of ++ * conditions and the following disclaimer. ++ * ++ * 2. Redistributions in binary form must reproduce the above copyright notice, this list ++ * of conditions and the following disclaimer in the documentation and/or other materials ++ * provided with the distribution. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY ++ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ++ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT ++ * SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, ++ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED ++ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR ++ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ++ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++static sljit_s32 load_immediate(struct sljit_compiler *compiler, sljit_s32 dst_r, sljit_sw imm, sljit_s32 tmp_r) ++{ ++ SLJIT_UNUSED_ARG(tmp_r); ++ ++ if (imm <= SIMM_MAX && imm >= SIMM_MIN) ++ return push_inst(compiler, ADDI | RD(dst_r) | RS1(TMP_ZERO) | IMM_I(imm)); ++ ++ if (imm & 0x800) ++ imm += 0x1000; ++ ++ FAIL_IF(push_inst(compiler, LUI | RD(dst_r) | (sljit_ins)(imm & ~0xfff))); ++ ++ if ((imm & 0xfff) == 0) ++ return SLJIT_SUCCESS; ++ ++ return push_inst(compiler, ADDI | RD(dst_r) | RS1(dst_r) | IMM_I(imm)); ++} ++ ++//SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fset64(struct sljit_compiler *compiler, ++// sljit_s32 freg, sljit_f64 value) ++//{ ++// union { ++// sljit_s32 imm[2]; ++// sljit_f64 value; ++// } u; ++// ++// CHECK_ERROR(); ++// CHECK(check_sljit_emit_fset64(compiler, freg, value)); ++// ++// u.value = value; ++// ++// if (u.imm[0] != 0) ++// FAIL_IF(load_immediate(compiler, TMP_REG1, u.imm[0], TMP_REG3)); ++// if (u.imm[1] != 0) ++// FAIL_IF(load_immediate(compiler, TMP_REG2, u.imm[1], TMP_REG3)); ++// ++// FAIL_IF(push_inst(compiler, ADDI | RD(SLJIT_SP) | RS1(SLJIT_SP) | IMM_I(-16))); ++// FAIL_IF(push_inst(compiler, SW | RS1(SLJIT_SP) | RS2(u.imm[0] != 0 ? TMP_REG1 : TMP_ZERO) | (8 << 7))); ++// FAIL_IF(push_inst(compiler, SW | RS1(SLJIT_SP) | RS2(u.imm[1] != 0 ? TMP_REG2 : TMP_ZERO) | (12 << 7))); ++// FAIL_IF(push_inst(compiler, FLD | FRD(freg) | RS1(SLJIT_SP) | IMM_I(8))); ++// return push_inst(compiler, ADDI | RD(SLJIT_SP) | RS1(SLJIT_SP) | IMM_I(16)); ++//} ++// ++//SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fcopy(struct sljit_compiler *compiler, sljit_s32 op, ++// sljit_s32 freg, sljit_s32 reg) ++//{ ++// sljit_ins inst; ++// sljit_s32 reg2 = 0; ++// ++// CHECK_ERROR(); ++// CHECK(check_sljit_emit_fcopy(compiler, op, freg, reg)); ++// ++// if (op & SLJIT_I32_OP) { ++// if (op == SLJIT_COPY32_TO_F32) ++// inst = FMV_W_X | RS1(reg) | FRD(freg); ++// else ++// inst = FMV_X_W | FRS1(freg) | RD(reg); ++// ++// return push_inst(compiler, inst); ++// } ++// ++// FAIL_IF(push_inst(compiler, ADDI | RD(SLJIT_SP) | RS1(SLJIT_SP) | IMM_I(-16))); ++// ++// if (reg & REG_PAIR_MASK) { ++// reg2 = REG_PAIR_SECOND(reg); ++// reg = REG_PAIR_FIRST(reg); ++// } ++// ++// if (op == SLJIT_COPY_TO_F64) { ++// if (reg2 != 0) ++// FAIL_IF(push_inst(compiler, SW | RS1(SLJIT_SP) | RS2(reg2) | (8 << 7))); ++// else ++// FAIL_IF(push_inst(compiler, FSW | RS1(SLJIT_SP) | FRS2(freg) | (8 << 7))); ++// ++// FAIL_IF(push_inst(compiler, SW | RS1(SLJIT_SP) | RS2(reg) | (12 << 7))); ++// FAIL_IF(push_inst(compiler, FLD | FRD(freg) | RS1(SLJIT_SP) | IMM_I(8))); ++// } else { ++// FAIL_IF(push_inst(compiler, FSD | RS1(SLJIT_SP) | FRS2(freg) | (8 << 7))); ++// ++// if (reg2 != 0) ++// FAIL_IF(push_inst(compiler, FMV_X_W | FRS1(freg) | RD(reg2))); ++// ++// FAIL_IF(push_inst(compiler, LW | RD(reg) | RS1(SLJIT_SP) | IMM_I(12))); ++// } ++// ++// return push_inst(compiler, ADDI | RD(SLJIT_SP) | RS1(SLJIT_SP) | IMM_I(16)); ++//} ++ ++static SLJIT_INLINE sljit_s32 emit_const(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw init_value, sljit_ins last_ins) ++{ ++ if ((init_value & 0x800) != 0) ++ init_value += 0x1000; ++ ++ FAIL_IF(push_inst(compiler, LUI | RD(dst) | (sljit_ins)(init_value & ~0xfff))); ++ return push_inst(compiler, last_ins | RS1(dst) | IMM_I(init_value)); ++} ++ ++SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_target, sljit_sw executable_offset) ++{ ++ sljit_ins *inst = (sljit_ins*)addr; ++ SLJIT_UNUSED_ARG(executable_offset); ++ ++ if ((new_target & 0x800) != 0) ++ new_target += 0x1000; ++ ++ SLJIT_UPDATE_WX_FLAGS(inst, inst + 5, 0); ++ ++ SLJIT_ASSERT((inst[0] & 0x7f) == LUI); ++ inst[0] = (inst[0] & 0xfff) | (sljit_ins)((sljit_sw)new_target & ~0xfff); ++ SLJIT_ASSERT((inst[1] & 0x707f) == ADDI || (inst[1] & 0x707f) == JALR); ++ inst[1] = (inst[1] & 0xfffff) | IMM_I(new_target); ++ ++ SLJIT_UPDATE_WX_FLAGS(inst, inst + 5, 1); ++ inst = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset); ++ SLJIT_CACHE_FLUSH(inst, inst + 5); ++} +diff --git a/sljit/sljitNativeRISCV_64.c b/sljit/sljitNativeRISCV_64.c +new file mode 100644 +index 0000000..cdecf2b +--- /dev/null ++++ b/sljit/sljitNativeRISCV_64.c +@@ -0,0 +1,183 @@ ++/* ++ * Stack-less Just-In-Time compiler ++ * ++ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without modification, are ++ * permitted provided that the following conditions are met: ++ * ++ * 1. Redistributions of source code must retain the above copyright notice, this list of ++ * conditions and the following disclaimer. ++ * ++ * 2. Redistributions in binary form must reproduce the above copyright notice, this list ++ * of conditions and the following disclaimer in the documentation and/or other materials ++ * provided with the distribution. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY ++ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ++ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT ++ * SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, ++ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED ++ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR ++ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ++ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++static sljit_s32 load_immediate(struct sljit_compiler *compiler, sljit_s32 dst_r, sljit_sw imm, sljit_s32 tmp_r) ++{ ++ sljit_sw high; ++ ++ if (imm <= SIMM_MAX && imm >= SIMM_MIN) ++ return push_inst(compiler, ADDI | RD(dst_r) | RS1(TMP_ZERO) | IMM_I(imm)); ++ ++ if (imm <= 0x7fffffffl && imm >= S32_MIN) { ++ if (imm > S32_MAX) { ++ SLJIT_ASSERT((imm & 0x800) != 0); ++ FAIL_IF(push_inst(compiler, LUI | RD(dst_r) | (sljit_ins)0x80000000u)); ++ return push_inst(compiler, XORI | RD(dst_r) | RS1(dst_r) | IMM_I(imm)); ++ } ++ ++ if ((imm & 0x800) != 0) ++ imm += 0x1000; ++ ++ FAIL_IF(push_inst(compiler, LUI | RD(dst_r) | (sljit_ins)(imm & ~0xfff))); ++ ++ if ((imm & 0xfff) == 0) ++ return SLJIT_SUCCESS; ++ ++ return push_inst(compiler, ADDI | RD(dst_r) | RS1(dst_r) | IMM_I(imm)); ++ } ++ ++ /* Trailing zeroes could be used to produce shifted immediates. */ ++ ++ if (imm <= 0x7ffffffffffl && imm >= -0x80000000000l) { ++ high = imm >> 12; ++ ++ if (imm & 0x800) ++ high = ~high; ++ ++ if (high > S32_MAX) { ++ SLJIT_ASSERT((high & 0x800) != 0); ++ FAIL_IF(push_inst(compiler, LUI | RD(dst_r) | (sljit_ins)0x80000000u)); ++ FAIL_IF(push_inst(compiler, XORI | RD(dst_r) | RS1(dst_r) | IMM_I(high))); ++ } else { ++ if ((high & 0x800) != 0) ++ high += 0x1000; ++ ++ FAIL_IF(push_inst(compiler, LUI | RD(dst_r) | (sljit_ins)(high & ~0xfff))); ++ ++ if ((high & 0xfff) != 0) ++ FAIL_IF(push_inst(compiler, ADDI | RD(dst_r) | RS1(dst_r) | IMM_I(high))); ++ } ++ ++ FAIL_IF(push_inst(compiler, SLLI | RD(dst_r) | RS1(dst_r) | IMM_I(12))); ++ ++ if ((imm & 0xfff) != 0) ++ return push_inst(compiler, XORI | RD(dst_r) | RS1(dst_r) | IMM_I(imm)); ++ ++ return SLJIT_SUCCESS; ++ } ++ ++ SLJIT_ASSERT(dst_r != tmp_r); ++ ++ high = imm >> 32; ++ imm = (sljit_s32)imm; ++ ++ if ((imm & 0x80000000l) != 0) ++ high = ~high; ++ ++ if (high <= 0x7ffff && high >= -0x80000) { ++ FAIL_IF(push_inst(compiler, LUI | RD(tmp_r) | (sljit_ins)(high << 12))); ++ high = 0x1000; ++ } else { ++ if ((high & 0x800) != 0) ++ high += 0x1000; ++ ++ FAIL_IF(push_inst(compiler, LUI | RD(tmp_r) | (sljit_ins)(high & ~0xfff))); ++ high &= 0xfff; ++ } ++ ++ if (imm <= SIMM_MAX && imm >= SIMM_MIN) { ++ FAIL_IF(push_inst(compiler, ADDI | RD(dst_r) | RS1(TMP_ZERO) | IMM_I(imm))); ++ imm = 0; ++ } else if (imm > S32_MAX) { ++ SLJIT_ASSERT((imm & 0x800) != 0); ++ ++ FAIL_IF(push_inst(compiler, LUI | RD(dst_r) | (sljit_ins)0x80000000u)); ++ imm = 0x1000 | (imm & 0xfff); ++ } else { ++ if ((imm & 0x800) != 0) ++ imm += 0x1000; ++ ++ FAIL_IF(push_inst(compiler, LUI | RD(dst_r) | (sljit_ins)(imm & ~0xfff))); ++ imm &= 0xfff; ++ } ++ ++ if ((high & 0xfff) != 0) ++ FAIL_IF(push_inst(compiler, ADDI | RD(tmp_r) | RS1(tmp_r) | IMM_I(high))); ++ ++ if (imm & 0x1000) ++ FAIL_IF(push_inst(compiler, XORI | RD(dst_r) | RS1(dst_r) | IMM_I(imm))); ++ else if (imm != 0) ++ FAIL_IF(push_inst(compiler, ADDI | RD(dst_r) | RS1(dst_r) | IMM_I(imm))); ++ ++ FAIL_IF(push_inst(compiler, SLLI | RD(tmp_r) | RS1(tmp_r) | IMM_I((high & 0x1000) ? 20 : 32))); ++ return push_inst(compiler, XOR | RD(dst_r) | RS1(dst_r) | RS2(tmp_r)); ++} ++ ++static SLJIT_INLINE sljit_s32 emit_const(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw init_value, sljit_ins last_ins) ++{ ++ sljit_sw high; ++ ++ if ((init_value & 0x800) != 0) ++ init_value += 0x1000; ++ ++ high = init_value >> 32; ++ ++ if ((init_value & 0x80000000l) != 0) ++ high = ~high; ++ ++ if ((high & 0x800) != 0) ++ high += 0x1000; ++ ++ FAIL_IF(push_inst(compiler, LUI | RD(TMP_REG3) | (sljit_ins)(high & ~0xfff))); ++ FAIL_IF(push_inst(compiler, ADDI | RD(TMP_REG3) | RS1(TMP_REG3) | IMM_I(high))); ++ FAIL_IF(push_inst(compiler, LUI | RD(dst) | (sljit_ins)(init_value & ~0xfff))); ++ FAIL_IF(push_inst(compiler, SLLI | RD(TMP_REG3) | RS1(TMP_REG3) | IMM_I(32))); ++ FAIL_IF(push_inst(compiler, XOR | RD(dst) | RS1(dst) | RS2(TMP_REG3))); ++ return push_inst(compiler, last_ins | RS1(dst) | IMM_I(init_value)); ++} ++ ++SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_target, sljit_sw executable_offset) ++{ ++ sljit_ins *inst = (sljit_ins*)addr; ++ sljit_sw high; ++ SLJIT_UNUSED_ARG(executable_offset); ++ ++ if ((new_target & 0x800) != 0) ++ new_target += 0x1000; ++ ++ high = (sljit_sw)new_target >> 32; ++ ++ if ((new_target & 0x80000000l) != 0) ++ high = ~high; ++ ++ if ((high & 0x800) != 0) ++ high += 0x1000; ++ ++ SLJIT_UPDATE_WX_FLAGS(inst, inst + 5, 0); ++ ++ SLJIT_ASSERT((inst[0] & 0x7f) == LUI); ++ inst[0] = (inst[0] & 0xfff) | (sljit_ins)(high & ~0xfff); ++ SLJIT_ASSERT((inst[1] & 0x707f) == ADDI); ++ inst[1] = (inst[1] & 0xfffff) | IMM_I(high); ++ SLJIT_ASSERT((inst[2] & 0x7f) == LUI); ++ inst[2] = (inst[2] & 0xfff) | (sljit_ins)((sljit_sw)new_target & ~0xfff); ++ SLJIT_ASSERT((inst[5] & 0x707f) == ADDI || (inst[5] & 0x707f) == JALR); ++ inst[5] = (inst[5] & 0xfffff) | IMM_I(new_target); ++ SLJIT_UPDATE_WX_FLAGS(inst, inst + 5, 1); ++ ++ inst = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset); ++ SLJIT_CACHE_FLUSH(inst, inst + 5); ++} +diff --git a/sljit/sljitNativeRISCV_common.c b/sljit/sljitNativeRISCV_common.c +new file mode 100644 +index 0000000..932e04e +--- /dev/null ++++ b/sljit/sljitNativeRISCV_common.c +@@ -0,0 +1,2627 @@ ++/* ++ * Stack-less Just-In-Time compiler ++ * ++ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without modification, are ++ * permitted provided that the following conditions are met: ++ * ++ * 1. Redistributions of source code must retain the above copyright notice, this list of ++ * conditions and the following disclaimer. ++ * ++ * 2. Redistributions in binary form must reproduce the above copyright notice, this list ++ * of conditions and the following disclaimer in the documentation and/or other materials ++ * provided with the distribution. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY ++ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ++ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT ++ * SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, ++ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED ++ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR ++ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ++ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++SLJIT_API_FUNC_ATTRIBUTE const char* sljit_get_platform_name(void) ++{ ++#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) ++ return "RISC-V-32" SLJIT_CPUINFO; ++#else /* !SLJIT_CONFIG_RISCV_32 */ ++ return "RISC-V-64" SLJIT_CPUINFO; ++#endif /* SLJIT_CONFIG_RISCV_32 */ ++} ++ ++/* Length of an instruction word ++ Both for riscv-32 and riscv-64 */ ++typedef sljit_u32 sljit_ins; ++ ++#define TMP_REG1 (SLJIT_NUMBER_OF_REGISTERS + 2) ++#define TMP_REG2 (SLJIT_NUMBER_OF_REGISTERS + 3) ++#define TMP_REG3 (SLJIT_NUMBER_OF_REGISTERS + 4) ++#define TMP_ZERO 0 ++ ++/* Flags are kept in volatile registers. */ ++#define EQUAL_FLAG (SLJIT_NUMBER_OF_REGISTERS + 5) ++#define RETURN_ADDR_REG TMP_REG2 ++#define OTHER_FLAG (SLJIT_NUMBER_OF_REGISTERS + 6) ++ ++#define TMP_FREG1 (SLJIT_NUMBER_OF_FLOAT_REGISTERS + 1) ++#define TMP_FREG2 (SLJIT_NUMBER_OF_FLOAT_REGISTERS + 2) ++ ++static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 7] = { ++ 0, 10, 11, 12, 13, 14, 15, 16, 17, 29, 30, 31, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 9, 8, 2, 6, 1, 7, 5, 28 ++}; ++ ++static const sljit_u8 freg_map[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 3] = { ++ 0, 10, 11, 12, 13, 14, 15, 16, 17 ++}; ++ ++// , 2, 3, 4, 5, 6, 7, 28, 29, 30, 31, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 9, 8, 0, 1, ++ ++/* --------------------------------------------------------------------- */ ++/* Instrucion forms */ ++/* --------------------------------------------------------------------- */ ++ ++#define RD(rd) ((sljit_ins)reg_map[rd] << 7) ++#define RS1(rs1) ((sljit_ins)reg_map[rs1] << 15) ++#define RS2(rs2) ((sljit_ins)reg_map[rs2] << 20) ++#define FRD(rd) ((sljit_ins)freg_map[rd] << 7) ++#define FRS1(rs1) ((sljit_ins)freg_map[rs1] << 15) ++#define FRS2(rs2) ((sljit_ins)freg_map[rs2] << 20) ++#define IMM_I(imm) ((sljit_ins)(imm) << 20) ++#define IMM_S(imm) ((((sljit_ins)(imm) & 0xfe0) << 20) | (((sljit_ins)(imm) & 0x1f) << 7)) ++ ++/* Represents funct(i) parts of the instructions. */ ++#define OPC(o) ((sljit_ins)(o)) ++#define F3(f) ((sljit_ins)(f) << 12) ++#define F12(f) ((sljit_ins)(f) << 20) ++#define F7(f) ((sljit_ins)(f) << 25) ++ ++#define ADD (F7(0x0) | F3(0x0) | OPC(0x33)) ++#define ADDI (F3(0x0) | OPC(0x13)) ++#define AND (F7(0x0) | F3(0x7) | OPC(0x33)) ++#define ANDI (F3(0x7) | OPC(0x13)) ++#define AUIPC (OPC(0x17)) ++#define BEQ (F3(0x0) | OPC(0x63)) ++#define BNE (F3(0x1) | OPC(0x63)) ++#define BLT (F3(0x4) | OPC(0x63)) ++#define BGE (F3(0x5) | OPC(0x63)) ++#define BLTU (F3(0x6) | OPC(0x63)) ++#define BGEU (F3(0x7) | OPC(0x63)) ++#define DIV (F7(0x1) | F3(0x4) | OPC(0x33)) ++#define DIVU (F7(0x1) | F3(0x5) | OPC(0x33)) ++#define EBREAK (F12(0x1) | F3(0x0) | OPC(0x73)) ++#define FADD_S (F7(0x0) | F3(0x7) | OPC(0x53)) ++#define FDIV_S (F7(0xc) | F3(0x7) | OPC(0x53)) ++#define FEQ_S (F7(0x50) | F3(0x2) | OPC(0x53)) ++#define FLD (F3(0x3) | OPC(0x7)) ++#define FLE_S (F7(0x50) | F3(0x0) | OPC(0x53)) ++#define FLT_S (F7(0x50) | F3(0x1) | OPC(0x53)) ++/* These conversion opcodes are partly defined. */ ++#define FCVT_S_D (F7(0x20) | OPC(0x53)) ++#define FCVT_S_W (F7(0x68) | OPC(0x53)) ++#define FCVT_S_WU (F7(0x68) | F12(0x1) | OPC(0x53)) ++#define FCVT_W_S (F7(0x60) | F3(0x1) | OPC(0x53)) ++#define FMUL_S (F7(0x8) | F3(0x7) | OPC(0x53)) ++#define FMV_X_W (F7(0x70) | F3(0x0) | OPC(0x53)) ++#define FMV_W_X (F7(0x78) | F3(0x0) | OPC(0x53)) ++#define FSD (F3(0x3) | OPC(0x27)) ++#define FSGNJ_S (F7(0x10) | F3(0x0) | OPC(0x53)) ++#define FSGNJ_D (F7(0x11) | F3(0x0) | OPC(0x53)) ++#define FSGNJN_S (F7(0x10) | F3(0x1) | OPC(0x53)) ++#define FSGNJX_S (F7(0x10) | F3(0x2) | OPC(0x53)) ++#define FSUB_S (F7(0x4) | F3(0x7) | OPC(0x53)) ++#define FSW (F3(0x2) | OPC(0x27)) ++#define JAL (OPC(0x6f)) ++#define JALR (F3(0x0) | OPC(0x67)) ++#define LD (F3(0x3) | OPC(0x3)) ++#define LUI (OPC(0x37)) ++#define LW (F3(0x2) | OPC(0x3)) ++#define MUL (F7(0x1) | F3(0x0) | OPC(0x33)) ++#define MULH (F7(0x1) | F3(0x1) | OPC(0x33)) ++#define MULHU (F7(0x1) | F3(0x3) | OPC(0x33)) ++#define OR (F7(0x0) | F3(0x6) | OPC(0x33)) ++#define ORI (F3(0x6) | OPC(0x13)) ++#define REM (F7(0x1) | F3(0x6) | OPC(0x33)) ++#define REMU (F7(0x1) | F3(0x7) | OPC(0x33)) ++#define SD (F3(0x3) | OPC(0x23)) ++#define SLL (F7(0x0) | F3(0x1) | OPC(0x33)) ++#define SLLI (IMM_I(0x0) | F3(0x1) | OPC(0x13)) ++#define SLT (F7(0x0) | F3(0x2) | OPC(0x33)) ++#define SLTI (F3(0x2) | OPC(0x13)) ++#define SLTU (F7(0x0) | F3(0x3) | OPC(0x33)) ++#define SLTUI (F3(0x3) | OPC(0x13)) ++#define SRL (F7(0x0) | F3(0x5) | OPC(0x33)) ++#define SRLI (IMM_I(0x0) | F3(0x5) | OPC(0x13)) ++#define SRA (F7(0x20) | F3(0x5) | OPC(0x33)) ++#define SRAI (IMM_I(0x400) | F3(0x5) | OPC(0x13)) ++#define SUB (F7(0x20) | F3(0x0) | OPC(0x33)) ++#define SW (F3(0x2) | OPC(0x23)) ++#define XOR (F7(0x0) | F3(0x4) | OPC(0x33)) ++#define XORI (F3(0x4) | OPC(0x13)) ++ ++#define SIMM_MAX (0x7ff) ++#define SIMM_MIN (-0x800) ++#define BRANCH_MAX (0xfff) ++#define BRANCH_MIN (-0x1000) ++#define JUMP_MAX (0xfffff) ++#define JUMP_MIN (-0x100000) ++ ++#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64) ++#define S32_MAX (0x7ffff7ffl) ++#define S32_MIN (-0x80000000l) ++#define S44_MAX (0x7fffffff7ffl) ++#define S52_MAX (0x7ffffffffffffl) ++#endif ++ ++static sljit_s32 push_inst(struct sljit_compiler *compiler, sljit_ins ins) ++{ ++ sljit_ins *ptr = (sljit_ins*)ensure_buf(compiler, sizeof(sljit_ins)); ++ FAIL_IF(!ptr); ++ *ptr = ins; ++ compiler->size++; ++ return SLJIT_SUCCESS; ++} ++ ++static sljit_s32 push_imm_s_inst(struct sljit_compiler *compiler, sljit_ins ins, sljit_sw imm) ++{ ++ return push_inst(compiler, ins | IMM_S(imm)); ++} ++ ++static SLJIT_INLINE sljit_ins* detect_jump_type(struct sljit_jump *jump, sljit_ins *code, sljit_sw executable_offset) ++{ ++ sljit_sw diff; ++ sljit_uw target_addr; ++ sljit_ins *inst; ++ ++ inst = (sljit_ins *)jump->addr; ++ ++ if (jump->flags & SLJIT_REWRITABLE_JUMP) ++ goto exit; ++ ++ if (jump->flags & JUMP_ADDR) ++ target_addr = jump->u.target; ++ else { ++ SLJIT_ASSERT(jump->flags & JUMP_LABEL); ++ target_addr = (sljit_uw)(code + jump->u.label->size) + (sljit_uw)executable_offset; ++ } ++ ++ diff = (sljit_sw)target_addr - (sljit_sw)inst - executable_offset; ++ ++ if (jump->flags & IS_COND) { ++ diff += SSIZE_OF(ins); ++ ++ if (diff >= BRANCH_MIN && diff <= BRANCH_MAX) { ++ inst--; ++ inst[0] = (inst[0] & 0x1fff07f) ^ 0x1000; ++ jump->flags |= PATCH_B; ++ jump->addr = (sljit_uw)inst; ++ return inst; ++ } ++ ++ diff -= SSIZE_OF(ins); ++ } ++ ++ if (diff >= JUMP_MIN && diff <= JUMP_MAX) { ++ if (jump->flags & IS_COND) { ++#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) ++ inst[-1] -= (sljit_ins)(1 * sizeof(sljit_ins)) << 7; ++#else ++ inst[-1] -= (sljit_ins)(5 * sizeof(sljit_ins)) << 7; ++#endif ++ } ++ ++ jump->flags |= PATCH_J; ++ return inst; ++ } ++ ++#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64) ++ if (diff >= S32_MIN && diff <= S32_MAX) { ++ if (jump->flags & IS_COND) ++ inst[-1] -= (sljit_ins)(4 * sizeof(sljit_ins)) << 7; ++ ++ jump->flags |= PATCH_REL32; ++ inst[1] = inst[0]; ++ return inst + 1; ++ } ++ ++ if (target_addr <= (sljit_uw)S32_MAX) { ++ if (jump->flags & IS_COND) ++ inst[-1] -= (sljit_ins)(4 * sizeof(sljit_ins)) << 7; ++ ++ jump->flags |= PATCH_ABS32; ++ inst[1] = inst[0]; ++ return inst + 1; ++ } ++ ++ if (target_addr <= S44_MAX) { ++ if (jump->flags & IS_COND) ++ inst[-1] -= (sljit_ins)(2 * sizeof(sljit_ins)) << 7; ++ ++ jump->flags |= PATCH_ABS44; ++ inst[3] = inst[0]; ++ return inst + 3; ++ } ++ ++ if (target_addr <= S52_MAX) { ++ if (jump->flags & IS_COND) ++ inst[-1] -= (sljit_ins)(1 * sizeof(sljit_ins)) << 7; ++ ++ jump->flags |= PATCH_ABS52; ++ inst[4] = inst[0]; ++ return inst + 4; ++ } ++#endif ++ ++exit: ++#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) ++ inst[1] = inst[0]; ++ return inst + 1; ++#else ++ inst[5] = inst[0]; ++ return inst + 5; ++#endif ++} ++ ++#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64) ++ ++static SLJIT_INLINE sljit_sw put_label_get_length(struct sljit_put_label *put_label, sljit_uw max_label) ++{ ++ if (max_label <= (sljit_uw)S32_MAX) { ++ put_label->flags = PATCH_ABS32; ++ return 1; ++ } ++ ++ if (max_label <= S44_MAX) { ++ put_label->flags = PATCH_ABS44; ++ return 3; ++ } ++ ++ if (max_label <= S52_MAX) { ++ put_label->flags = PATCH_ABS52; ++ return 4; ++ } ++ ++ put_label->flags = 0; ++ return 5; ++} ++ ++#endif /* SLJIT_CONFIG_RISCV_64 */ ++ ++static SLJIT_INLINE void load_addr_to_reg(void *dst, sljit_u32 reg) ++{ ++ struct sljit_jump *jump = NULL; ++ struct sljit_put_label *put_label; ++ sljit_uw flags; ++ sljit_ins *inst; ++#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64) ++ sljit_sw high; ++#endif ++ sljit_uw addr; ++ ++ if (reg != 0) { ++ jump = (struct sljit_jump*)dst; ++ flags = jump->flags; ++ inst = (sljit_ins*)jump->addr; ++ addr = (flags & JUMP_LABEL) ? jump->u.label->addr : jump->u.target; ++ } else { ++ put_label = (struct sljit_put_label*)dst; ++#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64) ++ flags = put_label->flags; ++#endif ++ inst = (sljit_ins*)put_label->addr; ++ addr = put_label->label->addr; ++ reg = *inst; ++ } ++ ++ if ((addr & 0x800) != 0) ++ addr += 0x1000; ++ ++#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) ++ inst[0] = LUI | RD(reg) | (sljit_ins)((sljit_sw)addr & ~0xfff); ++#else /* !SLJIT_CONFIG_RISCV_32 */ ++ ++ if (flags & PATCH_ABS32) { ++ SLJIT_ASSERT(addr <= S32_MAX); ++ inst[0] = LUI | RD(reg) | (sljit_ins)((sljit_sw)addr & ~0xfff); ++ } else if (flags & PATCH_ABS44) { ++ high = (sljit_sw)addr >> 12; ++ SLJIT_ASSERT((sljit_uw)high <= 0x7fffffff); ++ ++ if (high > S32_MAX) { ++ SLJIT_ASSERT((high & 0x800) != 0); ++ inst[0] = LUI | RD(reg) | (sljit_ins)0x80000000u; ++ inst[1] = XORI | RD(reg) | RS1(reg) | IMM_I(high); ++ } else { ++ if ((high & 0x800) != 0) ++ high += 0x1000; ++ ++ inst[0] = LUI | RD(reg) | (sljit_ins)(high & ~0xfff); ++ inst[1] = ADDI | RD(reg) | RS1(reg) | IMM_I(high); ++ } ++ ++ inst[2] = SLLI | RD(reg) | RS1(reg) | IMM_I(12); ++ inst += 2; ++ } else { ++ high = (sljit_sw)addr >> 32; ++ ++ if ((addr & 0x80000000l) != 0) ++ high = ~high; ++ ++ if ((high & 0x800) != 0) ++ high += 0x1000; ++ ++ if (flags & PATCH_ABS52) { ++ SLJIT_ASSERT(addr <= S52_MAX); ++ inst[0] = LUI | RD(TMP_REG3) | (sljit_ins)(high << 12); ++ } else { ++ inst[0] = LUI | RD(TMP_REG3) | (sljit_ins)(high & ~0xfff); ++ inst[1] = ADDI | RD(TMP_REG3) | RS1(TMP_REG3) | IMM_I(high); ++ inst++; ++ } ++ ++ inst[1] = LUI | RD(reg) | (sljit_ins)((sljit_sw)addr & ~0xfff); ++ inst[2] = SLLI | RD(TMP_REG3) | RS1(TMP_REG3) | IMM_I((flags & PATCH_ABS52) ? 20 : 32); ++ inst[3] = XOR | RD(reg) | RS1(reg) | RS2(TMP_REG3); ++ inst += 3; ++ } ++#endif /* !SLJIT_CONFIG_RISCV_32 */ ++ ++ if (jump != NULL) { ++ SLJIT_ASSERT((inst[1] & 0x707f) == JALR); ++ inst[1] = (inst[1] & 0xfffff) | IMM_I(addr); ++ } else ++ inst[1] = ADDI | RD(reg) | RS1(reg) | IMM_I(addr); ++} ++ ++SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compiler) ++{ ++ struct sljit_memory_fragment *buf; ++ sljit_ins *code; ++ sljit_ins *code_ptr; ++ sljit_ins *buf_ptr; ++ sljit_ins *buf_end; ++ sljit_uw word_count; ++ sljit_uw next_addr; ++ sljit_sw executable_offset; ++ sljit_uw addr; ++ ++ struct sljit_label *label; ++ struct sljit_jump *jump; ++ struct sljit_const *const_; ++ struct sljit_put_label *put_label; ++ ++ CHECK_ERROR_PTR(); ++ CHECK_PTR(check_sljit_generate_code(compiler)); ++ reverse_buf(compiler); ++ ++ code = (sljit_ins*)SLJIT_MALLOC_EXEC(compiler->size * sizeof(sljit_ins)); ++ PTR_FAIL_WITH_EXEC_IF(code); ++ buf = compiler->buf; ++ ++ code_ptr = code; ++ word_count = 0; ++ next_addr = 0; ++ executable_offset = SLJIT_EXEC_OFFSET(code); ++ ++ label = compiler->labels; ++ jump = compiler->jumps; ++ const_ = compiler->consts; ++ put_label = compiler->put_labels; ++ ++ do { ++ buf_ptr = (sljit_ins*)buf->memory; ++ buf_end = buf_ptr + (buf->used_size >> 2); ++ do { ++ *code_ptr = *buf_ptr++; ++ if (next_addr == word_count) { ++ SLJIT_ASSERT(!label || label->size >= word_count); ++ SLJIT_ASSERT(!jump || jump->addr >= word_count); ++ SLJIT_ASSERT(!const_ || const_->addr >= word_count); ++ SLJIT_ASSERT(!put_label || put_label->addr >= word_count); ++ ++ /* These structures are ordered by their address. */ ++ if (label && label->size == word_count) { ++ label->addr = (sljit_uw)SLJIT_ADD_EXEC_OFFSET(code_ptr, executable_offset); ++ label->size = (sljit_uw)(code_ptr - code); ++ label = label->next; ++ } ++ if (jump && jump->addr == word_count) { ++ word_count += JUMP_MAX_SIZE - 1; ++ jump->addr = (sljit_uw)code_ptr; ++ code_ptr = detect_jump_type(jump, code, executable_offset); ++ jump = jump->next; ++ } ++ if (const_ && const_->addr == word_count) { ++ const_->addr = (sljit_uw)code_ptr; ++ const_ = const_->next; ++ } ++ if (put_label && put_label->addr == word_count) { ++ SLJIT_ASSERT(put_label->label); ++ put_label->addr = (sljit_uw)code_ptr; ++#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) ++ code_ptr += 1; ++#else ++ code_ptr += put_label_get_length(put_label, (sljit_uw)(SLJIT_ADD_EXEC_OFFSET(code, executable_offset) + put_label->label->size)); ++#endif ++ word_count += JUMP_MAX_SIZE - 1; ++ put_label = put_label->next; ++ } ++ next_addr = compute_next_addr(label, jump, const_, put_label); ++ } ++ code_ptr++; ++ word_count++; ++ } while (buf_ptr < buf_end); ++ ++ buf = buf->next; ++ } while (buf); ++ ++ if (label && label->size == word_count) { ++ label->addr = (sljit_uw)code_ptr; ++ label->size = (sljit_uw)(code_ptr - code); ++ label = label->next; ++ } ++ ++ SLJIT_ASSERT(!label); ++ SLJIT_ASSERT(!jump); ++ SLJIT_ASSERT(!const_); ++ SLJIT_ASSERT(!put_label); ++ SLJIT_ASSERT(code_ptr - code <= (sljit_sw)compiler->size); ++ ++ jump = compiler->jumps; ++ while (jump) { ++ do { ++ if (!(jump->flags & (PATCH_B | PATCH_J | PATCH_REL32))) { ++ load_addr_to_reg(jump, TMP_REG1); ++ break; ++ } ++ ++ addr = (jump->flags & JUMP_LABEL) ? jump->u.label->addr : jump->u.target; ++ buf_ptr = (sljit_ins *)jump->addr; ++ addr -= (sljit_uw)SLJIT_ADD_EXEC_OFFSET(buf_ptr, executable_offset); ++ ++ if (jump->flags & PATCH_B) { ++ SLJIT_ASSERT((sljit_sw)addr >= BRANCH_MIN && (sljit_sw)addr <= BRANCH_MAX); ++ addr = ((addr & 0x800) >> 4) | ((addr & 0x1e) << 7) | ((addr & 0x7e0) << 20) | ((addr & 0x1000) << 19); ++ buf_ptr[0] |= (sljit_ins)addr; ++ break; ++ } ++ ++#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64) ++ if (jump->flags & PATCH_REL32) { ++ SLJIT_ASSERT((sljit_sw)addr >= S32_MIN && (sljit_sw)addr <= S32_MAX); ++ ++ if ((addr & 0x800) != 0) ++ addr += 0x1000; ++ ++ buf_ptr[0] = AUIPC | RD(TMP_REG1) | (sljit_ins)((sljit_sw)addr & ~0xfff); ++ SLJIT_ASSERT((buf_ptr[1] & 0x707f) == JALR); ++ buf_ptr[1] |= IMM_I(addr); ++ break; ++ } ++#endif ++ ++ SLJIT_ASSERT((sljit_sw)addr >= JUMP_MIN && (sljit_sw)addr <= JUMP_MAX); ++ addr = (addr & 0xff000) | ((addr & 0x800) << 9) | ((addr & 0x7fe) << 20) | ((addr & 0x100000) << 11); ++ buf_ptr[0] = JAL | RD((jump->flags & IS_CALL) ? RETURN_ADDR_REG : TMP_ZERO) | (sljit_ins)addr; ++ } while (0); ++ jump = jump->next; ++ } ++ ++ put_label = compiler->put_labels; ++ while (put_label) { ++ load_addr_to_reg(put_label, 0); ++ put_label = put_label->next; ++ } ++ ++ compiler->error = SLJIT_ERR_COMPILED; ++ compiler->executable_offset = executable_offset; ++ compiler->executable_size = (sljit_uw)(code_ptr - code) * sizeof(sljit_ins); ++ ++ code = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(code, executable_offset); ++ code_ptr = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(code_ptr, executable_offset); ++ ++ SLJIT_CACHE_FLUSH(code, code_ptr); ++ SLJIT_UPDATE_WX_FLAGS(code, code_ptr, 1); ++ return code; ++} ++ ++SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_has_cpu_feature(sljit_s32 feature_type) ++{ ++ switch (feature_type) { ++ case SLJIT_HAS_FPU: ++#ifdef SLJIT_IS_FPU_AVAILABLE ++ return (SLJIT_IS_FPU_AVAILABLE) != 0; ++#elif defined(__riscv_float_abi_soft) ++ return 0; ++#else ++ return 1; ++#endif /* SLJIT_IS_FPU_AVAILABLE */ ++ case SLJIT_HAS_CLZ: ++ case SLJIT_HAS_CMOV: ++ return 1; ++ default: ++ return 0; ++ } ++} ++ ++/* --------------------------------------------------------------------- */ ++/* Entry, exit */ ++/* --------------------------------------------------------------------- */ ++ ++/* Creates an index in data_transfer_insts array. */ ++#define LOAD_DATA 0x01 ++#define WORD_DATA 0x00 ++#define BYTE_DATA 0x02 ++#define HALF_DATA 0x04 ++#define INT_DATA 0x06 ++#define SIGNED_DATA 0x08 ++/* Separates integer and floating point registers */ ++#define GPR_REG 0x0f ++#define DOUBLE_DATA 0x10 ++#define SINGLE_DATA 0x12 ++ ++#define MEM_MASK 0x1f ++ ++#define ARG_TEST 0x00020 ++#define ALT_KEEP_CACHE 0x00040 ++#define CUMULATIVE_OP 0x00080 ++#define IMM_OP 0x00100 ++#define MOVE_OP 0x00200 ++#define SRC2_IMM 0x00400 ++ ++#define UNUSED_DEST 0x00800 ++#define REG_DEST 0x01000 ++#define REG1_SOURCE 0x02000 ++#define REG2_SOURCE 0x04000 ++#define SLOW_SRC1 0x08000 ++#define SLOW_SRC2 0x10000 ++#define SLOW_DEST 0x20000 ++#define MEM_USE_TMP2 0x40000 ++ ++#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) ++#define STACK_STORE SW ++#define STACK_LOAD LW ++#else ++#define STACK_STORE SD ++#define STACK_LOAD LD ++#endif ++ ++#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) ++#include "sljitNativeRISCV_32.c" ++#else ++#include "sljitNativeRISCV_64.c" ++#endif ++ ++#define STACK_MAX_DISTANCE (-SIMM_MIN) ++ ++static sljit_s32 emit_op_mem(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, sljit_s32 arg, sljit_sw argw); ++ ++SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compiler, ++ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds, ++ sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size) ++{ ++ sljit_s32 i, tmp, offset; ++ sljit_s32 saved_arg_count = SLJIT_KEPT_SAVEDS_COUNT(options); ++ ++ CHECK_ERROR(); ++ CHECK(check_sljit_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size)); ++ set_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size); ++ ++ local_size += GET_SAVED_REGISTERS_SIZE(scratches, saveds - saved_arg_count, 1); ++#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) ++ if (fsaveds > 0 || fscratches >= SLJIT_FIRST_SAVED_FLOAT_REG) { ++ if ((local_size & SSIZE_OF(sw)) != 0) ++ local_size += SSIZE_OF(sw); ++ local_size += GET_SAVED_FLOAT_REGISTERS_SIZE(fscratches, fsaveds, sizeof(sljit_f64)); ++ } ++#else ++ local_size += GET_SAVED_FLOAT_REGISTERS_SIZE(fscratches, fsaveds, f64); ++#endif ++ local_size = (local_size + SLJIT_LOCALS_OFFSET + 15) & ~0xf; ++ compiler->local_size = local_size; ++ ++ if (local_size <= STACK_MAX_DISTANCE) { ++ /* Frequent case. */ ++ FAIL_IF(push_inst(compiler, ADDI | RD(SLJIT_SP) | RS1(SLJIT_SP) | IMM_I(-local_size))); ++ offset = local_size - SSIZE_OF(sw); ++ local_size = 0; ++ } else { ++ FAIL_IF(push_inst(compiler, ADDI | RD(SLJIT_SP) | RS1(SLJIT_SP) | IMM_I(STACK_MAX_DISTANCE))); ++ local_size -= STACK_MAX_DISTANCE; ++ ++ if (local_size > STACK_MAX_DISTANCE) ++ FAIL_IF(load_immediate(compiler, TMP_REG1, local_size, TMP_REG3)); ++ offset = STACK_MAX_DISTANCE - SSIZE_OF(sw); ++ } ++ ++ FAIL_IF(push_imm_s_inst(compiler, STACK_STORE | RS1(SLJIT_SP) | RS2(RETURN_ADDR_REG), offset)); ++ ++ tmp = SLJIT_S0 - saveds; ++ for (i = SLJIT_S0 - saved_arg_count; i > tmp; i--) { ++ offset -= SSIZE_OF(sw); ++ FAIL_IF(push_imm_s_inst(compiler, STACK_STORE | RS1(SLJIT_SP) | RS2(i), offset)); ++ } ++ ++ for (i = scratches; i >= SLJIT_FIRST_SAVED_REG; i--) { ++ offset -= SSIZE_OF(sw); ++ FAIL_IF(push_imm_s_inst(compiler, STACK_STORE | RS1(SLJIT_SP) | RS2(i), offset)); ++ } ++ ++#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) ++ /* This alignment is valid because offset is not used after storing FPU regs. */ ++ if ((offset & SSIZE_OF(sw)) != 0) ++ offset -= SSIZE_OF(sw); ++#endif ++ ++ tmp = SLJIT_FS0 - fsaveds; ++ for (i = SLJIT_FS0; i > tmp; i--) { ++ offset -= SSIZE_OF(f64); ++ FAIL_IF(push_imm_s_inst(compiler, FSD | RS1(SLJIT_SP) | FRS2(i), offset)); ++ } ++ ++ for (i = fscratches; i >= SLJIT_FIRST_SAVED_FLOAT_REG; i--) { ++ offset -= SSIZE_OF(f64); ++ FAIL_IF(push_imm_s_inst(compiler, FSD | RS1(SLJIT_SP) | FRS2(i), offset)); ++ } ++ ++ if (local_size > STACK_MAX_DISTANCE) ++ FAIL_IF(push_inst(compiler, SUB | RD(SLJIT_SP) | RS1(SLJIT_SP) | RS2(TMP_REG1))); ++ else if (local_size > 0) ++ FAIL_IF(push_inst(compiler, ADDI | RD(SLJIT_SP) | RS1(SLJIT_SP) | IMM_I(-local_size))); ++ ++ arg_types >>= SLJIT_DEF_SHIFT; ++ saved_arg_count = 0; ++ tmp = SLJIT_R0; ++ ++ while (arg_types > 0) { ++ if ((arg_types & SLJIT_ARG_MASK) < SLJIT_ARG_TYPE_F64) { ++ if (!(arg_types & SLJIT_ARG_TYPE_SCRATCH_REG)) { ++ FAIL_IF(push_inst(compiler, ADDI | RD(SLJIT_S0 - saved_arg_count) | RS1(tmp) | IMM_I(0))); ++ saved_arg_count++; ++ } ++ tmp++; ++ } ++ ++ arg_types >>= SLJIT_DEF_SHIFT; ++ } ++ ++ return SLJIT_SUCCESS; ++} ++ ++#undef STACK_MAX_DISTANCE ++ ++SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_set_context(struct sljit_compiler *compiler, ++ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds, ++ sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size) ++{ ++ CHECK_ERROR(); ++ CHECK(check_sljit_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size)); ++ set_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size); ++ ++ local_size += GET_SAVED_REGISTERS_SIZE(scratches, saveds - SLJIT_KEPT_SAVEDS_COUNT(options), 1); ++#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) ++ if (fsaveds > 0 || fscratches >= SLJIT_FIRST_SAVED_FLOAT_REG) { ++ if ((local_size & SSIZE_OF(sw)) != 0) ++ local_size += SSIZE_OF(sw); ++ local_size += GET_SAVED_FLOAT_REGISTERS_SIZE(fscratches, fsaveds, f64); ++ } ++#else ++ local_size += GET_SAVED_FLOAT_REGISTERS_SIZE(fscratches, fsaveds, f64); ++#endif ++ compiler->local_size = (local_size + SLJIT_LOCALS_OFFSET + 15) & ~0xf; ++ ++ return SLJIT_SUCCESS; ++} ++ ++#define STACK_MAX_DISTANCE (-SIMM_MIN - 16) ++ ++static sljit_s32 emit_stack_frame_release(struct sljit_compiler *compiler, sljit_s32 is_return_to) ++{ ++ sljit_s32 i, tmp, offset; ++ sljit_s32 local_size = compiler->local_size; ++ ++ if (local_size > STACK_MAX_DISTANCE) { ++ local_size -= STACK_MAX_DISTANCE; ++ ++ if (local_size > STACK_MAX_DISTANCE) { ++ FAIL_IF(load_immediate(compiler, TMP_REG2, local_size, TMP_REG3)); ++ FAIL_IF(push_inst(compiler, ADD | RD(SLJIT_SP) | RS1(SLJIT_SP) | RS2(TMP_REG2))); ++ } else ++ FAIL_IF(push_inst(compiler, ADDI | RD(SLJIT_SP) | RS1(SLJIT_SP) | IMM_I(local_size))); ++ ++ local_size = STACK_MAX_DISTANCE; ++ } ++ ++ SLJIT_ASSERT(local_size > 0); ++ ++ offset = local_size - SSIZE_OF(sw); ++ if (!is_return_to) ++ FAIL_IF(push_inst(compiler, STACK_LOAD | RD(RETURN_ADDR_REG) | RS1(SLJIT_SP) | IMM_I(offset))); ++ ++ tmp = SLJIT_S0 - compiler->saveds; ++ for (i = SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options); i > tmp; i--) { ++ offset -= SSIZE_OF(sw); ++ FAIL_IF(push_inst(compiler, STACK_LOAD | RD(i) | RS1(SLJIT_SP) | IMM_I(offset))); ++ } ++ ++ for (i = compiler->scratches; i >= SLJIT_FIRST_SAVED_REG; i--) { ++ offset -= SSIZE_OF(sw); ++ FAIL_IF(push_inst(compiler, STACK_LOAD | RD(i) | RS1(SLJIT_SP) | IMM_I(offset))); ++ } ++ ++#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) ++ /* This alignment is valid because offset is not used after storing FPU regs. */ ++ if ((offset & SSIZE_OF(sw)) != 0) ++ offset -= SSIZE_OF(sw); ++#endif ++ ++ tmp = SLJIT_FS0 - compiler->fsaveds; ++ for (i = SLJIT_FS0; i > tmp; i--) { ++ offset -= SSIZE_OF(f64); ++ FAIL_IF(push_inst(compiler, FLD | FRD(i) | RS1(SLJIT_SP) | IMM_I(offset))); ++ } ++ ++ for (i = compiler->fscratches; i >= SLJIT_FIRST_SAVED_FLOAT_REG; i--) { ++ offset -= SSIZE_OF(f64); ++ FAIL_IF(push_inst(compiler, FLD | FRD(i) | RS1(SLJIT_SP) | IMM_I(offset))); ++ } ++ ++ return push_inst(compiler, ADDI | RD(SLJIT_SP) | RS1(SLJIT_SP) | IMM_I(local_size)); ++} ++ ++#undef STACK_MAX_DISTANCE ++ ++SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_void(struct sljit_compiler *compiler) ++{ ++ CHECK_ERROR(); ++ CHECK(check_sljit_emit_return_void(compiler)); ++ ++ FAIL_IF(emit_stack_frame_release(compiler, 0)); ++ return push_inst(compiler, JALR | RD(TMP_ZERO) | RS1(RETURN_ADDR_REG) | IMM_I(0)); ++} ++ ++SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_to(struct sljit_compiler *compiler, ++ sljit_s32 src, sljit_sw srcw) ++{ ++ CHECK_ERROR(); ++ CHECK(check_sljit_emit_return_to(compiler, src, srcw)); ++ ++ if (src & SLJIT_MEM) { ++ ADJUST_LOCAL_OFFSET(src, srcw); ++ FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, TMP_REG1, src, srcw)); ++ src = TMP_REG1; ++ srcw = 0; ++ } else if (src >= SLJIT_FIRST_SAVED_REG && src <= (SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options))) { ++ FAIL_IF(push_inst(compiler, ADDI | RD(TMP_REG1) | RS1(src) | IMM_I(0))); ++ src = TMP_REG1; ++ srcw = 0; ++ } ++ ++ FAIL_IF(emit_stack_frame_release(compiler, 1)); ++ ++ SLJIT_SKIP_CHECKS(compiler); ++ return sljit_emit_ijump(compiler, SLJIT_JUMP, src, srcw); ++} ++ ++static SLJIT_INLINE sljit_s32 emit_mov_before_return(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 src, sljit_sw srcw) ++{ ++ /* Return if don't need to do anything. */ ++ if (op == SLJIT_UNUSED) ++ return SLJIT_SUCCESS; ++ ++#if (defined SLJIT_64BIT_ARCHITECTURE && SLJIT_64BIT_ARCHITECTURE) ++ /* At the moment the pointer size is always equal to sljit_sw. May be changed in the future. */ ++ if (src == SLJIT_RETURN_REG && (op == SLJIT_MOV || op == SLJIT_MOV_P)) ++ return SLJIT_SUCCESS; ++#else ++ if (src == SLJIT_RETURN_REG && (op == SLJIT_MOV || op == SLJIT_MOV_U32 || op == SLJIT_MOV_S32 || op == SLJIT_MOV_P)) ++ return SLJIT_SUCCESS; ++#endif ++ ++#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) \ ++ || (defined SLJIT_VERBOSE && SLJIT_VERBOSE) ++ compiler->skip_checks = 1; ++#endif ++ return sljit_emit_op1(compiler, op, SLJIT_RETURN_REG, 0, src, srcw); ++} ++ ++static SLJIT_INLINE sljit_s32 emit_fmov_before_return(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 src, sljit_sw srcw) ++{ ++ if (src == SLJIT_FR0) ++ return SLJIT_SUCCESS; ++ ++ SLJIT_SKIP_CHECKS(compiler); ++ return sljit_emit_fop1(compiler, op, SLJIT_FR0, 0, src, srcw); ++} ++ ++SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 src, sljit_sw srcw) ++{ ++ CHECK_ERROR(); ++ CHECK(check_sljit_emit_return(compiler, op, src, srcw)); ++ ++ if (GET_OPCODE(op) < SLJIT_MOV_F64) { ++ FAIL_IF(emit_mov_before_return(compiler, op, src, srcw)); ++ } else { ++ FAIL_IF(emit_fmov_before_return(compiler, op, src, srcw)); ++ } ++ ++ SLJIT_SKIP_CHECKS(compiler); ++ return sljit_emit_return_void(compiler); ++} ++ ++/* --------------------------------------------------------------------- */ ++/* Operators */ ++/* --------------------------------------------------------------------- */ ++ ++#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) ++#define ARCH_32_64(a, b) a ++#else ++#define ARCH_32_64(a, b) b ++#endif ++ ++static const sljit_ins data_transfer_insts[16 + 4] = { ++/* u w s */ ARCH_32_64(F3(0x2) | OPC(0x23) /* sw */, F3(0x3) | OPC(0x23) /* sd */), ++/* u w l */ ARCH_32_64(F3(0x2) | OPC(0x3) /* lw */, F3(0x3) | OPC(0x3) /* ld */), ++/* u b s */ F3(0x0) | OPC(0x23) /* sb */, ++/* u b l */ F3(0x4) | OPC(0x3) /* lbu */, ++/* u h s */ F3(0x1) | OPC(0x23) /* sh */, ++/* u h l */ F3(0x5) | OPC(0x3) /* lhu */, ++/* u i s */ F3(0x2) | OPC(0x23) /* sw */, ++/* u i l */ ARCH_32_64(F3(0x2) | OPC(0x3) /* lw */, F3(0x6) | OPC(0x3) /* lwu */), ++ ++/* s w s */ ARCH_32_64(F3(0x2) | OPC(0x23) /* sw */, F3(0x3) | OPC(0x23) /* sd */), ++/* s w l */ ARCH_32_64(F3(0x2) | OPC(0x3) /* lw */, F3(0x3) | OPC(0x3) /* ld */), ++/* s b s */ F3(0x0) | OPC(0x23) /* sb */, ++/* s b l */ F3(0x0) | OPC(0x3) /* lb */, ++/* s h s */ F3(0x1) | OPC(0x23) /* sh */, ++/* s h l */ F3(0x1) | OPC(0x3) /* lh */, ++/* s i s */ F3(0x2) | OPC(0x23) /* sw */, ++/* s i l */ F3(0x2) | OPC(0x3) /* lw */, ++ ++/* d s */ F3(0x3) | OPC(0x27) /* fsd */, ++/* d l */ F3(0x3) | OPC(0x7) /* fld */, ++/* s s */ F3(0x2) | OPC(0x27) /* fsw */, ++/* s l */ F3(0x2) | OPC(0x7) /* flw */, ++}; ++ ++#undef ARCH_32_64 ++ ++static sljit_s32 push_mem_inst(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, sljit_s32 base, sljit_sw offset) ++{ ++ sljit_ins ins; ++ ++ SLJIT_ASSERT(FAST_IS_REG(base) && offset <= 0xfff && offset >= SIMM_MIN); ++ ++ ins = data_transfer_insts[flags & MEM_MASK] | RS1(base); ++ if (flags & LOAD_DATA) ++ ins |= ((flags & MEM_MASK) <= GPR_REG ? RD(reg) : FRD(reg)) | IMM_I(offset); ++ else ++ ins |= ((flags & MEM_MASK) <= GPR_REG ? RS2(reg) : FRS2(reg)) | IMM_S(offset); ++ ++ return push_inst(compiler, ins); ++} ++ ++/* Can perform an operation using at most 1 instruction. */ ++static sljit_s32 getput_arg_fast(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, sljit_s32 arg, sljit_sw argw) ++{ ++ SLJIT_ASSERT(arg & SLJIT_MEM); ++ ++ if (!(arg & OFFS_REG_MASK) && argw <= SIMM_MAX && argw >= SIMM_MIN) { ++ /* Works for both absoulte and relative addresses. */ ++ if (SLJIT_UNLIKELY(flags & ARG_TEST)) ++ return 1; ++ ++ FAIL_IF(push_mem_inst(compiler, flags, reg, arg & REG_MASK, argw)); ++ return -1; ++ } ++ return 0; ++} ++ ++#define TO_ARGW_HI(argw) (((argw) & ~0xfff) + (((argw) & 0x800) ? 0x1000 : 0)) ++ ++/* See getput_arg below. ++ Note: can_cache is called only for binary operators. */ ++static sljit_s32 can_cache(sljit_s32 arg, sljit_sw argw, sljit_s32 next_arg, sljit_sw next_argw) ++{ ++ SLJIT_ASSERT((arg & SLJIT_MEM) && (next_arg & SLJIT_MEM)); ++ ++ /* Simple operation except for updates. */ ++ if (arg & OFFS_REG_MASK) { ++ argw &= 0x3; ++ next_argw &= 0x3; ++ if (argw && argw == next_argw && (arg == next_arg || (arg & OFFS_REG_MASK) == (next_arg & OFFS_REG_MASK))) ++ return 1; ++ return 0; ++ } ++ ++ if (arg == next_arg) { ++ if (((next_argw - argw) <= SIMM_MAX && (next_argw - argw) >= SIMM_MIN) ++ || TO_ARGW_HI(argw) == TO_ARGW_HI(next_argw)) ++ return 1; ++ return 0; ++ } ++ ++ return 0; ++} ++ ++/* Emit the necessary instructions. See can_cache above. */ ++static sljit_s32 getput_arg(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, sljit_s32 arg, sljit_sw argw, sljit_s32 next_arg, sljit_sw next_argw) ++{ ++ sljit_s32 base = arg & REG_MASK; ++ sljit_s32 tmp_r = (flags & MEM_USE_TMP2) ? TMP_REG2 : TMP_REG1; ++ sljit_sw offset, argw_hi; ++ ++ SLJIT_ASSERT(arg & SLJIT_MEM); ++ if (!(next_arg & SLJIT_MEM)) { ++ next_arg = 0; ++ next_argw = 0; ++ } ++ ++ if (SLJIT_UNLIKELY(arg & OFFS_REG_MASK)) { ++ argw &= 0x3; ++ ++ /* Using the cache. */ ++ if (argw == compiler->cache_argw) { ++ if (arg == compiler->cache_arg) ++ return push_mem_inst(compiler, flags, reg, TMP_REG3, 0); ++ ++ if ((SLJIT_MEM | (arg & OFFS_REG_MASK)) == compiler->cache_arg) { ++ if (arg == next_arg && argw == (next_argw & 0x3)) { ++ compiler->cache_arg = arg; ++ compiler->cache_argw = argw; ++ FAIL_IF(push_inst(compiler, ADD | RD(TMP_REG3) | RS1(TMP_REG3) | RS2(base))); ++ return push_mem_inst(compiler, flags, reg, TMP_REG3, 0); ++ } ++ FAIL_IF(push_inst(compiler, ADD | RD(tmp_r) | RS1(base) | RS2(TMP_REG3))); ++ return push_mem_inst(compiler, flags, reg, tmp_r, 0); ++ } ++ } ++ ++ if (SLJIT_UNLIKELY(argw)) { ++ compiler->cache_arg = SLJIT_MEM | (arg & OFFS_REG_MASK); ++ compiler->cache_argw = argw; ++ FAIL_IF(push_inst(compiler, SLLI | RD(TMP_REG3) | RS1(OFFS_REG(arg)) | IMM_I(argw))); ++ } ++ ++ if (arg == next_arg && argw == (next_argw & 0x3)) { ++ compiler->cache_arg = arg; ++ compiler->cache_argw = argw; ++ FAIL_IF(push_inst(compiler, ADD | RD(TMP_REG3) | RS1(base) | RS2(!argw ? OFFS_REG(arg) : TMP_REG3))); ++ tmp_r = TMP_REG3; ++ } ++ else ++ FAIL_IF(push_inst(compiler, ADD | RD(tmp_r) | RS1(base) | RS2(!argw ? OFFS_REG(arg) : TMP_REG3))); ++ return push_mem_inst(compiler, flags, reg, tmp_r, 0); ++ } ++ ++ if (compiler->cache_arg == arg && argw - compiler->cache_argw <= SIMM_MAX && argw - compiler->cache_argw >= SIMM_MIN) ++ return push_mem_inst(compiler, flags, reg, TMP_REG3, argw - compiler->cache_argw); ++ ++ if (compiler->cache_arg == SLJIT_MEM && (argw - compiler->cache_argw <= SIMM_MAX) && (argw - compiler->cache_argw >= SIMM_MIN)) { ++ offset = argw - compiler->cache_argw; ++ } else { ++ compiler->cache_arg = SLJIT_MEM; ++ ++ argw_hi = TO_ARGW_HI(argw); ++ ++ if (next_arg && next_argw - argw <= SIMM_MAX && next_argw - argw >= SIMM_MIN && argw_hi != TO_ARGW_HI(next_argw)) { ++ FAIL_IF(load_immediate(compiler, TMP_REG3, argw, tmp_r)); ++ compiler->cache_argw = argw; ++ offset = 0; ++ } else { ++ FAIL_IF(load_immediate(compiler, TMP_REG3, argw_hi, tmp_r)); ++ compiler->cache_argw = argw_hi; ++ offset = argw & 0xfff; ++ argw = argw_hi; ++ } ++ } ++ ++ if (!base) ++ return push_mem_inst(compiler, flags, reg, TMP_REG3, offset); ++ ++ if (arg == next_arg && next_argw - argw <= SIMM_MAX && next_argw - argw >= SIMM_MIN) { ++ compiler->cache_arg = arg; ++ FAIL_IF(push_inst(compiler, ADD | RD(TMP_REG3) | RS1(TMP_REG3) | RS2(base))); ++ return push_mem_inst(compiler, flags, reg, TMP_REG3, offset); ++ } ++ ++ FAIL_IF(push_inst(compiler, ADD | RD(tmp_r) | RS1(TMP_REG3) | RS2(base))); ++ return push_mem_inst(compiler, flags, reg, tmp_r, offset); ++} ++ ++static sljit_s32 emit_op_mem(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, sljit_s32 arg, sljit_sw argw) ++{ ++ sljit_s32 base = arg & REG_MASK; ++ sljit_s32 tmp_r = TMP_REG1; ++ ++ if (getput_arg_fast(compiler, flags, reg, arg, argw)) ++ return compiler->error; ++ ++ if ((flags & MEM_MASK) <= GPR_REG && (flags & LOAD_DATA)) ++ tmp_r = reg; ++ ++ if (SLJIT_UNLIKELY(arg & OFFS_REG_MASK)) { ++ argw &= 0x3; ++ ++ if (SLJIT_UNLIKELY(argw)) { ++ FAIL_IF(push_inst(compiler, SLLI | RD(tmp_r) | RS1(OFFS_REG(arg)) | IMM_I(argw))); ++ FAIL_IF(push_inst(compiler, ADD | RD(tmp_r) | RS1(tmp_r) | RS2(base))); ++ } ++ else ++ FAIL_IF(push_inst(compiler, ADD | RD(tmp_r) | RS1(base) | RS2(OFFS_REG(arg)))); ++ ++ argw = 0; ++ } else { ++ FAIL_IF(load_immediate(compiler, tmp_r, TO_ARGW_HI(argw), TMP_REG3)); ++ ++ if (base != 0) ++ FAIL_IF(push_inst(compiler, ADD | RD(tmp_r) | RS1(tmp_r) | RS2(base))); ++ } ++ ++ return push_mem_inst(compiler, flags, reg, tmp_r, argw & 0xfff); ++} ++ ++static SLJIT_INLINE sljit_s32 emit_op_mem2(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, sljit_s32 arg1, sljit_sw arg1w, sljit_s32 arg2, sljit_sw arg2w) ++{ ++ if (getput_arg_fast(compiler, flags, reg, arg1, arg1w)) ++ return compiler->error; ++ return getput_arg(compiler, flags, reg, arg1, arg1w, arg2, arg2w); ++} ++ ++#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) ++#define WORD 0 ++#define IMM_EXTEND(v) (IMM_I(v)) ++#else /* !SLJIT_CONFIG_RISCV_32 */ ++#define WORD word ++#define IMM_EXTEND(v) (IMM_I((op & SLJIT_I32_OP) ? (v) : (32 + (v)))) ++#endif /* SLJIT_CONFIG_RISCV_32 */ ++ ++static sljit_s32 emit_clz_ctz(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 dst, sljit_sw src) ++{ ++ sljit_s32 is_clz = (GET_OPCODE(op) == SLJIT_CLZ); ++#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64) ++ sljit_ins word = (sljit_ins)(op & SLJIT_I32_OP) >> 5; ++ sljit_ins word_size = (op & SLJIT_I32_OP) ? 32 : 64; ++#else /* !SLJIT_CONFIG_RISCV_64 */ ++ sljit_ins word_size = 32; ++#endif /* SLJIT_CONFIG_RISCV_64 */ ++ ++ SLJIT_ASSERT(WORD == 0 || WORD == 0x8); ++ ++ /* The OTHER_FLAG is the counter. */ ++ FAIL_IF(push_inst(compiler, ADDI | WORD | RD(OTHER_FLAG) | RS1(TMP_ZERO) | IMM_I(word_size))); ++ ++ /* The TMP_REG2 is the next value. */ ++ if (src != TMP_REG2) ++ FAIL_IF(push_inst(compiler, ADDI | WORD | RD(TMP_REG2) | RS1(src) | IMM_I(0))); ++ ++ FAIL_IF(push_inst(compiler, BEQ | RS1(TMP_REG2) | RS2(TMP_ZERO) | ((sljit_ins)((is_clz ? 4 : 5) * SSIZE_OF(ins)) << 7) | ((sljit_ins)(8 * SSIZE_OF(ins)) << 20))); ++ ++ FAIL_IF(push_inst(compiler, ADDI | WORD | RD(OTHER_FLAG) | RS1(TMP_ZERO) | IMM_I(0))); ++ if (!is_clz) { ++ FAIL_IF(push_inst(compiler, ANDI | RD(TMP_REG1) | RS1(TMP_REG2) | IMM_I(1))); ++ FAIL_IF(push_inst(compiler, BNE | RS1(TMP_REG1) | RS2(TMP_ZERO) | ((sljit_ins)(2 * SSIZE_OF(ins)) << 7) | ((sljit_ins)(8 * SSIZE_OF(ins)) << 20))); ++ } else ++ FAIL_IF(push_inst(compiler, BLT | RS1(TMP_REG2) | RS2(TMP_ZERO) | ((sljit_ins)(2 * SSIZE_OF(ins)) << 7) | ((sljit_ins)(8 * SSIZE_OF(ins)) << 20))); ++ ++ /* The TMP_REG1 is the next shift. */ ++ FAIL_IF(push_inst(compiler, ADDI | WORD | RD(TMP_REG1) | RS1(TMP_ZERO) | IMM_I(word_size))); ++ ++ FAIL_IF(push_inst(compiler, ADDI | WORD | RD(EQUAL_FLAG) | RS1(TMP_REG2) | IMM_I(0))); ++ FAIL_IF(push_inst(compiler, SRLI | WORD | RD(TMP_REG1) | RS1(TMP_REG1) | IMM_I(1))); ++ ++ FAIL_IF(push_inst(compiler, (is_clz ? SRL : SLL) | WORD | RD(TMP_REG2) | RS1(EQUAL_FLAG) | RS2(TMP_REG1))); ++ FAIL_IF(push_inst(compiler, BNE | RS1(TMP_REG2) | RS2(TMP_ZERO) | ((sljit_ins)0xfe000e80 - ((2 * SSIZE_OF(ins)) << 7)))); ++ FAIL_IF(push_inst(compiler, ADDI | WORD | RD(TMP_REG2) | RS1(TMP_REG1) | IMM_I(-1))); ++ FAIL_IF(push_inst(compiler, (is_clz ? SRL : SLL) | WORD | RD(TMP_REG2) | RS1(EQUAL_FLAG) | RS2(TMP_REG2))); ++ FAIL_IF(push_inst(compiler, OR | RD(OTHER_FLAG) | RS1(OTHER_FLAG) | RS2(TMP_REG1))); ++ FAIL_IF(push_inst(compiler, BEQ | RS1(TMP_REG2) | RS2(TMP_ZERO) | ((sljit_ins)0xfe000e80 - ((5 * SSIZE_OF(ins)) << 7)))); ++ ++ return push_inst(compiler, ADDI | WORD | RD(dst) | RS1(OTHER_FLAG) | IMM_I(0)); ++} ++ ++#define EMIT_LOGICAL(op_imm, op_reg) \ ++ if (flags & SRC2_IMM) { \ ++ if (op & SLJIT_SET_Z) \ ++ FAIL_IF(push_inst(compiler, op_imm | RD(EQUAL_FLAG) | RS1(src1) | IMM_I(src2))); \ ++ if (!(flags & UNUSED_DEST)) \ ++ FAIL_IF(push_inst(compiler, op_imm | RD(dst) | RS1(src1) | IMM_I(src2))); \ ++ } \ ++ else { \ ++ if (op & SLJIT_SET_Z) \ ++ FAIL_IF(push_inst(compiler, op_reg | RD(EQUAL_FLAG) | RS1(src1) | RS2(src2))); \ ++ if (!(flags & UNUSED_DEST)) \ ++ FAIL_IF(push_inst(compiler, op_reg | RD(dst) | RS1(src1) | RS2(src2))); \ ++ } ++ ++#define EMIT_SHIFT(imm, reg) \ ++ op_imm = (imm); \ ++ op_reg = (reg); ++ ++static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 flags, ++ sljit_s32 dst, sljit_s32 src1, sljit_sw src2) ++{ ++ sljit_s32 is_overflow, is_carry, carry_src_r, is_handled, reg; ++ sljit_ins op_imm, op_reg; ++#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64) ++ sljit_ins word = (sljit_ins)(op & SLJIT_I32_OP) >> 5; ++#endif /* SLJIT_CONFIG_RISCV_64 */ ++ ++ SLJIT_ASSERT(WORD == 0 || WORD == 0x8); ++ ++ switch (GET_OPCODE(op)) { ++ case SLJIT_MOV: ++ SLJIT_ASSERT(src1 == TMP_ZERO && !(flags & SRC2_IMM)); ++ if (dst != src2) ++ return push_inst(compiler, ADDI | RD(dst) | RS1(src2) | IMM_I(0)); ++ return SLJIT_SUCCESS; ++ ++ case SLJIT_MOV_U8: ++ SLJIT_ASSERT(src1 == TMP_ZERO && !(flags & SRC2_IMM)); ++ if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) ++ return push_inst(compiler, ANDI | RD(dst) | RS1(src2) | IMM_I(0xff)); ++ SLJIT_ASSERT(dst == src2); ++ return SLJIT_SUCCESS; ++ ++ case SLJIT_MOV_S8: ++ SLJIT_ASSERT(src1 == TMP_ZERO && !(flags & SRC2_IMM)); ++ if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) { ++ FAIL_IF(push_inst(compiler, SLLI | WORD | RD(dst) | RS1(src2) | IMM_EXTEND(24))); ++ return push_inst(compiler, SRAI | WORD | RD(dst) | RS1(dst) | IMM_EXTEND(24)); ++ } ++ SLJIT_ASSERT(dst == src2); ++ return SLJIT_SUCCESS; ++ ++ case SLJIT_MOV_U16: ++ SLJIT_ASSERT(src1 == TMP_ZERO && !(flags & SRC2_IMM)); ++ if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) { ++ FAIL_IF(push_inst(compiler, SLLI | WORD | RD(dst) | RS1(src2) | IMM_EXTEND(16))); ++ return push_inst(compiler, SRLI | WORD | RD(dst) | RS1(dst) | IMM_EXTEND(16)); ++ } ++ SLJIT_ASSERT(dst == src2); ++ return SLJIT_SUCCESS; ++ ++ case SLJIT_MOV_S16: ++ SLJIT_ASSERT(src1 == TMP_ZERO && !(flags & SRC2_IMM)); ++ if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) { ++ FAIL_IF(push_inst(compiler, SLLI | WORD | RD(dst) | RS1(src2) | IMM_EXTEND(16))); ++ return push_inst(compiler, SRAI | WORD | RD(dst) | RS1(dst) | IMM_EXTEND(16)); ++ } ++ SLJIT_ASSERT(dst == src2); ++ return SLJIT_SUCCESS; ++ ++#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64) ++ case SLJIT_MOV_U32: ++ SLJIT_ASSERT(src1 == TMP_ZERO && !(flags & SRC2_IMM)); ++ if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) { ++ FAIL_IF(push_inst(compiler, SLLI | RD(dst) | RS1(src2) | IMM_I(32))); ++ return push_inst(compiler, SRLI | RD(dst) | RS1(dst) | IMM_I(32)); ++ } ++ SLJIT_ASSERT(dst == src2); ++ return SLJIT_SUCCESS; ++ ++ case SLJIT_MOV_S32: ++ SLJIT_ASSERT(src1 == TMP_ZERO && !(flags & SRC2_IMM)); ++ if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) ++ return push_inst(compiler, ADDI | 0x8 | RD(dst) | RS1(src2) | IMM_I(0)); ++ SLJIT_ASSERT(dst == src2); ++ return SLJIT_SUCCESS; ++#endif /* SLJIT_CONFIG_RISCV_64 */ ++ ++ case SLJIT_CLZ: ++ case SLJIT_NEG: ++ SLJIT_ASSERT(src1 == TMP_ZERO && !(flags & SRC2_IMM)); ++ return emit_clz_ctz(compiler, op, dst, src2); ++ ++ case SLJIT_ADD: ++ /* Overflow computation (both add and sub): overflow = src1_sign ^ src2_sign ^ result_sign ^ carry_flag */ ++ is_overflow = GET_FLAG_TYPE(op) == SLJIT_OVERFLOW; ++ carry_src_r = GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY); ++ ++ if (flags & SRC2_IMM) { ++ if (is_overflow) { ++ if (src2 >= 0) ++ FAIL_IF(push_inst(compiler, ADDI | RD(EQUAL_FLAG) | RS1(src1) | IMM_I(0))); ++ else ++ FAIL_IF(push_inst(compiler, XORI | RD(EQUAL_FLAG) | RS1(src1) | IMM_I(-1))); ++ } ++ else if (op & SLJIT_SET_Z) ++ FAIL_IF(push_inst(compiler, ADDI | WORD | RD(EQUAL_FLAG) | RS1(src1) | IMM_I(src2))); ++ ++ /* Only the zero flag is needed. */ ++ if (!(flags & UNUSED_DEST) || (op & VARIABLE_FLAG_MASK)) ++ FAIL_IF(push_inst(compiler, ADDI | WORD | RD(dst) | RS1(src1) | IMM_I(src2))); ++ } ++ else { ++ if (is_overflow) ++ FAIL_IF(push_inst(compiler, XOR | RD(EQUAL_FLAG) | RS1(src1) | RS2(src2))); ++ else if (op & SLJIT_SET_Z) ++ FAIL_IF(push_inst(compiler, ADD | WORD | RD(EQUAL_FLAG) | RS1(src1) | RS2(src2))); ++ ++ if (is_overflow || carry_src_r != 0) { ++ if (src1 != dst) ++ carry_src_r = (sljit_s32)src1; ++ else if (src2 != dst) ++ carry_src_r = (sljit_s32)src2; ++ else { ++ FAIL_IF(push_inst(compiler, ADDI | RD(OTHER_FLAG) | RS1(src1) | IMM_I(0))); ++ carry_src_r = OTHER_FLAG; ++ } ++ } ++ ++ /* Only the zero flag is needed. */ ++ if (!(flags & UNUSED_DEST) || (op & VARIABLE_FLAG_MASK)) ++ FAIL_IF(push_inst(compiler, ADD | WORD | RD(dst) | RS1(src1) | RS2(src2))); ++ } ++ ++ /* Carry is zero if a + b >= a or a + b >= b, otherwise it is 1. */ ++ if (is_overflow || carry_src_r != 0) { ++ if (flags & SRC2_IMM) ++ FAIL_IF(push_inst(compiler, SLTUI | RD(OTHER_FLAG) | RS1(dst) | IMM_I(src2))); ++ else ++ FAIL_IF(push_inst(compiler, SLTU | RD(OTHER_FLAG) | RS1(dst) | RS2(carry_src_r))); ++ } ++ ++ if (!is_overflow) ++ return SLJIT_SUCCESS; ++ ++ FAIL_IF(push_inst(compiler, XOR | RD(TMP_REG1) | RS1(dst) | RS2(EQUAL_FLAG))); ++ if (op & SLJIT_SET_Z) ++ FAIL_IF(push_inst(compiler, ADDI | RD(EQUAL_FLAG) | RS1(dst) | IMM_I(0))); ++ FAIL_IF(push_inst(compiler, SRLI | WORD | RD(TMP_REG1) | RS1(TMP_REG1) | IMM_EXTEND(31))); ++ return push_inst(compiler, XOR | RD(OTHER_FLAG) | RS1(TMP_REG1) | RS2(OTHER_FLAG)); ++ ++ case SLJIT_ADDC: ++ carry_src_r = GET_FLAG_TYPE(op) == SLJIT_SET_CARRY; ++ ++ if (flags & SRC2_IMM) { ++ FAIL_IF(push_inst(compiler, ADDI | WORD | RD(dst) | RS1(src1) | IMM_I(src2))); ++ } else { ++ if (carry_src_r != 0) { ++ if (src1 != dst) ++ carry_src_r = (sljit_s32)src1; ++ else if (src2 != dst) ++ carry_src_r = (sljit_s32)src2; ++ else { ++ FAIL_IF(push_inst(compiler, ADDI | RD(EQUAL_FLAG) | RS1(src1) | IMM_I(0))); ++ carry_src_r = EQUAL_FLAG; ++ } ++ } ++ ++ FAIL_IF(push_inst(compiler, ADD | WORD | RD(dst) | RS1(src1) | RS2(src2))); ++ } ++ ++ /* Carry is zero if a + b >= a or a + b >= b, otherwise it is 1. */ ++ if (carry_src_r != 0) { ++ if (flags & SRC2_IMM) ++ FAIL_IF(push_inst(compiler, SLTUI | RD(EQUAL_FLAG) | RS1(dst) | IMM_I(src2))); ++ else ++ FAIL_IF(push_inst(compiler, SLTU | RD(EQUAL_FLAG) | RS1(dst) | RS2(carry_src_r))); ++ } ++ ++ FAIL_IF(push_inst(compiler, ADD | WORD | RD(dst) | RS1(dst) | RS2(OTHER_FLAG))); ++ ++ if (carry_src_r == 0) ++ return SLJIT_SUCCESS; ++ ++ /* Set ULESS_FLAG (dst == 0) && (OTHER_FLAG == 1). */ ++ FAIL_IF(push_inst(compiler, SLTU | RD(OTHER_FLAG) | RS1(dst) | RS2(OTHER_FLAG))); ++ /* Set carry flag. */ ++ return push_inst(compiler, OR | RD(OTHER_FLAG) | RS1(OTHER_FLAG) | RS2(EQUAL_FLAG)); ++ ++ case SLJIT_SUB: ++ if ((flags & SRC2_IMM) && src2 == SIMM_MIN) { ++ FAIL_IF(push_inst(compiler, ADDI | RD(TMP_REG2) | RS1(TMP_ZERO) | IMM_I(src2))); ++ src2 = TMP_REG2; ++ flags &= ~SRC2_IMM; ++ } ++ ++ is_handled = 0; ++ ++ if (flags & SRC2_IMM) { ++ if (GET_FLAG_TYPE(op) == SLJIT_LESS) { ++ FAIL_IF(push_inst(compiler, SLTUI | RD(OTHER_FLAG) | RS1(src1) | IMM_I(src2))); ++ is_handled = 1; ++ } ++ else if (GET_FLAG_TYPE(op) == SLJIT_SIG_LESS) { ++ FAIL_IF(push_inst(compiler, SLTI | RD(OTHER_FLAG) | RS1(src1) | IMM_I(src2))); ++ is_handled = 1; ++ } ++ } ++ ++ if (!is_handled && GET_FLAG_TYPE(op) >= SLJIT_LESS && GET_FLAG_TYPE(op) <= SLJIT_SIG_LESS_EQUAL) { ++ is_handled = 1; ++ ++ if (flags & SRC2_IMM) { ++ reg = (src1 == TMP_REG1) ? TMP_REG2 : TMP_REG1; ++ FAIL_IF(push_inst(compiler, ADDI | RD(reg) | RS1(TMP_ZERO) | IMM_I(src2))); ++ src2 = reg; ++ flags &= ~SRC2_IMM; ++ } ++ ++ switch (GET_FLAG_TYPE(op)) { ++ case SLJIT_LESS: ++ case SLJIT_GREATER_EQUAL: ++ FAIL_IF(push_inst(compiler, SLTU | RD(OTHER_FLAG) | RS1(src1) | RS2(src2))); ++ break; ++ case SLJIT_GREATER: ++ case SLJIT_LESS_EQUAL: ++ FAIL_IF(push_inst(compiler, SLTU | RD(OTHER_FLAG) | RS1(src2) | RS2(src1))); ++ break; ++ case SLJIT_SIG_LESS: ++ case SLJIT_SIG_GREATER_EQUAL: ++ FAIL_IF(push_inst(compiler, SLT | RD(OTHER_FLAG) | RS1(src1) | RS2(src2))); ++ break; ++ case SLJIT_SIG_GREATER: ++ case SLJIT_SIG_LESS_EQUAL: ++ FAIL_IF(push_inst(compiler, SLT | RD(OTHER_FLAG) | RS1(src2) | RS2(src1))); ++ break; ++ } ++ } ++ ++ if (is_handled) { ++ if (flags & SRC2_IMM) { ++ if (op & SLJIT_SET_Z) ++ FAIL_IF(push_inst(compiler, ADDI | WORD | RD(EQUAL_FLAG) | RS1(src1) | IMM_I(-src2))); ++ if (!(flags & UNUSED_DEST)) ++ return push_inst(compiler, ADDI | WORD | RD(dst) | RS1(src1) | IMM_I(-src2)); ++ } ++ else { ++ if (op & SLJIT_SET_Z) ++ FAIL_IF(push_inst(compiler, SUB | WORD | RD(EQUAL_FLAG) | RS1(src1) | RS2(src2))); ++ if (!(flags & UNUSED_DEST)) ++ return push_inst(compiler, SUB | WORD | RD(dst) | RS1(src1) | RS2(src2)); ++ } ++ return SLJIT_SUCCESS; ++ } ++ ++ is_overflow = GET_FLAG_TYPE(op) == SLJIT_OVERFLOW; ++ is_carry = GET_FLAG_TYPE(op) == SLJIT_SET_CARRY; ++ ++ if (flags & SRC2_IMM) { ++ if (is_overflow) { ++ if (src2 >= 0) ++ FAIL_IF(push_inst(compiler, ADDI | RD(EQUAL_FLAG) | RS1(src1) | IMM_I(0))); ++ else ++ FAIL_IF(push_inst(compiler, XORI | RD(EQUAL_FLAG) | RS1(src1) | IMM_I(-1))); ++ } ++ else if (op & SLJIT_SET_Z) ++ FAIL_IF(push_inst(compiler, ADDI | WORD | RD(EQUAL_FLAG) | RS1(src1) | IMM_I(-src2))); ++ ++ if (is_overflow || is_carry) ++ FAIL_IF(push_inst(compiler, SLTUI | RD(OTHER_FLAG) | RS1(src1) | IMM_I(src2))); ++ ++ /* Only the zero flag is needed. */ ++ if (!(flags & UNUSED_DEST) || (op & VARIABLE_FLAG_MASK)) ++ FAIL_IF(push_inst(compiler, ADDI | WORD | RD(dst) | RS1(src1) | IMM_I(-src2))); ++ } ++ else { ++ if (is_overflow) ++ FAIL_IF(push_inst(compiler, XOR | RD(EQUAL_FLAG) | RS1(src1) | RS2(src2))); ++ else if (op & SLJIT_SET_Z) ++ FAIL_IF(push_inst(compiler, SUB | WORD | RD(EQUAL_FLAG) | RS1(src1) | RS2(src2))); ++ ++ if (is_overflow || is_carry) ++ FAIL_IF(push_inst(compiler, SLTU | RD(OTHER_FLAG) | RS1(src1) | RS2(src2))); ++ ++ /* Only the zero flag is needed. */ ++ if (!(flags & UNUSED_DEST) || (op & VARIABLE_FLAG_MASK)) ++ FAIL_IF(push_inst(compiler, SUB | WORD | RD(dst) | RS1(src1) | RS2(src2))); ++ } ++ ++ if (!is_overflow) ++ return SLJIT_SUCCESS; ++ ++ FAIL_IF(push_inst(compiler, XOR | RD(TMP_REG1) | RS1(dst) | RS2(EQUAL_FLAG))); ++ if (op & SLJIT_SET_Z) ++ FAIL_IF(push_inst(compiler, ADDI | RD(EQUAL_FLAG) | RS1(dst) | IMM_I(0))); ++ FAIL_IF(push_inst(compiler, SRLI | WORD | RD(TMP_REG1) | RS1(TMP_REG1) | IMM_EXTEND(31))); ++ return push_inst(compiler, XOR | RD(OTHER_FLAG) | RS1(TMP_REG1) | RS2(OTHER_FLAG)); ++ ++ case SLJIT_SUBC: ++ if ((flags & SRC2_IMM) && src2 == SIMM_MIN) { ++ FAIL_IF(push_inst(compiler, ADDI | RD(TMP_REG2) | RS1(TMP_ZERO) | IMM_I(src2))); ++ src2 = TMP_REG2; ++ flags &= ~SRC2_IMM; ++ } ++ ++ is_carry = GET_FLAG_TYPE(op) == SLJIT_SET_CARRY; ++ ++ if (flags & SRC2_IMM) { ++ if (is_carry) ++ FAIL_IF(push_inst(compiler, SLTUI | RD(EQUAL_FLAG) | RS1(src1) | IMM_I(src2))); ++ ++ FAIL_IF(push_inst(compiler, ADDI | WORD | RD(dst) | RS1(src1) | IMM_I(-src2))); ++ } ++ else { ++ if (is_carry) ++ FAIL_IF(push_inst(compiler, SLTU | RD(EQUAL_FLAG) | RS1(src1) | RS2(src2))); ++ ++ FAIL_IF(push_inst(compiler, SUB | WORD | RD(dst) | RS1(src1) | RS2(src2))); ++ } ++ ++ if (is_carry) ++ FAIL_IF(push_inst(compiler, SLTU | RD(TMP_REG1) | RS1(dst) | RS2(OTHER_FLAG))); ++ ++ FAIL_IF(push_inst(compiler, SUB | WORD | RD(dst) | RS1(dst) | RS2(OTHER_FLAG))); ++ ++ if (!is_carry) ++ return SLJIT_SUCCESS; ++ ++ return push_inst(compiler, OR | RD(OTHER_FLAG) | RS1(EQUAL_FLAG) | RS2(TMP_REG1)); ++ ++ case SLJIT_MUL: ++ SLJIT_ASSERT(!(flags & SRC2_IMM)); ++ ++ if (GET_FLAG_TYPE(op) != SLJIT_OVERFLOW) ++ return push_inst(compiler, MUL | WORD | RD(dst) | RS1(src1) | RS2(src2)); ++ ++#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64) ++ if (word) { ++ FAIL_IF(push_inst(compiler, MUL | RD(OTHER_FLAG) | RS1(src1) | RS2(src2))); ++ FAIL_IF(push_inst(compiler, MUL | 0x8 | RD(dst) | RS1(src1) | RS2(src2))); ++ return push_inst(compiler, SUB | RD(OTHER_FLAG) | RS1(dst) | RS2(OTHER_FLAG)); ++ } ++#endif /* SLJIT_CONFIG_RISCV_64 */ ++ ++ FAIL_IF(push_inst(compiler, MULH | RD(EQUAL_FLAG) | RS1(src1) | RS2(src2))); ++ FAIL_IF(push_inst(compiler, MUL | RD(dst) | RS1(src1) | RS2(src2))); ++#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) ++ FAIL_IF(push_inst(compiler, SRAI | RD(OTHER_FLAG) | RS1(dst) | IMM_I(31))); ++#else /* !SLJIT_CONFIG_RISCV_32 */ ++ FAIL_IF(push_inst(compiler, SRAI | RD(OTHER_FLAG) | RS1(dst) | IMM_I(63))); ++#endif /* SLJIT_CONFIG_RISCV_32 */ ++ return push_inst(compiler, SUB | RD(OTHER_FLAG) | RS1(EQUAL_FLAG) | RS2(OTHER_FLAG)); ++ ++ case SLJIT_AND: ++ EMIT_LOGICAL(ANDI, AND); ++ return SLJIT_SUCCESS; ++ ++ case SLJIT_OR: ++ EMIT_LOGICAL(ORI, OR); ++ return SLJIT_SUCCESS; ++ ++ case SLJIT_XOR: ++ EMIT_LOGICAL(XORI, XOR); ++ return SLJIT_SUCCESS; ++ ++ case SLJIT_SHL: ++ EMIT_SHIFT(SLLI, SLL); ++ break; ++ ++ case SLJIT_LSHR: ++ EMIT_SHIFT(SRLI, SRL); ++ break; ++ ++ case SLJIT_ASHR: ++ EMIT_SHIFT(SRAI, SRA); ++ break; ++ ++ default: ++ SLJIT_UNREACHABLE(); ++ return SLJIT_SUCCESS; ++ } ++ ++ if (flags & SRC2_IMM) { ++ if (op & SLJIT_SET_Z) ++ FAIL_IF(push_inst(compiler, op_imm | WORD | RD(EQUAL_FLAG) | RS1(src1) | IMM_I(src2))); ++ ++ if (flags & UNUSED_DEST) ++ return SLJIT_SUCCESS; ++ return push_inst(compiler, op_imm | WORD | RD(dst) | RS1(src1) | IMM_I(src2)); ++ } ++ ++ if (op & SLJIT_SET_Z) ++ FAIL_IF(push_inst(compiler, op_reg | WORD | RD(EQUAL_FLAG) | RS1(src1) | RS2(src2))); ++ ++ if (flags & UNUSED_DEST) ++ return SLJIT_SUCCESS; ++ return push_inst(compiler, op_reg | WORD | RD(dst) | RS1(src1) | RS2(src2)); ++} ++ ++#undef IMM_EXTEND ++ ++static sljit_s32 emit_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 flags, ++ sljit_s32 dst, sljit_sw dstw, ++ sljit_s32 src1, sljit_sw src1w, ++ sljit_s32 src2, sljit_sw src2w) ++{ ++ /* arg1 goes to TMP_REG1 or src reg ++ arg2 goes to TMP_REG2, imm or src reg ++ TMP_REG3 can be used for caching ++ result goes to TMP_REG2, so put result can use TMP_REG1 and TMP_REG3. */ ++ sljit_s32 dst_r = TMP_REG2; ++ sljit_s32 src1_r; ++ sljit_sw src2_r = 0; ++ sljit_s32 src2_tmp_reg = (GET_OPCODE(op) >= SLJIT_OP2_BASE && FAST_IS_REG(src1)) ? TMP_REG1 : TMP_REG2; ++ ++ if (!(flags & ALT_KEEP_CACHE)) { ++ compiler->cache_arg = 0; ++ compiler->cache_argw = 0; ++ } ++ ++ if (dst == 0) { ++ SLJIT_ASSERT(HAS_FLAGS(op)); ++ flags |= UNUSED_DEST; ++ dst = TMP_REG2; ++ } ++ else if (FAST_IS_REG(dst)) { ++ dst_r = dst; ++ flags |= REG_DEST; ++ if (flags & MOVE_OP) ++ src2_tmp_reg = dst_r; ++ } ++ else if ((dst & SLJIT_MEM) && !getput_arg_fast(compiler, flags | ARG_TEST, TMP_REG1, dst, dstw)) ++ flags |= SLOW_DEST; ++ ++ if (flags & IMM_OP) { ++ if (src2 == SLJIT_IMM && src2w != 0 && src2w <= SIMM_MAX && src2w >= SIMM_MIN) { ++ flags |= SRC2_IMM; ++ src2_r = src2w; ++ } ++ else if ((flags & CUMULATIVE_OP) && src1 == SLJIT_IMM && src1w != 0 && src1w <= SIMM_MAX && src1w >= SIMM_MIN) { ++ flags |= SRC2_IMM; ++ src2_r = src1w; ++ ++ /* And swap arguments. */ ++ src1 = src2; ++ src1w = src2w; ++ src2 = SLJIT_IMM; ++ /* src2w = src2_r unneeded. */ ++ } ++ } ++ ++ /* Source 1. */ ++ if (FAST_IS_REG(src1)) { ++ src1_r = src1; ++ flags |= REG1_SOURCE; ++ } else if (src1 == SLJIT_IMM) { ++ if (src1w) { ++ FAIL_IF(load_immediate(compiler, TMP_REG1, src1w, TMP_REG3)); ++ src1_r = TMP_REG1; ++ } ++ else ++ src1_r = TMP_ZERO; ++ } else { ++ if (getput_arg_fast(compiler, flags | LOAD_DATA, TMP_REG1, src1, src1w)) ++ FAIL_IF(compiler->error); ++ else ++ flags |= SLOW_SRC1; ++ src1_r = TMP_REG1; ++ } ++ ++ /* Source 2. */ ++ if (FAST_IS_REG(src2)) { ++ src2_r = src2; ++ flags |= REG2_SOURCE; ++ if ((flags & (REG_DEST | MOVE_OP)) == MOVE_OP) ++ dst_r = (sljit_s32)src2_r; ++ } else if (src2 == SLJIT_IMM) { ++ if (!(flags & SRC2_IMM)) { ++ if (src2w) { ++ FAIL_IF(load_immediate(compiler, src2_tmp_reg, src2w, TMP_REG3)); ++ src2_r = src2_tmp_reg; ++ } else { ++ src2_r = TMP_ZERO; ++ if (flags & MOVE_OP) { ++ if (dst & SLJIT_MEM) ++ dst_r = 0; ++ else ++ op = SLJIT_MOV; ++ } ++ } ++ } ++ } else { ++ if (getput_arg_fast(compiler, flags | LOAD_DATA, src2_tmp_reg, src2, src2w)) ++ FAIL_IF(compiler->error); ++ else ++ flags |= SLOW_SRC2; ++ src2_r = src2_tmp_reg; ++ } ++ ++ if ((flags & (SLOW_SRC1 | SLOW_SRC2)) == (SLOW_SRC1 | SLOW_SRC2)) { ++ SLJIT_ASSERT(src2_r == TMP_REG2); ++ if ((flags & SLOW_DEST) && !can_cache(src2, src2w, src1, src1w) && can_cache(src2, src2w, dst, dstw)) { ++ FAIL_IF(getput_arg(compiler, flags | LOAD_DATA, TMP_REG1, src1, src1w, src2, src2w)); ++ FAIL_IF(getput_arg(compiler, flags | LOAD_DATA | MEM_USE_TMP2, TMP_REG2, src2, src2w, dst, dstw)); ++ } else { ++ FAIL_IF(getput_arg(compiler, flags | LOAD_DATA, TMP_REG2, src2, src2w, src1, src1w)); ++ FAIL_IF(getput_arg(compiler, flags | LOAD_DATA, TMP_REG1, src1, src1w, dst, dstw)); ++ } ++ } ++ else if (flags & SLOW_SRC1) ++ FAIL_IF(getput_arg(compiler, flags | LOAD_DATA, TMP_REG1, src1, src1w, dst, dstw)); ++ else if (flags & SLOW_SRC2) ++ FAIL_IF(getput_arg(compiler, flags | LOAD_DATA | ((src1_r == TMP_REG1) ? MEM_USE_TMP2 : 0), src2_tmp_reg, src2, src2w, dst, dstw)); ++ ++ FAIL_IF(emit_single_op(compiler, op, flags, dst_r, src1_r, src2_r)); ++ ++ if (dst & SLJIT_MEM) { ++ if (!(flags & SLOW_DEST)) { ++ getput_arg_fast(compiler, flags, dst_r, dst, dstw); ++ return compiler->error; ++ } ++ return getput_arg(compiler, flags, dst_r, dst, dstw, 0, 0); ++ } ++ ++ return SLJIT_SUCCESS; ++} ++ ++SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op0(struct sljit_compiler *compiler, sljit_s32 op) ++{ ++#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64) ++ sljit_ins word = (sljit_ins)(op & SLJIT_I32_OP) >> 5; ++ ++ SLJIT_ASSERT(word == 0 || word == 0x8); ++#endif /* SLJIT_CONFIG_RISCV_64 */ ++ ++ CHECK_ERROR(); ++ CHECK(check_sljit_emit_op0(compiler, op)); ++ ++ switch (GET_OPCODE(op)) { ++ case SLJIT_BREAKPOINT: ++ return push_inst(compiler, EBREAK); ++ case SLJIT_NOP: ++ return push_inst(compiler, ADDI | RD(TMP_ZERO) | RS1(TMP_ZERO) | IMM_I(0)); ++ case SLJIT_LMUL_UW: ++ FAIL_IF(push_inst(compiler, ADDI | RD(TMP_REG1) | RS1(SLJIT_R1) | IMM_I(0))); ++ FAIL_IF(push_inst(compiler, MULHU | RD(SLJIT_R1) | RS1(SLJIT_R0) | RS2(SLJIT_R1))); ++ return push_inst(compiler, MUL | RD(SLJIT_R0) | RS1(SLJIT_R0) | RS2(TMP_REG1)); ++ case SLJIT_LMUL_SW: ++ FAIL_IF(push_inst(compiler, ADDI | RD(TMP_REG1) | RS1(SLJIT_R1) | IMM_I(0))); ++ FAIL_IF(push_inst(compiler, MULH | RD(SLJIT_R1) | RS1(SLJIT_R0) | RS2(SLJIT_R1))); ++ return push_inst(compiler, MUL | RD(SLJIT_R0) | RS1(SLJIT_R0) | RS2(TMP_REG1)); ++ case SLJIT_DIVMOD_UW: ++ FAIL_IF(push_inst(compiler, ADDI | RD(TMP_REG1) | RS1(SLJIT_R0) | IMM_I(0))); ++ FAIL_IF(push_inst(compiler, DIVU | WORD | RD(SLJIT_R0) | RS1(SLJIT_R0) | RS2(SLJIT_R1))); ++ return push_inst(compiler, REMU | WORD | RD(SLJIT_R1) | RS1(TMP_REG1) | RS2(SLJIT_R1)); ++ case SLJIT_DIVMOD_SW: ++ FAIL_IF(push_inst(compiler, ADDI | RD(TMP_REG1) | RS1(SLJIT_R0) | IMM_I(0))); ++ FAIL_IF(push_inst(compiler, DIV | WORD | RD(SLJIT_R0) | RS1(SLJIT_R0) | RS2(SLJIT_R1))); ++ return push_inst(compiler, REM | WORD | RD(SLJIT_R1) | RS1(TMP_REG1) | RS2(SLJIT_R1)); ++ case SLJIT_DIV_UW: ++ return push_inst(compiler, DIVU | WORD | RD(SLJIT_R0) | RS1(SLJIT_R0) | RS2(SLJIT_R1)); ++ case SLJIT_DIV_SW: ++ return push_inst(compiler, DIV | WORD | RD(SLJIT_R0) | RS1(SLJIT_R0) | RS2(SLJIT_R1)); ++ default: ++ return SLJIT_SUCCESS; ++ } ++ ++ return SLJIT_SUCCESS; ++} ++ ++SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compiler, sljit_s32 op, ++ sljit_s32 dst, sljit_sw dstw, ++ sljit_s32 src, sljit_sw srcw) ++{ ++ sljit_s32 flags = 0; ++ ++ CHECK_ERROR(); ++ CHECK(check_sljit_emit_op1(compiler, op, dst, dstw, src, srcw)); ++ ADJUST_LOCAL_OFFSET(dst, dstw); ++ ADJUST_LOCAL_OFFSET(src, srcw); ++ ++#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64) ++ if (op & SLJIT_I32_OP) ++ flags = INT_DATA | SIGNED_DATA; ++#endif ++ ++ switch (GET_OPCODE(op)) { ++ case SLJIT_MOV: ++#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) ++ case SLJIT_MOV_U32: ++ case SLJIT_MOV_S32: ++ case SLJIT_MOV32: ++#endif ++ case SLJIT_MOV_P: ++ return emit_op(compiler, SLJIT_MOV, WORD_DATA | MOVE_OP, dst, dstw, TMP_ZERO, 0, src, srcw); ++ ++#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64) ++ case SLJIT_MOV_U32: ++ return emit_op(compiler, SLJIT_MOV_U32, INT_DATA | MOVE_OP, dst, dstw, TMP_ZERO, 0, src, (src == SLJIT_IMM) ? (sljit_u32)srcw : srcw); ++ ++ case SLJIT_MOV_S32: ++ /* Logical operators have no W variant, so sign extended input is necessary for them. */ ++ case SLJIT_MOV32: ++ return emit_op(compiler, SLJIT_MOV_S32, INT_DATA | SIGNED_DATA | MOVE_OP, dst, dstw, TMP_ZERO, 0, src, (src == SLJIT_IMM) ? (sljit_s32)srcw : srcw); ++#endif ++ ++ case SLJIT_MOV_U8: ++ return emit_op(compiler, op, BYTE_DATA | MOVE_OP, dst, dstw, TMP_ZERO, 0, src, (src == SLJIT_IMM) ? (sljit_u8)srcw : srcw); ++ ++ case SLJIT_MOV_S8: ++ return emit_op(compiler, op, BYTE_DATA | SIGNED_DATA | MOVE_OP, dst, dstw, TMP_ZERO, 0, src, (src == SLJIT_IMM) ? (sljit_s8)srcw : srcw); ++ ++ case SLJIT_MOV_U16: ++ return emit_op(compiler, op, HALF_DATA | MOVE_OP, dst, dstw, TMP_ZERO, 0, src, (src == SLJIT_IMM) ? (sljit_u16)srcw : srcw); ++ ++ case SLJIT_MOV_S16: ++ return emit_op(compiler, op, HALF_DATA | SIGNED_DATA | MOVE_OP, dst, dstw, TMP_ZERO, 0, src, (src == SLJIT_IMM) ? (sljit_s16)srcw : srcw); ++ ++ case SLJIT_CLZ: ++ return emit_op(compiler, op, flags, dst, dstw, TMP_ZERO, 0, src, srcw); ++ ++ case SLJIT_NEG: ++ return emit_op(compiler, SLJIT_SUB | GET_ALL_FLAGS(op), flags | IMM_OP, dst, dstw, SLJIT_IMM, 0, src, srcw); ++ ++ case SLJIT_NOT: ++ return emit_op(compiler, op, flags, dst, dstw, TMP_REG1, 0, src, srcw); ++ } ++ ++ SLJIT_UNREACHABLE(); ++ return SLJIT_SUCCESS; ++} ++ ++SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compiler, sljit_s32 op, ++ sljit_s32 dst, sljit_sw dstw, ++ sljit_s32 src1, sljit_sw src1w, ++ sljit_s32 src2, sljit_sw src2w) ++{ ++ sljit_s32 flags = 0; ++ ++ CHECK_ERROR(); ++ CHECK(check_sljit_emit_op2(compiler, op, 0, dst, dstw, src1, src1w, src2, src2w)); ++ ADJUST_LOCAL_OFFSET(dst, dstw); ++ ADJUST_LOCAL_OFFSET(src1, src1w); ++ ADJUST_LOCAL_OFFSET(src2, src2w); ++ ++#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64) ++ if (op & SLJIT_I32_OP) { ++ flags |= INT_DATA | SIGNED_DATA; ++ if (src1 == SLJIT_IMM) ++ src1w = (sljit_s32)src1w; ++ if (src2 == SLJIT_IMM) ++ src2w = (sljit_s32)src2w; ++ } ++#endif ++ ++ switch (GET_OPCODE(op)) { ++ case SLJIT_ADD: ++ case SLJIT_ADDC: ++ return emit_op(compiler, op, flags | CUMULATIVE_OP | IMM_OP, dst, dstw, src1, src1w, src2, src2w); ++ ++ case SLJIT_SUB: ++ case SLJIT_SUBC: ++ return emit_op(compiler, op, flags | IMM_OP, dst, dstw, src1, src1w, src2, src2w); ++ ++ case SLJIT_MUL: ++ return emit_op(compiler, op, flags | CUMULATIVE_OP, dst, dstw, src1, src1w, src2, src2w); ++ ++ case SLJIT_AND: ++ case SLJIT_OR: ++ case SLJIT_XOR: ++ return emit_op(compiler, op, flags | CUMULATIVE_OP | IMM_OP, dst, dstw, src1, src1w, src2, src2w); ++ ++ case SLJIT_SHL: ++ case SLJIT_LSHR: ++ case SLJIT_ASHR: ++ if (src2 == SLJIT_IMM) { ++#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) ++ src2w &= 0x1f; ++#else /* !SLJIT_CONFIG_RISCV_32 */ ++ if (op & SLJIT_I32_OP) ++ src2w &= 0x1f; ++ else ++ src2w &= 0x3f; ++#endif /* SLJIT_CONFIG_RISCV_32 */ ++ } ++ ++ return emit_op(compiler, op, flags | IMM_OP, dst, dstw, src1, src1w, src2, src2w); ++ } ++ ++ SLJIT_UNREACHABLE(); ++ return SLJIT_SUCCESS; ++} ++ ++SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_register_index(sljit_s32 reg) ++{ ++ CHECK_REG_INDEX(check_sljit_get_register_index(reg)); ++ return reg_map[reg]; ++} ++ ++SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_float_register_index(sljit_s32 reg) ++{ ++ CHECK_REG_INDEX(check_sljit_get_float_register_index(reg)); ++ return freg_map[reg]; ++} ++ ++SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_custom(struct sljit_compiler *compiler, ++ void *instruction, sljit_s32 size) ++{ ++ CHECK_ERROR(); ++ CHECK(check_sljit_emit_op_custom(compiler, instruction, size)); ++ ++ return push_inst(compiler, *(sljit_ins*)instruction); ++} ++ ++/* --------------------------------------------------------------------- */ ++/* Floating point operators */ ++/* --------------------------------------------------------------------- */ ++ ++#define FLOAT_DATA(op) (DOUBLE_DATA | ((op & SLJIT_I32_OP) >> 7)) ++#define FMT(op) ((sljit_ins)((op & SLJIT_I32_OP) ^ SLJIT_I32_OP) << 17) ++ ++static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_sw_from_f64(struct sljit_compiler *compiler, sljit_s32 op, ++ sljit_s32 dst, sljit_sw dstw, ++ sljit_s32 src, sljit_sw srcw) ++{ ++#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) ++# define flags (sljit_u32)0 ++#else ++ sljit_u32 flags = ((sljit_u32)(GET_OPCODE(op) == SLJIT_CONV_SW_FROM_F64)) << 21; ++#endif ++ sljit_s32 dst_r = FAST_IS_REG(dst) ? dst : TMP_REG2; ++ ++ if (src & SLJIT_MEM) { ++ FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src, srcw, dst, dstw)); ++ src = TMP_FREG1; ++ } ++ ++ FAIL_IF(push_inst(compiler, FCVT_W_S | FMT(op) | flags | RD(dst_r) | FRS1(src))); ++ ++ /* Store the integer value from a VFP register. */ ++ if (dst & SLJIT_MEM) { ++#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) ++ return emit_op_mem2(compiler, WORD_DATA, TMP_REG2, dst, dstw, 0, 0); ++#else ++ return emit_op_mem2(compiler, flags ? WORD_DATA : INT_DATA, TMP_REG2, dst, dstw, 0, 0); ++#endif ++ } ++ return SLJIT_SUCCESS; ++ ++#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) ++# undef flags ++#endif ++} ++ ++static sljit_s32 sljit_emit_fop1_conv_f64_from_w(struct sljit_compiler *compiler, sljit_ins ins, ++ sljit_s32 dst, sljit_sw dstw, ++ sljit_s32 src, sljit_sw srcw) ++{ ++ sljit_s32 dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1; ++ ++ if (src & SLJIT_MEM) { ++#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) ++ FAIL_IF(emit_op_mem2(compiler, WORD_DATA | LOAD_DATA, TMP_REG1, src, srcw, dst, dstw)); ++#else /* SLJIT_CONFIG_RISCV_32 */ ++ FAIL_IF(emit_op_mem2(compiler, ((ins & (1 << 21)) ? WORD_DATA : INT_DATA) | LOAD_DATA, TMP_REG1, src, srcw, dst, dstw)); ++#endif /* !SLJIT_CONFIG_RISCV_32 */ ++ src = TMP_REG1; ++ } else if (src == SLJIT_IMM) { ++ FAIL_IF(load_immediate(compiler, TMP_REG1, srcw, TMP_REG3)); ++ src = TMP_REG1; ++ } ++ ++ FAIL_IF(push_inst(compiler, ins | FRD(dst_r) | RS1(src))); ++ ++ if (dst & SLJIT_MEM) ++ return emit_op_mem2(compiler, DOUBLE_DATA | ((sljit_s32)(~ins >> 24) & 0x2), TMP_FREG1, dst, dstw, 0, 0); ++ return SLJIT_SUCCESS; ++} ++ ++static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_f64_from_sw(struct sljit_compiler *compiler, sljit_s32 op, ++ sljit_s32 dst, sljit_sw dstw, ++ sljit_s32 src, sljit_sw srcw) ++{ ++ sljit_ins ins = FCVT_S_W | FMT(op); ++ ++#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) ++ if (op & SLJIT_I32_OP) ++ ins |= F3(0x7); ++#else /* !SLJIT_CONFIG_RISCV_32 */ ++ if (GET_OPCODE(op) == SLJIT_CONV_F64_FROM_SW) ++ ins |= (1 << 21); ++ else if (src == SLJIT_IMM) ++ srcw = (sljit_s32)srcw; ++ ++ if (op != SLJIT_CONV_F64_FROM_S32) ++ ins |= F3(0x7); ++#endif /* SLJIT_CONFIG_RISCV_32 */ ++ ++ return sljit_emit_fop1_conv_f64_from_w(compiler, ins, dst, dstw, src, srcw); ++} ++ ++static SLJIT_INLINE sljit_s32 sljit_emit_fop1_cmp(struct sljit_compiler *compiler, sljit_s32 op, ++ sljit_s32 src1, sljit_sw src1w, ++ sljit_s32 src2, sljit_sw src2w) ++{ ++ sljit_ins inst; ++ ++ if (src1 & SLJIT_MEM) { ++ FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src1, src1w, src2, src2w)); ++ src1 = TMP_FREG1; ++ } ++ ++ if (src2 & SLJIT_MEM) { ++ FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG2, src2, src2w, 0, 0)); ++ src2 = TMP_FREG2; ++ } ++ ++ switch (GET_FLAG_TYPE(op)) { ++ case SLJIT_EQUAL_F64: ++ inst = FEQ_S | FMT(op) | RD(OTHER_FLAG) | FRS1(src1) | FRS2(src2); ++ break; ++ ++ case SLJIT_LESS_F64: ++ inst = FLT_S | FMT(op) | RD(OTHER_FLAG) | FRS1(src1) | FRS2(src2); ++ break; ++ ++ case SLJIT_GREATER_F64: ++ inst = FLE_S | FMT(op) | RD(OTHER_FLAG) | FRS1(src1) | FRS2(src2); ++ break; ++ ++ default: /* SLJIT_UNORDERED */ ++ if (src1 == src2) { ++ inst = FEQ_S | FMT(op) | RD(OTHER_FLAG) | FRS1(src1) | FRS2(src1); ++ break; ++ } ++ FAIL_IF(push_inst(compiler, FEQ_S | FMT(op) | RD(OTHER_FLAG) | FRS1(src1) | FRS2(src1))); ++ FAIL_IF(push_inst(compiler, FEQ_S | FMT(op) | RD(TMP_REG1) | FRS1(src2) | FRS2(src2))); ++ inst = AND | RD(OTHER_FLAG) | RS1(OTHER_FLAG) | RS2(TMP_REG1); ++ break; ++ } ++ ++ return push_inst(compiler, inst); ++} ++ ++SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop1(struct sljit_compiler *compiler, sljit_s32 op, ++ sljit_s32 dst, sljit_sw dstw, ++ sljit_s32 src, sljit_sw srcw) ++{ ++ sljit_s32 dst_r; ++ ++ CHECK_ERROR(); ++ compiler->cache_arg = 0; ++ compiler->cache_argw = 0; ++ ++ SLJIT_COMPILE_ASSERT((SLJIT_I32_OP == 0x100) && !(DOUBLE_DATA & 0x2), float_transfer_bit_error); ++ SELECT_FOP1_OPERATION_WITH_CHECKS(compiler, op, dst, dstw, src, srcw); ++ ++ if (GET_OPCODE(op) == SLJIT_CONV_F64_FROM_F32) ++ op ^= SLJIT_I32_OP; ++ ++ dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1; ++ ++ if (src & SLJIT_MEM) { ++ FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op) | LOAD_DATA, dst_r, src, srcw, dst, dstw)); ++ src = dst_r; ++ } ++ ++ switch (GET_OPCODE(op)) { ++ case SLJIT_MOV_F64: ++ if (src != dst_r) { ++ if (!(dst & SLJIT_MEM)) ++ FAIL_IF(push_inst(compiler, FSGNJ_S | FMT(op) | FRD(dst_r) | FRS1(src) | FRS2(src))); ++ else ++ dst_r = src; ++ } ++ break; ++ case SLJIT_NEG_F64: ++ FAIL_IF(push_inst(compiler, FSGNJN_S | FMT(op) | FRD(dst_r) | FRS1(src) | FRS2(src))); ++ break; ++ case SLJIT_ABS_F64: ++ FAIL_IF(push_inst(compiler, FSGNJX_S | FMT(op) | FRD(dst_r) | FRS1(src) | FRS2(src))); ++ break; ++ case SLJIT_CONV_F64_FROM_F32: ++ /* The SLJIT_I32_OP bit is inverted because sljit_f32 needs to be loaded from the memory. */ ++ FAIL_IF(push_inst(compiler, FCVT_S_D | ((op & SLJIT_I32_OP) ? (1 << 25) : ((1 << 20) | F3(7))) | FRD(dst_r) | FRS1(src))); ++ op ^= SLJIT_I32_OP; ++ break; ++ } ++ ++ if (dst & SLJIT_MEM) ++ return emit_op_mem2(compiler, FLOAT_DATA(op), dst_r, dst, dstw, 0, 0); ++ return SLJIT_SUCCESS; ++} ++ ++SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop2(struct sljit_compiler *compiler, sljit_s32 op, ++ sljit_s32 dst, sljit_sw dstw, ++ sljit_s32 src1, sljit_sw src1w, ++ sljit_s32 src2, sljit_sw src2w) ++{ ++ sljit_s32 dst_r, flags = 0; ++ ++ CHECK_ERROR(); ++ CHECK(check_sljit_emit_fop2(compiler, op, dst, dstw, src1, src1w, src2, src2w)); ++ ADJUST_LOCAL_OFFSET(dst, dstw); ++ ADJUST_LOCAL_OFFSET(src1, src1w); ++ ADJUST_LOCAL_OFFSET(src2, src2w); ++ ++ compiler->cache_arg = 0; ++ compiler->cache_argw = 0; ++ ++ dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG2; ++ ++ if (src1 & SLJIT_MEM) { ++ if (getput_arg_fast(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src1, src1w)) { ++ FAIL_IF(compiler->error); ++ src1 = TMP_FREG1; ++ } else ++ flags |= SLOW_SRC1; ++ } ++ ++ if (src2 & SLJIT_MEM) { ++ if (getput_arg_fast(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG2, src2, src2w)) { ++ FAIL_IF(compiler->error); ++ src2 = TMP_FREG2; ++ } else ++ flags |= SLOW_SRC2; ++ } ++ ++ if ((flags & (SLOW_SRC1 | SLOW_SRC2)) == (SLOW_SRC1 | SLOW_SRC2)) { ++ if ((dst & SLJIT_MEM) && !can_cache(src1, src1w, src2, src2w) && can_cache(src1, src1w, dst, dstw)) { ++ FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG2, src2, src2w, src1, src1w)); ++ FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src1, src1w, dst, dstw)); ++ } else { ++ FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src1, src1w, src2, src2w)); ++ FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG2, src2, src2w, dst, dstw)); ++ } ++ } ++ else if (flags & SLOW_SRC1) ++ FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src1, src1w, dst, dstw)); ++ else if (flags & SLOW_SRC2) ++ FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG2, src2, src2w, dst, dstw)); ++ ++ if (flags & SLOW_SRC1) ++ src1 = TMP_FREG1; ++ if (flags & SLOW_SRC2) ++ src2 = TMP_FREG2; ++ ++ switch (GET_OPCODE(op)) { ++ case SLJIT_ADD_F64: ++ FAIL_IF(push_inst(compiler, FADD_S | FMT(op) | FRD(dst_r) | FRS1(src1) | FRS2(src2))); ++ break; ++ ++ case SLJIT_SUB_F64: ++ FAIL_IF(push_inst(compiler, FSUB_S | FMT(op) | FRD(dst_r) | FRS1(src1) | FRS2(src2))); ++ break; ++ ++ case SLJIT_MUL_F64: ++ FAIL_IF(push_inst(compiler, FMUL_S | FMT(op) | FRD(dst_r) | FRS1(src1) | FRS2(src2))); ++ break; ++ ++ case SLJIT_DIV_F64: ++ FAIL_IF(push_inst(compiler, FDIV_S | FMT(op) | FRD(dst_r) | FRS1(src1) | FRS2(src2))); ++ break; ++ } ++ ++ if (dst_r != dst) ++ FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op), TMP_FREG2, dst, dstw, 0, 0)); ++ ++ return SLJIT_SUCCESS; ++} ++ ++/* --------------------------------------------------------------------- */ ++/* Other instructions */ ++/* --------------------------------------------------------------------- */ ++ ++SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_enter(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw) ++{ ++ CHECK_ERROR(); ++ CHECK(check_sljit_emit_fast_enter(compiler, dst, dstw)); ++ ADJUST_LOCAL_OFFSET(dst, dstw); ++ ++ if(FAST_IS_REG(dst)) ++ return push_inst(compiler, ADDI | RD(dst) | RS1(RETURN_ADDR_REG) | IMM_I(0)); ++ ++ return emit_op_mem(compiler, WORD_DATA, RETURN_ADDR_REG, dst, dstw); ++} ++ ++SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_return(struct sljit_compiler *compiler, sljit_s32 src, sljit_sw srcw) ++{ ++ CHECK_ERROR(); ++ CHECK(check_sljit_emit_fast_return(compiler, src, srcw)); ++ ADJUST_LOCAL_OFFSET(src, srcw); ++ ++ if (FAST_IS_REG(src)) ++ FAIL_IF(push_inst(compiler, ADDI | RD(RETURN_ADDR_REG) | RS1(src) | IMM_I(0))); ++ else ++ FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, RETURN_ADDR_REG, src, srcw)); ++ ++ return push_inst(compiler, JALR | RD(TMP_ZERO) | RS1(RETURN_ADDR_REG) | IMM_I(0)); ++} ++ ++/* --------------------------------------------------------------------- */ ++/* Conditional instructions */ ++/* --------------------------------------------------------------------- */ ++ ++SLJIT_API_FUNC_ATTRIBUTE struct sljit_label* sljit_emit_label(struct sljit_compiler *compiler) ++{ ++ struct sljit_label *label; ++ ++ CHECK_ERROR_PTR(); ++ CHECK_PTR(check_sljit_emit_label(compiler)); ++ ++ if (compiler->last_label && compiler->last_label->size == compiler->size) ++ return compiler->last_label; ++ ++ label = (struct sljit_label*)ensure_abuf(compiler, sizeof(struct sljit_label)); ++ PTR_FAIL_IF(!label); ++ set_label(label, compiler); ++ return label; ++} ++ ++#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) ++#define BRANCH_LENGTH ((sljit_ins)(3 * sizeof(sljit_ins)) << 7) ++#else ++#define BRANCH_LENGTH ((sljit_ins)(7 * sizeof(sljit_ins)) << 7) ++#endif ++ ++static sljit_ins get_jump_instruction(sljit_s32 type) ++{ ++ switch (type) { ++ case SLJIT_EQUAL: ++ return BNE | RS1(EQUAL_FLAG) | RS2(TMP_ZERO); ++ case SLJIT_NOT_EQUAL: ++ return BEQ | RS1(EQUAL_FLAG) | RS2(TMP_ZERO); ++ case SLJIT_LESS: ++ case SLJIT_GREATER: ++ case SLJIT_SIG_LESS: ++ case SLJIT_SIG_GREATER: ++ case SLJIT_OVERFLOW: ++ case SLJIT_MUL_OVERFLOW: ++ case SLJIT_EQUAL_F64: ++ case SLJIT_LESS_F64: ++ case SLJIT_LESS_EQUAL_F64: ++ case SLJIT_ORDERED_F64: ++ case SLJIT_EQUAL_F32: ++ case SLJIT_LESS_F32: ++ case SLJIT_LESS_EQUAL_F32: ++ case SLJIT_ORDERED_F32: ++ return BEQ | RS1(OTHER_FLAG) | RS2(TMP_ZERO); ++ case SLJIT_GREATER_EQUAL: ++ case SLJIT_LESS_EQUAL: ++ case SLJIT_SIG_GREATER_EQUAL: ++ case SLJIT_SIG_LESS_EQUAL: ++ case SLJIT_NOT_OVERFLOW: ++ case SLJIT_MUL_NOT_OVERFLOW: ++ case SLJIT_NOT_EQUAL_F64: ++ case SLJIT_GREATER_EQUAL_F64: ++ case SLJIT_GREATER_F64: ++ case SLJIT_UNORDERED_F64: ++ case SLJIT_NOT_EQUAL_F32: ++ case SLJIT_GREATER_EQUAL_F32: ++ case SLJIT_GREATER_F32: ++ case SLJIT_UNORDERED_F32: ++ return BNE | RS1(OTHER_FLAG) | RS2(TMP_ZERO); ++ default: ++ /* Not conditional branch. */ ++ return 0; ++ } ++} ++ ++SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_jump(struct sljit_compiler *compiler, sljit_s32 type) ++{ ++ struct sljit_jump *jump; ++ sljit_ins inst; ++ ++ CHECK_ERROR_PTR(); ++ CHECK_PTR(check_sljit_emit_jump(compiler, type)); ++ ++ jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump)); ++ PTR_FAIL_IF(!jump); ++ set_jump(jump, compiler, type & SLJIT_REWRITABLE_JUMP); ++ type &= 0xff; ++ ++ inst = get_jump_instruction(type); ++ ++ if (inst != 0) { ++ PTR_FAIL_IF(push_inst(compiler, inst | BRANCH_LENGTH)); ++ jump->flags |= IS_COND; ++ } ++ ++ jump->addr = compiler->size; ++ inst = JALR | RS1(TMP_REG1) | IMM_I(0); ++ ++ if (type >= SLJIT_FAST_CALL) { ++ jump->flags |= IS_CALL; ++ inst |= RD(RETURN_ADDR_REG); ++ } ++ ++ PTR_FAIL_IF(push_inst(compiler, inst)); ++ ++ /* Maximum number of instructions required for generating a constant. */ ++ compiler->size += JUMP_MAX_SIZE - 1; ++ return jump; ++} ++ ++SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_call(struct sljit_compiler *compiler, sljit_s32 type, ++ sljit_s32 arg_types) ++{ ++ SLJIT_UNUSED_ARG(arg_types); ++ CHECK_ERROR_PTR(); ++ CHECK_PTR(check_sljit_emit_call(compiler, type, arg_types)); ++ ++ SLJIT_SKIP_CHECKS(compiler); ++ return sljit_emit_jump(compiler, type); ++} ++ ++SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_cmp(struct sljit_compiler *compiler, sljit_s32 type, ++ sljit_s32 src1, sljit_sw src1w, ++ sljit_s32 src2, sljit_sw src2w) ++{ ++ struct sljit_jump *jump; ++ sljit_s32 flags; ++ sljit_ins inst; ++ ++ CHECK_ERROR_PTR(); ++ CHECK_PTR(check_sljit_emit_cmp(compiler, type, src1, src1w, src2, src2w)); ++ ADJUST_LOCAL_OFFSET(src1, src1w); ++ ADJUST_LOCAL_OFFSET(src2, src2w); ++ ++ compiler->cache_arg = 0; ++ compiler->cache_argw = 0; ++#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) ++ flags = WORD_DATA | LOAD_DATA; ++#else /* !SLJIT_CONFIG_RISCV_32 */ ++ flags = ((type & SLJIT_I32_OP) ? INT_DATA : WORD_DATA) | LOAD_DATA; ++#endif /* SLJIT_CONFIG_RISCV_32 */ ++ ++ if (src1 & SLJIT_MEM) { ++ PTR_FAIL_IF(emit_op_mem2(compiler, flags, TMP_REG1, src1, src1w, src2, src2w)); ++ src1 = TMP_REG1; ++ } ++ ++ if (src2 & SLJIT_MEM) { ++ PTR_FAIL_IF(emit_op_mem2(compiler, flags, TMP_REG2, src2, src2w, 0, 0)); ++ src2 = TMP_REG2; ++ } ++ ++ if (src1 & SLJIT_IMM) { ++ if (src1w != 0) { ++ PTR_FAIL_IF(load_immediate(compiler, TMP_REG1, src1w, TMP_REG3)); ++ src1 = TMP_REG1; ++ } else ++ src1 = TMP_ZERO; ++ } ++ ++ if (src2 & SLJIT_IMM) { ++ if (src2w != 0) { ++ PTR_FAIL_IF(load_immediate(compiler, TMP_REG2, src2w, TMP_REG3)); ++ src2 = TMP_REG2; ++ } else ++ src2 = TMP_ZERO; ++ } ++ ++ jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump)); ++ PTR_FAIL_IF(!jump); ++ set_jump(jump, compiler, (sljit_u32)((type & SLJIT_REWRITABLE_JUMP) | IS_COND)); ++ type &= 0xff; ++ ++ switch (type) { ++ case SLJIT_EQUAL: ++ inst = BNE | RS1(src1) | RS2(src2) | BRANCH_LENGTH; ++ break; ++ case SLJIT_NOT_EQUAL: ++ inst = BEQ | RS1(src1) | RS2(src2) | BRANCH_LENGTH; ++ break; ++ case SLJIT_LESS: ++ inst = BGEU | RS1(src1) | RS2(src2) | BRANCH_LENGTH; ++ break; ++ case SLJIT_GREATER_EQUAL: ++ inst = BLTU | RS1(src1) | RS2(src2) | BRANCH_LENGTH; ++ break; ++ case SLJIT_GREATER: ++ inst = BGEU | RS1(src2) | RS2(src1) | BRANCH_LENGTH; ++ break; ++ case SLJIT_LESS_EQUAL: ++ inst = BLTU | RS1(src2) | RS2(src1) | BRANCH_LENGTH; ++ break; ++ case SLJIT_SIG_LESS: ++ inst = BGE | RS1(src1) | RS2(src2) | BRANCH_LENGTH; ++ break; ++ case SLJIT_SIG_GREATER_EQUAL: ++ inst = BLT | RS1(src1) | RS2(src2) | BRANCH_LENGTH; ++ break; ++ case SLJIT_SIG_GREATER: ++ inst = BGE | RS1(src2) | RS2(src1) | BRANCH_LENGTH; ++ break; ++ case SLJIT_SIG_LESS_EQUAL: ++ inst = BLT | RS1(src2) | RS2(src1) | BRANCH_LENGTH; ++ break; ++ } ++ ++ PTR_FAIL_IF(push_inst(compiler, inst)); ++ ++ jump->addr = compiler->size; ++ PTR_FAIL_IF(push_inst(compiler, JALR | RD(TMP_ZERO) | RS1(TMP_REG1) | IMM_I(0))); ++ ++ /* Maximum number of instructions required for generating a constant. */ ++ compiler->size += JUMP_MAX_SIZE - 1; ++ return jump; ++} ++ ++#undef BRANCH_LENGTH ++ ++SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 src, sljit_sw srcw) ++{ ++ struct sljit_jump *jump; ++ ++ CHECK_ERROR(); ++ CHECK(check_sljit_emit_ijump(compiler, type, src, srcw)); ++ ++ if (src != SLJIT_IMM) { ++ if (src & SLJIT_MEM) { ++ ADJUST_LOCAL_OFFSET(src, srcw); ++ FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, TMP_REG1, src, srcw)); ++ src = TMP_REG1; ++ } ++ return push_inst(compiler, JALR | RD((type >= SLJIT_FAST_CALL) ? RETURN_ADDR_REG : TMP_ZERO) | RS1(src) | IMM_I(0)); ++ } ++ ++ /* These jumps are converted to jump/call instructions when possible. */ ++ jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump)); ++ FAIL_IF(!jump); ++ set_jump(jump, compiler, JUMP_ADDR | ((type >= SLJIT_FAST_CALL) ? IS_CALL : 0)); ++ jump->u.target = (sljit_uw)srcw; ++ ++ jump->addr = compiler->size; ++ FAIL_IF(push_inst(compiler, JALR | RD((type >= SLJIT_FAST_CALL) ? RETURN_ADDR_REG : TMP_ZERO) | RS1(TMP_REG1) | IMM_I(0))); ++ ++ /* Maximum number of instructions required for generating a constant. */ ++ compiler->size += JUMP_MAX_SIZE - 1; ++ return SLJIT_SUCCESS; ++} ++ ++SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_icall(struct sljit_compiler *compiler, sljit_s32 type, ++ sljit_s32 arg_types, ++ sljit_s32 src, sljit_sw srcw) ++{ ++ SLJIT_UNUSED_ARG(arg_types); ++ CHECK_ERROR(); ++ CHECK(check_sljit_emit_icall(compiler, type, arg_types, src, srcw)); ++ ++ if (src & SLJIT_MEM) { ++ ADJUST_LOCAL_OFFSET(src, srcw); ++ FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, TMP_REG1, src, srcw)); ++ src = TMP_REG1; ++ } ++ ++ SLJIT_SKIP_CHECKS(compiler); ++ return sljit_emit_ijump(compiler, type, src, srcw); ++} ++ ++SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *compiler, sljit_s32 op, ++ sljit_s32 dst, sljit_sw dstw, ++ sljit_s32 type) ++{ ++ sljit_s32 src_r, dst_r, invert; ++ sljit_s32 saved_op = op; ++#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) ++ sljit_s32 mem_type = WORD_DATA; ++#else ++ sljit_s32 mem_type = ((op & SLJIT_I32_OP) || op == SLJIT_MOV32) ? (INT_DATA | SIGNED_DATA) : WORD_DATA; ++#endif ++ ++ CHECK_ERROR(); ++ CHECK(check_sljit_emit_op_flags(compiler, op, dst, dstw, type)); ++ ADJUST_LOCAL_OFFSET(dst, dstw); ++ ++ op = GET_OPCODE(op); ++ dst_r = (op < SLJIT_ADD && FAST_IS_REG(dst)) ? dst : TMP_REG2; ++ ++ compiler->cache_arg = 0; ++ compiler->cache_argw = 0; ++ ++ if (op >= SLJIT_ADD && (dst & SLJIT_MEM)) ++ FAIL_IF(emit_op_mem2(compiler, mem_type | LOAD_DATA, TMP_REG1, dst, dstw, dst, dstw)); ++ ++ if (type < SLJIT_EQUAL_F64) { ++ src_r = OTHER_FLAG; ++ invert = type & 0x1; ++ ++ switch (type) { ++ case SLJIT_EQUAL: ++ case SLJIT_NOT_EQUAL: ++ FAIL_IF(push_inst(compiler, SLTUI | RD(dst_r) | RS1(EQUAL_FLAG) | IMM_I(1))); ++ src_r = dst_r; ++ break; ++ case SLJIT_OVERFLOW: ++ case SLJIT_NOT_OVERFLOW: ++ ++ FAIL_IF(push_inst(compiler, SLTUI | RD(dst_r) | RS1(OTHER_FLAG) | IMM_I(1))); ++ src_r = dst_r; ++ invert ^= 0x1; ++ break; ++ } ++ } else { ++ invert = 0; ++ src_r = OTHER_FLAG; ++ ++ switch (type) { ++ case SLJIT_NOT_EQUAL_F64: ++ case SLJIT_GREATER_EQUAL_F64: ++ case SLJIT_GREATER_F64: ++ case SLJIT_UNORDERED_F64: ++ invert = 1; ++ break; ++ } ++ } ++ ++ if (invert) { ++ FAIL_IF(push_inst(compiler, XORI | RD(dst_r) | RS1(src_r) | IMM_I(1))); ++ src_r = dst_r; ++ } ++ ++ if (op < SLJIT_ADD) { ++ if (dst & SLJIT_MEM) ++ return emit_op_mem(compiler, mem_type, src_r, dst, dstw); ++ ++ if (src_r != dst_r) ++ return push_inst(compiler, ADDI | RD(dst_r) | RS1(src_r) | IMM_I(0)); ++ return SLJIT_SUCCESS; ++ } ++ ++ mem_type |= CUMULATIVE_OP | IMM_OP | ALT_KEEP_CACHE; ++ ++ if (dst & SLJIT_MEM) ++ return emit_op(compiler, saved_op, mem_type, dst, dstw, TMP_REG1, 0, src_r, 0); ++ return emit_op(compiler, saved_op, mem_type, dst, dstw, dst, dstw, src_r, 0); ++} ++ ++#undef WORD ++ ++#undef FLOAT_DATA ++#undef FMT ++ ++#define SLJIT_SKIP_CHECKS(compiler) (compiler)->skip_checks = 1 ++ ++/* Sets 32 bit operation mode on 64 bit CPUs. This option is ignored on ++ 32 bit CPUs. When this option is set for an arithmetic operation, only ++ the lower 32 bits of the input registers are used, and the CPU status ++ flags are set according to the 32 bit result. Although the higher 32 bit ++ of the input and the result registers are not defined by SLJIT, it might ++ be defined by the CPU architecture (e.g. MIPS). To satisfy these CPU ++ requirements all source registers must be the result of those operations ++ where this option was also set. Memory loads read 32 bit values rather ++ than 64 bit ones. In other words 32 bit and 64 bit operations cannot be ++ mixed. The only exception is SLJIT_MOV32 which source register can hold ++ any 32 or 64 bit value, and it is converted to a 32 bit compatible format ++ first. When the source and destination registers are the same, this ++ conversion is free (no instructions are emitted) on most CPUs. A 32 bit ++ value can also be converted to a 64 bit value by SLJIT_MOV_S32 ++ (sign extension) or SLJIT_MOV_U32 (zero extension). ++ ++ As for floating-point operations, this option sets 32 bit single ++ precision mode. Similar to the integer operations, all register arguments ++ must be the result of those operations where this option was also set. ++ ++ Note: memory addressing always uses 64 bit values on 64 bit systems so ++ the result of a 32 bit operation must not be used with SLJIT_MEMx ++ macros. ++ ++ This option is part of the instruction name, so there is no need to ++ manually set it. E.g: ++ ++ SLJIT_ADD32 == (SLJIT_ADD | SLJIT_32) */ ++ ++static sljit_s32 sljit_emit_mem_unaligned(struct sljit_compiler *compiler, sljit_s32 type, ++ sljit_s32 reg, ++ sljit_s32 mem, sljit_sw memw) ++{ ++ SLJIT_SKIP_CHECKS(compiler); ++ ++ if (type & SLJIT_MEM_STORE) ++ return sljit_emit_op1(compiler, type & (0xff | SLJIT_I32_OP), mem, memw, reg, 0); ++ return sljit_emit_op1(compiler, type & (0xff | SLJIT_I32_OP), reg, 0, mem, memw); ++} ++ ++SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem(struct sljit_compiler *compiler, sljit_s32 type, ++ sljit_s32 reg, ++ sljit_s32 mem, sljit_sw memw) ++{ ++ sljit_s32 flags; ++ ++ CHECK_ERROR(); ++ CHECK(check_sljit_emit_mem(compiler, type, reg, mem, memw)); ++ ++ if (!(reg & REG_PAIR_MASK)) ++ return sljit_emit_mem_unaligned(compiler, type, reg, mem, memw); ++ ++ if (SLJIT_UNLIKELY(mem & OFFS_REG_MASK)) { ++ memw &= 0x3; ++ ++ if (SLJIT_UNLIKELY(memw != 0)) { ++ FAIL_IF(push_inst(compiler, SLLI | RD(TMP_REG1) | RS1(OFFS_REG(mem)) | IMM_I(memw))); ++ FAIL_IF(push_inst(compiler, ADD | RD(TMP_REG1) | RS1(TMP_REG1) | RS2(mem & REG_MASK))); ++ } else ++ FAIL_IF(push_inst(compiler, ADD | RD(TMP_REG1) | RS1(mem & REG_MASK) | RS2(OFFS_REG(mem)))); ++ ++ mem = TMP_REG1; ++ memw = 0; ++ } else if (memw > SIMM_MAX - SSIZE_OF(sw) || memw < SIMM_MIN) { ++ if (((memw + 0x800) & 0xfff) <= 0xfff - SSIZE_OF(sw)) { ++ FAIL_IF(load_immediate(compiler, TMP_REG1, TO_ARGW_HI(memw), TMP_REG3)); ++ memw &= 0xfff; ++ } else { ++ FAIL_IF(load_immediate(compiler, TMP_REG1, memw, TMP_REG3)); ++ memw = 0; ++ } ++ ++ if (mem & REG_MASK) ++ FAIL_IF(push_inst(compiler, ADD | RD(TMP_REG1) | RS1(TMP_REG1) | RS2(mem & REG_MASK))); ++ ++ mem = TMP_REG1; ++ } else { ++ mem &= REG_MASK; ++ memw &= 0xfff; ++ } ++ ++ SLJIT_ASSERT((memw >= 0 && memw <= SIMM_MAX - SSIZE_OF(sw)) || (memw > SIMM_MAX && memw <= 0xfff)); ++ ++ if (!(type & SLJIT_MEM_STORE) && mem == REG_PAIR_FIRST(reg)) { ++ FAIL_IF(push_mem_inst(compiler, WORD_DATA | LOAD_DATA, REG_PAIR_SECOND(reg), mem, (memw + SSIZE_OF(sw)) & 0xfff)); ++ return push_mem_inst(compiler, WORD_DATA | LOAD_DATA, REG_PAIR_FIRST(reg), mem, memw); ++ } ++ ++ flags = WORD_DATA | (!(type & SLJIT_MEM_STORE) ? LOAD_DATA : 0); ++ ++ FAIL_IF(push_mem_inst(compiler, flags, REG_PAIR_FIRST(reg), mem, memw)); ++ return push_mem_inst(compiler, flags, REG_PAIR_SECOND(reg), mem, (memw + SSIZE_OF(sw)) & 0xfff); ++} ++ ++#undef TO_ARGW_HI ++ ++SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_cmov(struct sljit_compiler *compiler, sljit_s32 type, ++ sljit_s32 dst_reg, ++ sljit_s32 src, sljit_sw srcw) ++{ ++ CHECK_ERROR(); ++ CHECK(check_sljit_emit_cmov(compiler, type, dst_reg, src, srcw)); ++ ++ return sljit_emit_cmov_generic(compiler, type, dst_reg, src, srcw);; ++} ++ ++ ++SLJIT_API_FUNC_ATTRIBUTE struct sljit_const* sljit_emit_const(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw, sljit_sw init_value) ++{ ++ struct sljit_const *const_; ++ sljit_s32 dst_r; ++ ++ CHECK_ERROR_PTR(); ++ CHECK_PTR(check_sljit_emit_const(compiler, dst, dstw, init_value)); ++ ADJUST_LOCAL_OFFSET(dst, dstw); ++ ++ const_ = (struct sljit_const*)ensure_abuf(compiler, sizeof(struct sljit_const)); ++ PTR_FAIL_IF(!const_); ++ set_const(const_, compiler); ++ ++ dst_r = FAST_IS_REG(dst) ? dst : TMP_REG2; ++ PTR_FAIL_IF(emit_const(compiler, dst_r, init_value, ADDI | RD(dst_r))); ++ ++ if (dst & SLJIT_MEM) ++ PTR_FAIL_IF(emit_op_mem(compiler, WORD_DATA, TMP_REG2, dst, dstw)); ++ ++ return const_; ++} ++ ++SLJIT_API_FUNC_ATTRIBUTE struct sljit_put_label* sljit_emit_put_label(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw) ++{ ++ struct sljit_put_label *put_label; ++ sljit_s32 dst_r; ++ ++ CHECK_ERROR_PTR(); ++ CHECK_PTR(check_sljit_emit_put_label(compiler, dst, dstw)); ++ ADJUST_LOCAL_OFFSET(dst, dstw); ++ ++ put_label = (struct sljit_put_label*)ensure_abuf(compiler, sizeof(struct sljit_put_label)); ++ PTR_FAIL_IF(!put_label); ++ set_put_label(put_label, compiler, 0); ++ ++ dst_r = FAST_IS_REG(dst) ? dst : TMP_REG2; ++ PTR_FAIL_IF(push_inst(compiler, (sljit_ins)dst_r)); ++ compiler->size += JUMP_MAX_SIZE - 1; ++ ++ if (dst & SLJIT_MEM) ++ PTR_FAIL_IF(emit_op_mem(compiler, WORD_DATA, TMP_REG2, dst, dstw)); ++ ++ return put_label; ++} ++ ++SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_sw new_constant, sljit_sw executable_offset) ++{ ++ sljit_set_jump_addr(addr, (sljit_uw)new_constant, executable_offset); ++} +\ No newline at end of file diff --git a/pcre.spec b/pcre.spec index 5bba355..aef30b8 100644 --- a/pcre.spec +++ b/pcre.spec @@ -1,6 +1,6 @@ Name: pcre Version: 8.45 -Release: 5 +Release: 6 Summary: Perl Compatible Regular Expressions ## Source package only: # INSTALL: FSFAP @@ -26,7 +26,7 @@ Summary: Perl Compatible Regular Expressions License: BSD URL: http://www.pcre.org/ Source0: https://sourceforge.net/projects/pcre/files/%{name}/%{version}/%{name}-%{version}.tar.bz2 - +Patch0001: add-riscv-jit-backport.patch BuildRequires: readline-devel autoconf automake coreutils BuildRequires: gcc gcc-c++ glibc-common libtool make @@ -69,13 +69,18 @@ Requires: man The %{name}-help package contains doc files for %{name}. %prep -%autosetup -n %{name}-%{version} -p1 +%setup -n %{name}-%{version} + +%ifarch riscv64 +%patch -P0001 -p1 +%endif + %build libtoolize -cf autoreconf -vif %configure \ -%ifarch riscv64 sw_64 loongarch64 +%ifarch sw_64 loongarch64 --disable-jit \ %else --enable-jit \ @@ -116,6 +121,12 @@ make check VERBOSE=yes %{_mandir}/man3/* %changelog +* Tue Apr 30 2024 TexasOct - 8.45-6 +- Type:enhancement +- ID:NA +- SUG:NA +- DESC:add RISCV support backport + * Sat Nov 26 2022 yangmingtai - 8.45-5 - Type:bugfix - ID:NA -- Gitee