From 677848575041be4859018d04deac2d5d4f7b5b29 Mon Sep 17 00:00:00 2001 From: Josef Schlehofer Date: Sat, 15 Aug 2026 12:12:39 +0200 Subject: [PATCH] luajit2: update to 2026-07-31 Also refresh 020-riscv64-support.patch from the current state of openresty/luajit2#236. The previous snapshot defined asm_tail_prep(ASMState *as), but since openresty/luajit2 commit 68354f44 the callers pass a second TraceNo argument, so the riscv64 build failed with: lj_asm.c:2559:5: error: too many arguments to function 'asm_tail_prep' The refreshed patch carries the upstream adaptation and restores the build. The other four patches apply unchanged. Signed-off-by: Josef Schlehofer --- lang/lua/luajit2/Makefile | 8 +- .../luajit2/patches/020-riscv64-support.patch | 859 ++++++++++-------- 2 files changed, 459 insertions(+), 408 deletions(-) diff --git a/lang/lua/luajit2/Makefile b/lang/lua/luajit2/Makefile index c61183cced919..a46dfee89567e 100644 --- a/lang/lua/luajit2/Makefile +++ b/lang/lua/luajit2/Makefile @@ -1,14 +1,14 @@ include $(TOPDIR)/rules.mk PKG_NAME:=luajit2 -PKG_SOURCE_DATE:=2026-02-27 +PKG_SOURCE_DATE:=2026-07-31 PKG_VERSION:=2.1.$(subst -,.,$(PKG_SOURCE_DATE)) -PKG_RELEASE:=2 +PKG_RELEASE:=1 PKG_SOURCE_PROTO:=git PKG_SOURCE_URL:=https://github.com/openresty/luajit2 -PKG_MIRROR_HASH:=057f6c02cdfac3f7cacc21fcac45ff74084cabb4a4db2cc589ed332bb1aeddbb -PKG_SOURCE_VERSION:=928290a470d35eb01324d5275b22fb86b13602a2 +PKG_MIRROR_HASH:=cf3ae553640fcd6400bc545d87c9a7a3317b9e944216bd76433b84eabb756c3e +PKG_SOURCE_VERSION:=52f52587b37867ab19236eb6917001c2d6b662e7 PKG_MAINTAINER:=Javier Marcet PKG_LICENSE:=MIT diff --git a/lang/lua/luajit2/patches/020-riscv64-support.patch b/lang/lua/luajit2/patches/020-riscv64-support.patch index ee4d560e9621e..06b14dad58729 100644 --- a/lang/lua/luajit2/patches/020-riscv64-support.patch +++ b/lang/lua/luajit2/patches/020-riscv64-support.patch @@ -1,6 +1,6 @@ -From dff0c180819449c742ed77b2cb542b5a95c1f0d5 Mon Sep 17 00:00:00 2001 +From e00e8aaa56093d8d1a5f89b7fa7dd1c09dc7a4c1 Mon Sep 17 00:00:00 2001 From: gns -Date: Thu, 27 Nov 2025 20:48:19 +0800 +Date: Tue, 16 Jun 2026 09:10:36 +0000 Subject: [PATCH] Add RISC-V 64 support This backports RISC-V 64 support from openresty/luajit2 PR #236. @@ -11,6 +11,10 @@ The patch adds comprehensive RISC-V64 JIT implementation including: - JIT compiler, assembler, and emitter implementations - FFI calling conventions and Linux-specific support +Refreshed against the current state of the pull request, which adapts +asm_tail_prep() to the TraceNo argument added by openresty/luajit2 commit +68354f44 and so restores the build on riscv64. + Original work by gns from PLCT Lab, ISCAS. See: https://github.com/openresty/luajit2/pull/236 @@ -28,13 +32,11 @@ Co-authored-by: Heinrich Schuchardt ifeq (,$(findstring Windows,$(OS))) --- /dev/null +++ b/dynasm/dasm_riscv.h -@@ -0,0 +1,435 @@ +@@ -0,0 +1,433 @@ +/* +** DynASM RISC-V encoding engine. -+** Copyright (C) 2005-2025 Mike Pall. All rights reserved. ++** Copyright (C) 2022-2026 ISRC, ISCAS. All rights reserved. +** Released under the MIT license. See dynasm.lua for full copyright notice. -+** -+** Contributed by gns from PLCT Lab, ISCAS. +*/ + +#include @@ -470,10 +472,10 @@ Co-authored-by: Heinrich Schuchardt +------------------------------------------------------------------------------ +-- DynASM RISC-V module. +-- -+-- Copyright (C) 2005-2025 Mike Pall. All rights reserved. ++-- Copyright (C) 2022-2026 ISRC, ISCAS. +-- See dynasm.lua for full copyright notice. +-- -+-- Contributed by gns from PLCT Lab, ISCAS. ++-- Contributed by gns from PLCT Lab, ISRC, ISCAS. +------------------------------------------------------------------------------ + +local riscv32 = riscv32 @@ -486,7 +488,7 @@ Co-authored-by: Heinrich Schuchardt + version = "1.5.0", + vernum = 10500, + release = "2022-07-12", -+ author = "Mike Pall", ++ author = "gns", + license = "MIT", +} + @@ -1452,7 +1454,7 @@ Co-authored-by: Heinrich Schuchardt +------------------------------------------------------------------------------ +-- DynASM RISC-V 32 module. +-- -+-- Copyright (C) 2005-2025 Mike Pall. All rights reserved. ++-- Copyright (C) 2022-2026 ISRC, ISCAS. All rights reserved. +-- See dynasm.lua for full copyright notice. +------------------------------------------------------------------------------ +-- This module just sets 32 bit mode for the combined RISC-V module. @@ -1467,7 +1469,7 @@ Co-authored-by: Heinrich Schuchardt +------------------------------------------------------------------------------ +-- DynASM RISC-V 64 module. +-- -+-- Copyright (C) 2005-2025 Mike Pall. All rights reserved. ++-- Copyright (C) 2022-2026 ISRC, ISCAS. All rights reserved. +-- See dynasm.lua for full copyright notice. +------------------------------------------------------------------------------ +-- This module just sets 64 bit mode for the combined RISC-V module. @@ -1588,11 +1590,11 @@ Co-authored-by: Heinrich Schuchardt +------------------------------------------------------------------------------ +-- LuaJIT RISC-V disassembler module. +-- -+-- Copyright (C) 2005-2025 Mike Pall. All rights reserved. ++-- Copyright (C) 2022-2026 ISRC, ISCAS. All rights reserved. +-- Released under the MIT license. See Copyright Notice in luajit.h +-- +-- Contributed by Milos Poletanovic from Syrmia.com. -+-- Contributed by gns from PLCT Lab, ISCAS. ++-- Contributed by gns from PLCT Lab, ISRC, ISCAS. +------------------------------------------------------------------------------ +-- This is a helper module used by the LuaJIT machine code dumper module. +-- @@ -2570,7 +2572,7 @@ Co-authored-by: Heinrich Schuchardt +---------------------------------------------------------------------------- +-- LuaJIT RISC-V 64 disassembler wrapper module. +-- -+-- Copyright (C) 2005-2025 Mike Pall. All rights reserved. ++-- Copyright (C) 2022-2026 ISRC, ISCAS. All rights reserved. +-- Released under the MIT license. See Copyright Notice in luajit.h +---------------------------------------------------------------------------- +-- This module just exports the default riscv little-endian functions from the @@ -2583,10 +2585,9 @@ Co-authored-by: Heinrich Schuchardt + disass = dis_riscv.disass, + regname = dis_riscv.regname +} -\ No newline at end of file --- a/src/lib_jit.c +++ b/src/lib_jit.c -@@ -706,6 +706,104 @@ JIT_PARAMDEF(JIT_PARAMINIT) +@@ -713,6 +713,104 @@ JIT_PARAMDEF(JIT_PARAMINIT) #include #endif @@ -2691,7 +2692,7 @@ Co-authored-by: Heinrich Schuchardt /* Arch-dependent CPU feature detection. */ static uint32_t jit_cpudetect(void) { -@@ -778,6 +876,29 @@ static uint32_t jit_cpudetect(void) +@@ -785,6 +883,29 @@ static uint32_t jit_cpudetect(void) #endif #elif LJ_TARGET_S390X /* No optional CPU features to detect (for now). */ @@ -2752,7 +2753,7 @@ Co-authored-by: Heinrich Schuchardt #else #error "Architecture not supported (in this version), see: https://luajit.org/status.html#architectures" #endif -@@ -506,6 +510,20 @@ +@@ -508,6 +512,20 @@ #define LJ_ARCH_NUMMODE LJ_NUMMODE_DUAL #define LJ_TARGET_GC64 1 #define LJ_ARCH_NOJIT 1 /* NYI */ @@ -2773,7 +2774,7 @@ Co-authored-by: Heinrich Schuchardt #else #error "No target architecture defined" -@@ -590,6 +608,10 @@ +@@ -592,6 +610,10 @@ #error "Only n64 ABI supported for MIPS64" #undef LJ_TARGET_MIPS #endif @@ -2806,12 +2807,12 @@ Co-authored-by: Heinrich Schuchardt #endif --- /dev/null +++ b/src/lj_asm_riscv64.h -@@ -0,0 +1,2037 @@ +@@ -0,0 +1,2049 @@ +/* +** RISC-V IR assembler (SSA IR -> machine code). -+** Copyright (C) 2005-2025 Mike Pall. See Copyright Notice in luajit.h ++** Copyright (C) 2022-2026 ISRC, ISCAS. See Copyright Notice in luajit.h +** -+** Contributed by gns from PLCT Lab, ISCAS. ++** Contributed by gns from PLCT Lab, ISRC, ISCAS. +*/ + +/* -- Register allocator extensions --------------------------------------- */ @@ -2908,18 +2909,30 @@ Co-authored-by: Heinrich Schuchardt +static void asm_exitstub_setup(ASMState *as, ExitNo nexits) +{ + ExitNo i; ++ MCode *target = (MCode *)(void *)lj_vm_exit_handler; + MCode *mxp = as->mctop; + if (mxp - (nexits + 4 + MCLIM_REDZONE) < as->mclim) + asm_mclimit(as); + for (i = nexits-1; (int32_t)i >= 0; i--) + *--mxp = RISCVI_JAL | RISCVF_D(RID_RA) | RISCVF_IMMJ((uintptr_t)(4*(-4-i))); -+ ptrdiff_t delta = (char *)lj_vm_exit_handler - (char *)(mxp-3); -+ /* 1: sw ra, 0(sp); auipc+jalr ->vm_exit_handler; lui x0, traceno; jal <1; jal <1; ... */ ++ ptrdiff_t delta = (char *)target - (char *)(mxp-3); ++ /* !ind: 1: sw ra, 0(sp); auipc+jalr ->vm_exit_handler; lui x0, traceno; jal <1; jal <1; ... ++ ** ind: 1: sw ra, 0(sp); ld tmp, K64_VXH(gl); jalr tmp; lui x0, traceno; jal <1; jal <1; ... ++ ** Note: RID_TMP is RID_RA! ++ */ + *--mxp = RISCVI_LUI | RISCVF_IMMU(as->T->traceno); -+ *--mxp = RISCVI_JALR | RISCVF_D(RID_RA) | RISCVF_S1(RID_TMP) -+ | RISCVF_IMMI(RISCVF_LO((uintptr_t)(void *)delta)); -+ *--mxp = RISCVI_AUIPC | RISCVF_D(RID_TMP) -+ | RISCVF_IMMU(RISCVF_HI((uintptr_t)(void *)delta)); ++ if (checki32auipc(delta)) { ++ *--mxp = RISCVI_JALR | RISCVF_D(RID_RA) | RISCVF_S1(RID_TMP) ++ | RISCVF_IMMI(RISCVF_LO((uintptr_t)(void *)delta)); ++ *--mxp = RISCVI_AUIPC | RISCVF_D(RID_TMP) ++ | RISCVF_IMMU(RISCVF_HI((uintptr_t)(void *)delta)); ++ } else { ++ *--mxp = RISCVI_JALR | RISCVF_D(RID_RA) | RISCVF_S1(RID_TMP) | RISCVF_IMMI(0); ++ *--mxp = RISCVI_LD | RISCVF_D(RID_TMP) | RISCVF_S1(RID_GL) ++ | RISCVF_IMMI(glofs(as, &as->J->k64[LJ_K64_VM_EXIT_HANDLER])); ++ lj_assertA(checki12(glofs(as, &as->J->k64[LJ_K64_VM_EXIT_HANDLER])), ++ "exit handler address offset overflow"); ++ } + *--mxp = RISCVI_SD | RISCVF_S2(RID_RA) | RISCVF_S1(RID_SP); + as->mctop = mxp; +} @@ -2937,7 +2950,6 @@ Co-authored-by: Heinrich Schuchardt + MCode *p = as->mcp; + if (LJ_UNLIKELY(p == as->invmcp)) { + as->loopinv = 1; -+ as->mcp = ++p; + *p = RISCVI_JAL | RISCVF_IMMJ((char *)target - (char *)p); + riscvi = riscvi^RISCVF_FUNCT3(1); /* Invert cond. */ + target = p - 1; /* Patch target later in asm_loop_fixup. */ @@ -3319,14 +3331,18 @@ Co-authored-by: Heinrich Schuchardt + } else { + Reg left = ra_alloc1(as, lref, RSET_FPR); + Reg dest = ra_dest(as, ir, RSET_GPR); -+ RISCVIns riscvi = irt_is64(ir->t) ? -+ (st == IRT_NUM ? -+ (irt_isi64(ir->t) ? RISCVI_FCVT_L_D : RISCVI_FCVT_LU_D) : -+ (irt_isi64(ir->t) ? RISCVI_FCVT_L_S : RISCVI_FCVT_LU_S)) : -+ (st == IRT_NUM ? -+ (irt_isint(ir->t) ? RISCVI_FCVT_W_D : RISCVI_FCVT_WU_D) : -+ (irt_isint(ir->t) ? RISCVI_FCVT_W_S : RISCVI_FCVT_WU_S)); -+ emit_ds(as, riscvi|RISCVF_RM(RISCVRM_RTZ), dest, left); ++ lj_assertA(!irt_isu32(ir->t), "bad CONV u32.fp emitted"); ++ if (irt_isu64(ir->t)) { ++ MCLabel l_end = emit_label(as); ++ emit_ds(as, st == IRT_NUM ? RISCVI_FCVT_L_D : RISCVI_FCVT_L_S, dest, left); ++ emit_branch(as, RISCVI_BNE, dest, RID_ZERO, l_end, -1); ++ emit_ds(as, st == IRT_NUM ? RISCVI_FCVT_LU_D : RISCVI_FCVT_LU_S, dest, left); ++ } else { ++ RISCVIns riscvi = irt_is64(ir->t) ? ++ (st == IRT_NUM ? RISCVI_FCVT_L_D : RISCVI_FCVT_L_S) : ++ (st == IRT_NUM ? RISCVI_FCVT_W_D : RISCVI_FCVT_W_S); ++ emit_ds(as, riscvi|RISCVF_RM(RISCVRM_RTZ), dest, left); ++ } + } + } else if (st >= IRT_I8 && st <= IRT_U16) { /* Extend to 32 bit integer. */ + Reg dest = ra_dest(as, ir, RSET_GPR); @@ -4055,7 +4071,7 @@ Co-authored-by: Heinrich Schuchardt + emit_ds(as, RISCVI_FCVT_D_L, ftmp, RID_TMP); + } + emit_ds(as, riscvi, RID_TMP, left); -+ emit_branch(as, RISCVI_BLT, RID_ZERO, RID_TMP, l_end, 0); ++ emit_branch(as, RISCVI_BLT, RID_ZERO, RID_TMP, l_end, -1); + emit_dsi(as, RISCVI_ADDI, RID_TMP, RID_TMP, -1075); + emit_dsi(as, RISCVI_ANDI, RID_TMP, RID_TMP, 0x7ff); + emit_dsi(as, RISCVI_SRLI, RID_TMP, RID_TMP, 52); @@ -4320,7 +4336,8 @@ Co-authored-by: Heinrich Schuchardt + Reg left = ra_alloc1(as, ir->op1, RSET_GPR); + uint32_t shmsk = irt_is64(ir->t) ? 63 : 31; + if (irref_isk(ir->op2)) { /* Constant shifts. */ -+ uint32_t shift = (uint32_t)(IR(ir->op2)->i & shmsk); ++ IRIns *irr = IR(ir->op2); ++ uint32_t shift = (uint32_t) irr->o == IR_KINT ? (uint32_t)irr->i : (uint32_t)ir_kint64(irr)->u64; + switch (riscvik) { + case RISCVI_SRAI: case RISCVI_SRLI: case RISCVI_SLLI: + case RISCVI_SRAIW: case RISCVI_SLLIW: case RISCVI_SRLIW: @@ -4730,33 +4747,37 @@ Co-authored-by: Heinrich Schuchardt +/* Fixup the tail code. */ +static void asm_tail_fixup(ASMState *as, TraceNo lnk) +{ -+ MCode *p = as->mctop; -+ MCode *target = lnk ? traceref(as->J,lnk)->mcode : (MCode *)lj_vm_exit_interp; ++ MCode *mcp = as->mctail; ++ MCode *target; + int32_t spadj = as->T->spadjust; -+ if (spadj == 0) { -+ p[-3] = RISCVI_NOP; -+ // as->mctop = p-2; ++ if (spadj) { /* Emit stack adjustment */ ++ *mcp++ = RISCVI_ADDI | RISCVF_D(RID_SP) | RISCVF_S1(RID_SP) | RISCVF_IMMI(spadj); ++ } ++ /* Emit exit jump. */ ++ target = lnk ? traceref(as->J,lnk)->mcode : (MCode *)lj_vm_exit_interp; ++ ptrdiff_t delta = (char *)target - (char *)mcp; ++ if (lnk || checki32auipc(delta)) { ++ *mcp++ = RISCVI_AUIPC | RISCVF_D(RID_TMP) | RISCVF_IMMU(RISCVF_HI(delta)); ++ *mcp++ = RISCVI_JALR | RISCVF_S1(RID_TMP) | RISCVF_IMMI(RISCVF_LO(delta)); + } else { -+ /* Patch stack adjustment. */ -+ p[-3] = RISCVI_ADDI | RISCVF_D(RID_SP) | RISCVF_S1(RID_SP) | RISCVF_IMMI(spadj); ++ *mcp++ = RISCVI_LD | RISCVF_D(RID_TMP) | RISCVF_S1(RID_GL) | RISCVF_IMMI(glofs(as, &as->J->k64[LJ_K64_VM_EXIT_INTERP])); ++ *mcp++ = RISCVI_JALR | RISCVF_S1(RID_TMP); + } -+ /* Patch exit jump. */ -+ ptrdiff_t delta = (char *)target - (char *)(p - 2); -+ p[-2] = RISCVI_AUIPC | RISCVF_D(RID_TMP) | RISCVF_IMMU(RISCVF_HI(delta)); -+ p[-1] = RISCVI_JALR | RISCVF_S1(RID_TMP) | RISCVF_IMMI(RISCVF_LO(delta)); ++ while (as->mctop > mcp) *--as->mctop = RISCVI_NOP; /* NOP out unused space. */ +} + +/* Prepare tail of code. */ -+static void asm_tail_prep(ASMState *as) ++static void asm_tail_prep(ASMState *as, TraceNo lnk) +{ -+ MCode *p = as->mctop - 2; /* Leave room for exitstub. */ ++ MCode *p = as->mctop - 1; /* Leave room for exitstub. */ + if (as->loopref) { -+ as->invmcp = as->mcp = p; ++ as->invmcp = as->mcp = p; /* A single jump */ + } else { -+ as->mcp = p-1; /* Leave room for stack pointer adjustment. */ ++ as->mcp = (p -= 2); /* Stack pointer adjustment and AUIPC+JALR */ + as->invmcp = NULL; ++ p[0] = p[1] = p[2] = RISCVI_EBREAK; + } -+ p[0] = p[1] = RISCVI_NOP; /* Prevent load/store merging. */ ++ as->mctail = p; +} + +/* -- Trace setup --------------------------------------------------------- */ @@ -4831,14 +4852,6 @@ Co-authored-by: Heinrich Schuchardt + } + /* Ignore jump slot overflow. Child trace is simply not attached. */ + } -+ } else if (p+2 == pe) { -+ if (p[0] == RISCVI_NOP && p[1] == RISCVI_NOP) { -+ ptrdiff_t delta = (char *)target - (char *)p; -+ lj_assertJ(checki32(delta), "jump target out of range"); -+ p[0] = RISCVI_AUIPC | RISCVF_D(RID_TMP) | RISCVF_IMMU(RISCVF_HI(delta)); -+ p[1] = RISCVI_JALR | RISCVF_S1(RID_TMP) | RISCVF_IMMI(RISCVF_LO(delta)); -+ if (!cstart) cstart = p; -+ } + } + } + if (cstart) lj_mcode_sync(cstart, px+1); @@ -4846,51 +4859,46 @@ Co-authored-by: Heinrich Schuchardt +} --- a/src/lj_ccall.c +++ b/src/lj_ccall.c -@@ -687,6 +687,146 @@ +@@ -689,6 +689,197 @@ if (ngpr < maxgpr) { dp = &cc->gpr[ngpr++]; goto done; } \ } +#elif LJ_TARGET_RISCV64 +/* -- RISC-V lp64d calling conventions ------------------------------------ */ + ++/* Forward declarations for functions used by macros below. */ ++#define CCALL_MIX_MAXFIELDS 2 /* MIX state machine has exactly 2 slots. */ ++typedef struct { ++ CTSize offset; ++ uint8_t esz; ++ uint8_t isfp; ++} CCallMixField; ++ ++typedef struct { ++ uint8_t nfields; ++ CCallMixField fields[CCALL_MIX_MAXFIELDS]; ++} CCallMixFieldList; ++ ++static CTSize ccall_place_mix_fields(CCallState *cc, CCallMixFieldList *list, ++ CTSize sz, uint8_t *src, ++ MSize *pnfpr, MSize *pngpr, int isva); ++static void ccall_unplace_mix_fields(CCallState *cc, CCallMixFieldList *list, ++ uint8_t *dp); ++ +#define CCALL_HANDLE_STRUCTRET \ -+ /* Return structs of size > 16 by reference. */ \ -+ cc->retref = !(sz <= 16); \ ++ /* Return by reference only if >16 bytes and not classifiable as MIX. */ \ ++ cc->retref = !ccall_classify_struct(cts, ctr, NULL, 0).ispod && sz > 16; \ + if (cc->retref) cc->gpr[ngpr++] = (GPRArg)dp; + +#define CCALL_HANDLE_STRUCTRET2 \ -+ CCallStructClass cl = ccall_classify_struct(cts, ctr); \ ++{ \ ++ CCallMixFieldList mixlist = {0}; \ ++ CCallStructClass cl = ccall_classify_struct(cts, ctr, &mixlist, 0); \ + CCallStructMix mix = cl.mix; \ + switch (mix.val) { \ -+ case MIX_IX: { \ -+ ((intptr_t *)dp)[0] = cc->gpr[0]; \ -+ break; \ -+ } \ -+ case MIX_FX: case MIX_DX: \ -+ case MIX_FF: case MIX_FD: \ -+ case MIX_DF: case MIX_DD: { \ -+ eCCallStructMixElem es[2] = { mix.e1, mix.e2 }; \ -+ for (int ti = 0; ti < 2; ti++) { \ -+ if (es[ti] == MIX_ELEM_FLOAT) { \ -+ ((float *)dp)[ti] = cc->fpr[ti].f; \ -+ } else /*if (es[ti] == MIX_ELEM_DOUBLE)*/ { \ -+ ((double *)dp)[ti] = cc->fpr[ti].d; \ -+ } \ -+ } \ -+ break; \ -+ } \ -+ case MIX_FI: case MIX_DI: \ -+ case MIX_IF: case MIX_ID: { \ -+ eCCallStructMixElem es[2] = { mix.e1, mix.e2 }; \ -+ for (int ti = 0; ti < 2; ti++) { \ -+ if (es[ti] == MIX_ELEM_FLOAT) { \ -+ ((float *)dp)[ti] = cc->fpr[0].f; \ -+ } else if (es[ti] == MIX_ELEM_DOUBLE) { \ -+ ((double *)dp)[ti] = cc->fpr[0].d; \ -+ } else /*if (es[ti] == MIX_ELEM_INT)*/ { \ -+ ((intptr_t *)dp)[ti] = cc->gpr[0]; \ -+ } \ -+ } \ ++ case MIX_IX: case MIX_FX: \ ++ case MIX_FF: case MIX_IF: case MIX_FI: { \ ++ ccall_unplace_mix_fields(cc, &mixlist, (uint8_t *)dp); \ + break; \ + } \ + case MIX_UNINIT: \ @@ -4899,7 +4907,8 @@ Co-authored-by: Heinrich Schuchardt + break; \ + } \ + default: lj_assertL(0, "Invalid ret mix %d", mix.val); \ -+ } ++ } \ ++} + +#define CCALL_HANDLE_COMPLEXRET \ + /* Complex values are returned in 1 or 2 FPRs. */ \ @@ -4925,7 +4934,7 @@ Co-authored-by: Heinrich Schuchardt + mix = (CCallStructMix){ .val = MIX_FF }; \ + sz = CTSIZE_PTR; \ + } else /*if (sz == 2*sizeof(double))*/ { \ -+ mix = (CCallStructMix){ .val = MIX_DD }; \ ++ mix = (CCallStructMix){ .val = MIX_FF }; \ + sz = 2*CTSIZE_PTR; \ + } + @@ -4934,66 +4943,121 @@ Co-authored-by: Heinrich Schuchardt + sp = (uint8_t *)&cc->fpr[0].f; + +#define CCALL_HANDLE_STRUCTARG \ -+ /* Pass structs of size >16 by reference. */ \ -+ CCallStructClass cl = ccall_classify_struct(cts, d); \ ++ /* Pass structs of size >16 by reference if not classifiable as MIX. */ \ ++ CCallMixFieldList mixlist = {0}; \ ++ CCallStructClass cl = ccall_classify_struct(cts, d, &mixlist, 0); \ + uint8_t ispod = cl.ispod; \ + mix = cl.mix; \ ++ /* Check if MIX registers available; fall back to integer conv if not */ \ ++ if (ispod && mix.val != MIX_FAILED && mix.val != MIX_UNINIT) { \ ++ int need_fpr = 0, need_gpr = 0; \ ++ switch (mix.val) { \ ++ case MIX_FX: need_fpr = 1; break; \ ++ case MIX_FF: need_fpr = 2; break; \ ++ case MIX_FI: case MIX_IF: need_fpr = 1; need_gpr = 1; break; \ ++ default: lj_assertCTS(0, "Invalid MIX %d", mix.val); \ ++ } \ ++ if ((need_fpr && nfpr + need_fpr > CCALL_NARG_FPR) || \ ++ (need_gpr && ngpr + need_gpr > CCALL_NARG_GPR)) \ ++ mix.val = MIX_FAILED; /* Fall back to integer convention */ \ ++ } \ ++ /* Integer convention >2xXLEN: pass by reference */ \ ++ if (ispod && (mix.val == MIX_FAILED || mix.val == MIX_UNINIT) && sz > 16) \ ++ ispod = 0; /* Route to by-reference path below. */ \ + if (!ispod && sz > 16) { \ + rp = cdataptr(lj_cdata_new(cts, did, sz)); \ + sz = CTSIZE_PTR; \ ++ } else if (ispod) { \ ++ /* POD struct: convert to stack, then place fields in registers. */ \ ++ MSize al = (1u << ctype_align(ccall_struct_align(cts, d))) - 1; \ ++ MSize ns = (nsp + al) & ~al; \ ++ if (ns + sz > CCALL_SIZE_STACK) goto err_nyi; \ ++ uint8_t *src = (uint8_t *)cc->stack + ns; \ ++ lj_cconv_ct_tv(cts, d, src, o, CCF_ARG(narg)); \ ++ if (mix.val == MIX_FAILED || mix.val == MIX_UNINIT) { \ ++ /* Integer convention: place 8-byte chunks in GPRs, rest on stack. */ \ ++ int nchunks = (sz + 7) >> 3; \ ++ int chunks_avail = CCALL_NARG_GPR - (int)ngpr; \ ++ int i; \ ++ for (i = 0; i < chunks_avail && i < nchunks; i++) \ ++ cc->gpr[ngpr++] = ((GPRArg *)src)[i]; \ ++ if (i < nchunks) { \ ++ memmove(src, src + i * 8, sz - i * 8); \ ++ nsp = ns + (nchunks - i) * 8; \ ++ } \ ++ } else { \ ++ /* MIX convention: place fields from flat descriptor list. */ \ ++ CTSize tail = ccall_place_mix_fields(cc, &mixlist, sz, \ ++ src, &nfpr, &ngpr, isva); \ ++ if (tail < sz) { \ ++ memmove(src, src + tail, sz - tail); \ ++ nsp = ns + (sz - tail); \ ++ } \ ++ } \ ++ continue; \ + } + -+ +#define CCALL_HANDLE_REGARG \ -+ if (!isva) { /* Try determine MIX registers. */ \ -+ int n2 = 0; \ -+ switch (mix.val) { \ -+ case MIX_UNINIT: \ -+ if (isfp) { \ -+ n2 = 1; \ -+ break; \ -+ } \ -+ /* fallthrough */ \ -+ case MIX_FAILED: \ -+ /* MIX_[IFD]X are just like a standalone element */ \ -+ case MIX_IX: goto reghandle_gpr; \ -+ case MIX_FX: case MIX_DX: \ -+ n2 = 1; \ -+ break; \ -+ /* MIX_[FD][FD] are just like two standalone elements */ \ -+ /* fix float later */ \ -+ case MIX_FF: case MIX_DD: \ -+ case MIX_FD: case MIX_DF: \ -+ n2 = 2; \ -+ break; \ -+ /* Setup MIX_I[FD] or MIX[FD]I on stack first, fix later */ \ -+ default: goto reghandle_exit; \ -+ } \ -+ if (nfpr + n2 <= CCALL_NARG_FPR) { \ ++ if (!isva) { \ ++ if (isfp && nfpr + 1 <= CCALL_NARG_FPR) { \ + dp = &cc->fpr[nfpr]; \ -+ nfpr += n2; \ ++ nfpr += 1; \ + goto done; \ -+ } else { \ -+ if (ngpr + n2 <= maxgpr) { \ -+ dp = &cc->gpr[ngpr]; \ -+ ngpr += n2; \ -+ goto done; \ -+ } \ + } \ -+ } else { /* Try to pass argument in GPRs. */ \ -+ reghandle_gpr: \ -+ if (ngpr + n <= maxgpr) { \ -+ dp = &cc->gpr[ngpr]; \ -+ ngpr += n; \ -+ goto done; \ -+ } \ -+ reghandle_exit: \ ++ } \ ++ if (ngpr + n <= maxgpr) { \ ++ dp = &cc->gpr[ngpr]; \ ++ ngpr += n; \ ++ goto done; \ ++ } ++ ++/* Place MIX struct fields from a flat descriptor list into registers. ++** Returns byte offset of first unplaced field (sz if all placed). */ ++static CTSize ccall_place_mix_fields(CCallState *cc, CCallMixFieldList *list, ++ CTSize sz, uint8_t *src, ++ MSize *pnfpr, MSize *pngpr, ++ int isva) ++{ ++ for (int i = 0; i < list->nfields; i++) { ++ CCallMixField *f = &list->fields[i]; ++ uint64_t val; ++ if (f->isfp) { ++ val = (f->esz == 4) ? (*(uint32_t *)(src + f->offset) | U64x(ffffffff,00000000)) ++ : *(uint64_t *)(src + f->offset); ++ if (!isva && *pnfpr < CCALL_NARG_FPR) cc->fpr[(*pnfpr)++].u = val; ++ else if (*pngpr < CCALL_NARG_GPR) cc->gpr[(*pngpr)++] = val; ++ else return f->offset; ++ } else { ++ val = (f->esz < 8) ? (uint64_t)(int64_t)*(int32_t *)(src + f->offset) ++ : *(uint64_t *)(src + f->offset); ++ if (*pngpr < CCALL_NARG_GPR) cc->gpr[(*pngpr)++] = val; ++ else return f->offset; ++ } + } ++ return sz; /* all placed */ ++} ++ ++/* Reconstruct MIX struct fields from registers using flat descriptor list. */ ++static void ccall_unplace_mix_fields(CCallState *cc, CCallMixFieldList *list, ++ uint8_t *dp) ++{ ++ int gpi = 0, fpi = 0; ++ for (int i = 0; i < list->nfields; i++) { ++ CCallMixField *f = &list->fields[i]; ++ if (f->isfp) { ++ if (f->esz == 4) *(float *)(dp + f->offset) = cc->fpr[fpi++].f; ++ else *(double *)(dp + f->offset) = cc->fpr[fpi++].d; ++ } else { ++ if (f->esz < 8) *(int32_t *)(dp + f->offset) = (int32_t)cc->gpr[gpi++]; ++ else *(int64_t *)(dp + f->offset) = cc->gpr[gpi++]; ++ } ++ } ++} + #else #error "Missing calling convention definitions for this architecture" #endif -@@ -1055,6 +1195,164 @@ static void ccall_copy_struct(CCallState +@@ -1085,6 +1276,206 @@ static void ccall_copy_struct(CCallState #endif @@ -5004,29 +5068,35 @@ Co-authored-by: Heinrich Schuchardt +/* RISC-V 64 LP64D fp reg struct classification. */ +/* X: unknown/uninit, F: float, D: double, I: integer */ + ++/* Flat descriptor for a single field element produced by classification. ++** Place/unplace iterate this list instead of re-walking the ctype tree. */ ++#define CCALL_MIX_MAXFIELDS 2 /* MIX state machine has exactly 2 slots. */ ++ ++/* Add a field descriptor to the flat list (if space remains). */ ++static LJ_AINLINE void ccall_add_mix_field(CCallMixFieldList *list, ++ CTSize offset, CTSize esz, int isfp) ++{ ++ if (list && list->nfields < CCALL_MIX_MAXFIELDS) { ++ CCallMixField *f = &list->fields[list->nfields++]; ++ f->offset = offset; ++ f->esz = (uint8_t)esz; ++ f->isfp = (uint8_t)isfp; ++ } ++} ++ +typedef enum eCCallStructMixElem { + MIX_ELEM_UNINIT = 0, + MIX_ELEM_FLOAT = 1, -+ MIX_ELEM_DOUBLE = 2, -+ MIX_ELEM_INT = 3, ++ MIX_ELEM_INT = 2, +} eCCallStructMixElem; +typedef enum eCCallStructMix { -+ MIX_UNINIT = 0, // i.e. MIX_XX -+ MIX_FX = 1, -+ MIX_DX = 2, -+ MIX_IX = 3, -+ MIX_XF = 4, -+ MIX_FF = 5, -+ MIX_DF = 6, -+ MIX_IF = 7, -+ MIX_XD = 8, -+ MIX_FD = 9, -+ MIX_DD = 10, -+ MIX_ID = 11, -+ MIX_XI = 12, -+ MIX_FI = 13, -+ MIX_DI = 14, -+ MIX_FAILED = 15, // MIX_II but that's not mixed ++ MIX_UNINIT = 0, /* XX */ ++ MIX_FX = 1, /* F X — single float/double */ ++ MIX_IX = 2, /* I X — single int */ ++ MIX_FF = 5, /* F F — two floats/doubles */ ++ MIX_IF = 6, /* I F — int then float/double */ ++ MIX_FI = 9, /* F I — float/double then int */ ++ MIX_FAILED = 10 /* anything else */ +} eCCallStructMix; + +typedef union CCallStructMix { @@ -5045,7 +5115,8 @@ Co-authored-by: Heinrich Schuchardt + }; +} CCallStructClass; + -+static CCallStructClass ccall_classify_struct(CTState *cts, CType *ct) ++static CCallStructClass ccall_classify_struct(CTState *cts, CType *ct, ++ CCallMixFieldList *list, CTSize base_ofs) +{ + CTSize sz = ct->size; + CCallStructMix mix = { .val = MIX_UNINIT }; @@ -5054,111 +5125,146 @@ Co-authored-by: Heinrich Schuchardt + unsigned int m = 1; + CType *sct; + ct = ctype_get(cts, ct->sib); -+ if (ctype_isfield(ct->info)) { -+ sct = ctype_rawchild(cts, ct); -+ if (ctype_isarray(sct->info)) { -+ CType *cct = ctype_rawchild(cts, sct); -+ if (!cct->size) continue; -+ m = sct->size / cct->size; -+ sct = cct; -+ } -+ if (ctype_isfp(sct->info)) { -+ while (m--) { -+ /* Mix state trans: fp -+ * mix XX -> mix = [FD]X -+ * mix X[IFD] -> mix = FAILED -+ * mix [IFD]X -> mix = [IFD][FD] -+ * mix [IFD][IFD] -> mix = FAILED -+ */ -+ eCCallStructMixElem ne = (sct->size == 4) ? MIX_ELEM_FLOAT : MIX_ELEM_DOUBLE; -+ if (mix.val == MIX_UNINIT) { -+ mix = (CCallStructMix){ .e1 = ne, .e2 = MIX_ELEM_UNINIT }; -+ } else { -+ eCCallStructMixElem o1 = mix.e1, o2 = mix.e2, n2 = ne; -+ mix = (o2 != MIX_ELEM_UNINIT ? (CCallStructMix){ .val = MIX_FAILED } -+ : (CCallStructMix){ .e1 = o1, .e2 = n2 }); -+ } -+ } -+ } else if (ctype_iscomplex(sct->info)) { -+ while (m--) { -+ /* Mix state trans: complex -+ * mix XX -> mix = [FD][FD] -+ * mix other -> mix = FAILED -+ */ -+ eCCallStructMixElem ne = (sct->size == 8) ? MIX_ELEM_FLOAT : MIX_ELEM_DOUBLE; -+ mix = (mix.val == MIX_UNINIT) ? (CCallStructMix){ .e1 = ne, .e2 = ne } -+ : (CCallStructMix){ .val = MIX_FAILED }; -+ } -+ } else if (ctype_isinteger_or_bool(sct->info) || ctype_isenum(sct->info)) { -+ while (m--) { -+ /* Mix state trans: int -+ * mix XX -> mix = IX -+ * mix X[IFD] -> mix = FAILED -+ * mix [IFD]X -> mix = [FD]I; this auto fails II -+ * mix [IFD][IFD] -> mix = FAILED -+ */ -+ if (mix.val == MIX_UNINIT) { -+ mix = (CCallStructMix){ .e1 = MIX_ELEM_INT, .e2 = MIX_ELEM_UNINIT }; -+ } else { -+ eCCallStructMixElem o1 = mix.e1, o2 = mix.e2, n2 = MIX_ELEM_INT; -+ mix = (o2 != MIX_ELEM_UNINIT ? (CCallStructMix){ .val = MIX_FAILED } -+ : (CCallStructMix){ .e1 = o1, .e2 = n2 }); -+ } -+ } -+ } else if (ctype_isstruct(sct->info)) { -+ goto substruct; -+ } else { -+ goto not_ag; -+ } -+ } else if (ctype_isbitfield(ct->info)) { -+ goto not_ag; -+ } else if (ctype_isxattrib(ct->info, CTA_SUBTYPE)) { -+ sct = ctype_rawchild(cts, ct); -+ substruct: -+ if (sct->size > 0) { -+ CCallStructClass s = ccall_classify_struct(cts, sct); -+ CCallStructMix smix = s.mix; -+ uint8_t spod = s.ispod; -+ if (smix.val == MIX_FAILED) mix.val = MIX_FAILED; -+ if (!spod) goto not_ag; -+ while (m--) { -+ /* Mix state transfer: substruct -+ * mix XX, smix any -> mix = smix -+ * mix X[IFD], smix any -> mix = FAILED -+ * mix [IFD]X, smix [XIFD]X -> mix = [IFD][XIFD] -+ * smix other -> mix = FAILED -+ * mix [IFD][IFD], smix XX -> mix = mix -+ * smix other -> mix = FAILED; this keep II fail -+ */ -+ if (mix.val == MIX_UNINIT) { -+ mix = smix; -+ } else { -+ eCCallStructMixElem o1 = mix.e1, o2 = mix.e2; -+ eCCallStructMixElem n1 = smix.e1, n2 = smix.e2; -+ if (o2 != MIX_ELEM_UNINIT) { -+ mix = (smix.val != MIX_UNINIT) ? mix -+ : (CCallStructMix){ .val = MIX_FAILED }; -+ } else { -+ mix = (n2 != MIX_ELEM_UNINIT) ? (CCallStructMix){ .val = MIX_FAILED } -+ : (CCallStructMix){ .e1 = o1, .e2 = n1 }; -+ } -+ } -+ } ++ { ++ CTSize fofs = ct->size; /* Field offset within this struct. */ ++ if (ctype_isfield(ct->info)) { ++ sct = ctype_rawchild(cts, ct); ++ if (ctype_isarray(sct->info) && !sct->size) continue; ++ while (ctype_isarray(sct->info)) { ++ CType *cct = ctype_rawchild(cts, sct); ++ m *= sct->size / cct->size; ++ sct = cct; ++ } ++ { ++ CTSize esz = sct->size; ++ if (ctype_isfp(sct->info)) { ++ unsigned int mi; ++ for (mi = 0; mi < m; mi++) { ++ /* Mix state trans: fp (float or double) ++ * mix XX -> mix = FX ++ * mix X[IF] -> mix = FAILED ++ * mix [IF]X -> mix = [IF]F ++ * mix [IF][IF] -> mix = FAILED ++ */ ++ eCCallStructMixElem ne = MIX_ELEM_FLOAT; ++ if (mix.val == MIX_UNINIT) { ++ mix = (CCallStructMix){ .e1 = ne, .e2 = MIX_ELEM_UNINIT }; ++ } else { ++ eCCallStructMixElem o1 = mix.e1, o2 = mix.e2, n2 = ne; ++ mix = (o2 != MIX_ELEM_UNINIT ? (CCallStructMix){ .val = MIX_FAILED } ++ : (CCallStructMix){ .e1 = o1, .e2 = n2 }); ++ } ++ ccall_add_mix_field(list, base_ofs + fofs + mi*esz, esz, 1); ++ } ++ } else if (ctype_iscomplex(sct->info)) { ++ unsigned int mi; ++ CType *esct = ctype_rawchild(cts, sct); ++ CTSize cesz = esct->size; ++ for (mi = 0; mi < m; mi++) { ++ /* Mix state trans: complex ++ * mix XX -> mix = FF ++ * mix other -> mix = FAILED ++ */ ++ eCCallStructMixElem ne = MIX_ELEM_FLOAT; ++ mix = (mix.val == MIX_UNINIT) ? (CCallStructMix){ .e1 = ne, .e2 = ne } ++ : (CCallStructMix){ .val = MIX_FAILED }; ++ ccall_add_mix_field(list, base_ofs + fofs + mi*cesz*2, cesz, 1); ++ ccall_add_mix_field(list, base_ofs + fofs + mi*cesz*2 + cesz, cesz, 1); ++ } ++ } else if (ctype_isinteger_or_bool(sct->info) || ctype_isenum(sct->info)) { ++ unsigned int mi; ++ for (mi = 0; mi < m; mi++) { ++ /* Mix state trans: int ++ * mix XX -> mix = IX ++ * mix X[IF] -> mix = FAILED ++ * mix [IF]X -> mix = [IF]I; this auto fails II ++ * mix [IF][IF] -> mix = FAILED ++ */ ++ if (mix.val == MIX_UNINIT) { ++ mix = (CCallStructMix){ .e1 = MIX_ELEM_INT, .e2 = MIX_ELEM_UNINIT }; ++ } else { ++ eCCallStructMixElem o1 = mix.e1, o2 = mix.e2, n2 = MIX_ELEM_INT; ++ mix = (o2 != MIX_ELEM_UNINIT ? (CCallStructMix){ .val = MIX_FAILED } ++ : (CCallStructMix){ .e1 = o1, .e2 = n2 }); ++ } ++ ccall_add_mix_field(list, base_ofs + fofs + mi*esz, esz, 0); ++ } ++ } else if (ctype_isstruct(sct->info)) { ++ goto substruct; ++ } else { ++ goto not_ag; ++ } ++ } ++ } else if (ctype_isbitfield(ct->info) && ctype_bitbsz(ct->info)) { ++ /* Non-zero bitfield: treat as integer element for MIX classification. */ ++ CTSize fofs = ct->size; ++ sct = ctype_rawchild(cts, ct); ++ { ++ /* Mix state trans: bitfield ++ * mix XX -> mix = IX ++ * mix X[IF] -> mix = FAILED ++ * mix [IF]X -> mix = [IF]I ++ * mix [IF][IF] -> mix = FAILED ++ */ ++ if (mix.val == MIX_UNINIT) { ++ mix = (CCallStructMix){ .e1 = MIX_ELEM_INT, .e2 = MIX_ELEM_UNINIT }; ++ } else { ++ eCCallStructMixElem o1 = mix.e1, o2 = mix.e2, n2 = MIX_ELEM_INT; ++ mix = (o2 != MIX_ELEM_UNINIT ? (CCallStructMix){ .val = MIX_FAILED } ++ : (CCallStructMix){ .e1 = o1, .e2 = n2 }); ++ } ++ ccall_add_mix_field(list, base_ofs + fofs, sct->size, 0); ++ } ++ } else if (ctype_isxattrib(ct->info, CTA_SUBTYPE)) { ++ sct = ctype_rawchild(cts, ct); ++ substruct: ++ if (sct->size > 0) { ++ CCallStructClass s = ccall_classify_struct(cts, sct, list, ++ base_ofs + fofs); ++ CCallStructMix smix = s.mix; ++ uint8_t spod = s.ispod; ++ if (smix.val == MIX_FAILED) mix.val = MIX_FAILED; ++ if (!spod) goto not_ag; ++ while (m--) { ++ /* Mix state transfer: substruct ++ * mix XX, smix any -> mix = smix ++ * mix X[IF], smix any -> mix = FAILED ++ * mix [IF]X, smix [XIF]X -> mix = [IF][XIF] ++ * smix other -> mix = FAILED ++ * mix [IF][IF], smix XX -> mix = mix ++ * smix other -> mix = FAILED; this keeps II fail ++ */ ++ if (mix.val == MIX_UNINIT) { ++ mix = smix; ++ } else { ++ eCCallStructMixElem o1 = mix.e1, o2 = mix.e2; ++ eCCallStructMixElem n1 = smix.e1, n2 = smix.e2; ++ if (o2 != MIX_ELEM_UNINIT) { ++ mix = (smix.val != MIX_UNINIT) ? (CCallStructMix){ .val = MIX_FAILED } ++ : mix; ++ } else { ++ mix = (n2 != MIX_ELEM_UNINIT) ? (CCallStructMix){ .val = MIX_FAILED } ++ : (CCallStructMix){ .e1 = o1, .e2 = n1 }; ++ } ++ } ++ } ++ } + } + } + } -+ if (MIX_UNINIT < mix.val && mix.val < MIX_FAILED) /* Mixed passing */ ++ if (MIX_UNINIT < mix.val && mix.val < MIX_FAILED && mix.e1 != MIX_ELEM_UNINIT) /* Mixed passing */ + return (CCallStructClass){ .ispod = 1, .mix = mix }; +not_ag: /* Not a float/double aggregate or int/fp mix pair aggregate */ -+ return (CCallStructClass){ .ispod = (sz <= 16), .mix = mix }; /* Return structs of size <= 16 in GPRs. */ ++ if (list) list->nfields = 0; /* Invalidate flat list on classification failure. */ ++ return (CCallStructClass){ .ispod = (sz <= 16), .mix = mix }; +} + +#endif + - /* -- Common C call handling ---------------------------------------------- */ - - /* Infer the destination CTypeID for a vararg argument. -@@ -1106,6 +1404,7 @@ static int ccall_set_args(lua_State *L, + #ifndef ccall_struct_align + /* Alignment of pass-by-value structs. */ + #define ccall_struct_align(cts, ct) ((ct)->info & CTF_ALIGN) +@@ -1141,6 +1532,7 @@ static int ccall_set_args(lua_State *L, #endif #endif @@ -5166,40 +5272,17 @@ Co-authored-by: Heinrich Schuchardt /* Clear unused regs to get some determinism in case of misdeclaration. */ memset(cc->gpr, 0, sizeof(cc->gpr)); #if CCALL_NUM_FPR -@@ -1168,6 +1467,11 @@ static int ccall_set_args(lua_State *L, +@@ -1203,6 +1595,9 @@ static int ccall_set_args(lua_State *L, CType *d; CTSize sz; MSize n, isfp = 0, isva = 0; +#if LJ_TARGET_RISCV64 -+ MSize onsp, mnsp; + CCallStructMix mix = { .val = MIX_UNINIT }; -+ int isstack = 0; +#endif void *dp, *rp = NULL; #if LJ_TARGET_PPC && LJ_ARCH_BITS == 64 int isf32 = 0; -@@ -1216,6 +1520,10 @@ static int ccall_set_args(lua_State *L, - #if LJ_TARGET_S390X - onstack = 1; - #endif -+#if LJ_TARGET_RISCV64 -+ isstack = 1; -+ onsp = nsp; -+#endif - if (CCALL_ALIGN_STACKARG) { /* Align argument on stack. */ - MSize align = (1u << ctype_align(d->info)) - 1; - if (rp || (CCALL_PACK_STACKARG && isva && align < CTSIZE_PTR-1)) -@@ -1228,6 +1536,9 @@ static int ccall_set_args(lua_State *L, - #else - dp = ((uint8_t *)cc->stack) + nsp; - #endif -+#if LJ_TARGET_RISCV64 -+ mnsp = nsp + n * CTSIZE_PTR / 2; -+#endif - nsp += CCALL_PACK_STACKARG ? sz : n * CTSIZE_PTR; - if ((int32_t)nsp > CCALL_SIZE_STACK) { /* Too many arguments. */ - err_nyi: -@@ -1295,7 +1606,11 @@ static int ccall_set_args(lua_State *L, +@@ -1333,7 +1728,11 @@ static int ccall_set_args(lua_State *L, *(int64_t *)dp = (int64_t)*(int32_t *)dp; } #endif @@ -5212,114 +5295,6 @@ Co-authored-by: Heinrich Schuchardt if ((ctype_isinteger_or_bool(d->info) || ctype_isenum(d->info) #if LJ_TARGET_MIPS64 || (isfp && nsp == 0) -@@ -1335,6 +1650,107 @@ static int ccall_set_args(lua_State *L, - CTSize i = (sz >> 2) - 1; - do { ((uint64_t *)dp)[i] = ((uint32_t *)dp)[i]; } while (i--); - } -+#elif LJ_TARGET_RISCV64 -+ switch (mix.val) { -+ case MIX_UNINIT: -+ break; -+ /* Fix MIX values */ -+ case MIX_DF: { -+ ((uint32_t *)dp)[3] = 0xffffffffu; -+ break; -+ } -+ case MIX_FF: -+ if (isstack) break; -+ ((uint64_t *)dp)[1] = 0xffffffff00000000ul | ((uint32_t *)dp)[1]; -+ case MIX_FX: -+ case MIX_FD: { -+ ((uint64_t *)dp)[0] = 0xffffffff00000000ul | ((uint32_t *)dp)[0]; -+ break; -+ } -+ case MIX_FI: { -+ lj_assertL(sz == 8 || sz == 16, "invalid MIX_FI size %d", (int)sz); -+ if (ngpr >= CCALL_NARG_GPR) break; -+ if (sz == 8) { -+ FPRArg farg = { .hi = 0xffffffffu, .lo = ((uint32_t *)dp)[0] }; -+ if (!isva && nfpr + 1 <= CCALL_NARG_FPR) { -+ cc->fpr[nfpr++] = farg; -+ goto fi_next; -+ } else if (ngpr + 1 <= CCALL_NARG_GPR) { -+ cc->gpr[ngpr++] = farg.u; -+fi_next: -+ ((uint32_t *)dp)[0] = ((uint32_t *)dp)[1]; -+ ((uint32_t *)dp)[1] = 0; -+ if (ngpr + 1 <= CCALL_NARG_GPR) { -+ cc->gpr[ngpr++] = ((uint32_t *)dp)[0]; -+ ((uint32_t *)dp)[0] = 0, nsp = onsp; -+ } -+ } -+ break; -+ } else /*if (sz == 16)*/ { -+ ((uint64_t *)dp)[0] |= 0xffffffff00000000ul; -+ /* fallthrough */ -+ } -+ } -+ case MIX_DI: { -+ lj_assertL(sz == 16, "invalid MIX_DI size %d", (int)sz); -+ if (ngpr >= CCALL_NARG_GPR) break; -+ if (!isva && nfpr + 1 <= CCALL_NARG_FPR) { -+ cc->fpr[nfpr++] = (FPRArg){ .u = ((uint64_t *)dp)[0] }; -+ goto di_next; -+ } else if (ngpr + 1 <= CCALL_NARG_GPR) { -+ cc->gpr[ngpr++] = ((uint64_t *)dp)[0]; -+di_next: -+ ((uint64_t *)dp)[0] = ((uint64_t *)dp)[1]; -+ ((uint64_t *)dp)[1] = 0, nsp = mnsp; -+ if (ngpr + 1 <= CCALL_NARG_GPR) { -+ cc->gpr[ngpr++] = ((uint64_t *)dp)[0]; -+ ((uint64_t *)dp)[0] = 0, nsp = onsp; -+ } -+ } -+ break; -+ } -+ case MIX_IF: { -+ lj_assertL(sz == 8 || sz == 16, "invalid MIX_IF size %d", (int)sz); -+ if (sz == 8) { -+ FPRArg farg = { .hi = 0xffffffffu, .lo = ((uint32_t *)dp)[1] }; -+ if (ngpr + 1 <= CCALL_NARG_GPR) { -+ cc->gpr[ngpr++] = ((uint32_t *)dp)[0]; -+ ((uint32_t *)dp)[0] = ((uint32_t *)dp)[1]; -+ ((uint32_t *)dp)[1] = 0; -+ if (!isva && nfpr + 1 <= CCALL_NARG_FPR) { -+ cc->fpr[nfpr++] = farg; -+ goto if_next; -+ } else if (ngpr + 1 <= CCALL_NARG_GPR) { -+ cc->gpr[ngpr++] = farg.u; -+if_next: -+ ((uint32_t *)dp)[0] = 0, nsp = onsp; -+ } -+ } -+ break; -+ } else /*if (sz == 16)*/ { -+ ((uint64_t *)dp)[1] |= 0xffffffff00000000ul; -+ /* fallthrough */ -+ } -+ } -+ case MIX_ID: { -+ lj_assertL(sz == 16, "invalid MIX_ID size %d", (int)sz); -+ if (ngpr + 1 <= CCALL_NARG_GPR) { -+ cc->gpr[ngpr++] = ((uint64_t *)dp)[0]; -+ ((uint64_t *)dp)[0] = ((uint64_t *)dp)[1]; -+ ((uint64_t *)dp)[1] = 0, nsp = mnsp; -+ if (!isva && nfpr + 1 <= CCALL_NARG_FPR) { -+ cc->fpr[nfpr++] = (FPRArg){ .u = ((uint64_t *)dp)[0] }; -+ goto id_next; -+ } else if (ngpr + 1 <= CCALL_NARG_GPR) { -+ cc->gpr[ngpr++] = ((uint64_t *)dp)[0]; -+id_next: -+ ((uint64_t *)dp)[0] = 0, nsp = onsp; -+ } -+ } -+ break; -+ } -+ default: break; -+ } - #else - UNUSED(isfp); - #endif --- a/src/lj_ccall.h +++ b/src/lj_ccall.h @@ -157,6 +157,23 @@ typedef union FPRArg { @@ -5442,12 +5417,12 @@ Co-authored-by: Heinrich Schuchardt (LJ_ABI_SOFTFP || ctype_isinteger_or_bool(ctr->info))) --- /dev/null +++ b/src/lj_emit_riscv.h -@@ -0,0 +1,574 @@ +@@ -0,0 +1,576 @@ +/* +** RISC-V instruction emitter. -+** Copyright (C) 2005-2025 Mike Pall. See Copyright Notice in luajit.h ++** Copyright (C) 2022-2026 ISRC, ISCAS. See Copyright Notice in luajit.h +** -+** Contributed by gns from PLCT Lab, ISCAS. ++** Contributed by gns from PLCT Lab, ISRC, ISCAS. +*/ + +static intptr_t get_k64val(ASMState *as, IRRef ref) @@ -5764,6 +5739,8 @@ Co-authored-by: Heinrich Schuchardt +/* Prefer rematerialization of BASE/L from global_State over spills. */ +#define emit_canremat(ref) ((ref) <= REF_BASE) + ++#define glofs(as, k) \ ++ ((intptr_t)((uintptr_t)(k) - (uintptr_t)&J2GG(as->J)->g)) + +/* Load a 32 bit constant into a GPR. */ +#define emit_loadi(as, r, i) emit_loadk32(as, r, i); @@ -6122,6 +6099,22 @@ Co-authored-by: Heinrich Schuchardt #else #define JIT_F_CPUSTRING "" +@@ -364,13 +404,13 @@ enum { + #if LJ_TARGET_MIPS + LJ_K64_2P31, /* 2^31 */ + #endif +-#if LJ_TARGET_ARM64 || LJ_TARGET_MIPS64 ++#if LJ_TARGET_ARM64 || LJ_TARGET_MIPS64 || LJ_TARGET_RISCV64 + LJ_K64_VM_EXIT_HANDLER, + LJ_K64_VM_EXIT_INTERP, + #endif + LJ_K64__MAX, + }; +-#define LJ_K64__USED (LJ_TARGET_X86ORX64 || LJ_TARGET_ARM64 || LJ_TARGET_MIPS) ++#define LJ_K64__USED (LJ_TARGET_X86ORX64 || LJ_TARGET_ARM64 || LJ_TARGET_MIPS || LJ_TARGET_RISCV64) + + enum { + #if LJ_TARGET_X86ORX64 || LJ_TARGET_MIPS64 --- a/src/lj_mcode.c +++ b/src/lj_mcode.c @@ -38,6 +38,12 @@ @@ -6180,7 +6173,7 @@ Co-authored-by: Heinrich Schuchardt @@ -0,0 +1,542 @@ +/* +** Definitions for RISC-V CPUs. -+** Copyright (C) 2005-2025 Mike Pall. See Copyright Notice in luajit.h ++** Copyright (C) 2022-2026 ISRC, ISCAS. See Copyright Notice in luajit.h +*/ + +#ifndef _LJ_TARGET_RISCV_H @@ -6720,6 +6713,17 @@ Co-authored-by: Heinrich Schuchardt +}; + +#endif +--- a/src/lj_trace.c ++++ b/src/lj_trace.c +@@ -351,7 +351,7 @@ void lj_trace_initstate(global_State *g) + J->k32[LJ_K32_VM_EXIT_HANDLER] = (uintptr_t)(void *)lj_vm_exit_handler; + J->k32[LJ_K32_VM_EXIT_INTERP] = (uintptr_t)(void *)lj_vm_exit_interp; + #endif +-#if LJ_TARGET_ARM64 || LJ_TARGET_MIPS64 ++#if LJ_TARGET_ARM64 || LJ_TARGET_MIPS64 || LJ_TARGET_RISCV64 + J->k64[LJ_K64_VM_EXIT_HANDLER].u64 = (uintptr_t)lj_ptr_sign((void *)lj_vm_exit_handler, 0); + J->k64[LJ_K64_VM_EXIT_INTERP].u64 = (uintptr_t)lj_ptr_sign((void *)lj_vm_exit_interp, 0); + #endif --- a/src/lj_vm.h +++ b/src/lj_vm.h @@ -37,6 +37,9 @@ LJ_ASMF int lj_vm_cpuid(uint32_t f, uint @@ -6746,12 +6750,12 @@ Co-authored-by: Heinrich Schuchardt uint32_t y, ua, ub; --- /dev/null +++ b/src/vm_riscv64.dasc -@@ -0,0 +1,4810 @@ +@@ -0,0 +1,4857 @@ +|// Low-level VM code for RISC-V 64 CPUs. +|// Bytecode interpreter, fast functions and helper functions. -+|// Copyright (C) 2005-2025 Mike Pall. See Copyright Notice in luajit.h ++|// Copyright (C) 2022-2026 ISRC, ISCAS. See Copyright Notice in luajit.h +|// -+|// Contributed by gns from PLCT Lab, ISCAS. ++|// Contributed by gns from PLCT Lab, ISRC, ISCAS. +| +|.arch riscv64 +|.section code_op, code_sub @@ -9261,6 +9265,46 @@ Co-authored-by: Heinrich Schuchardt + | + | + |//----------------------------------------------------------------------- ++ |//-- Number conversion functions -------------------------------------------- ++ |//----------------------------------------------------------------------- ++ | ++ |// int64_t lj_vm_num2int_check(double x) ++ |->vm_num2int_check: ++ | fcvt.w.d CRET1, FARG1, rtz ++ | fcvt.d.w FARG2, CRET1 ++ | feq.d CARG2, FARG1, FARG2 ++ | beqz CARG2, >1 ++ | ret ++ |1: ++ | lui CRET1, 0x80000 ++ | slli CRET1, CRET1, 8 ++ | addi CRET1, CRET1, 0x80 ++ | slli CRET1, CRET1, 24 ++ | ret ++ | ++ |// int64_t lj_vm_num2i64(double x) ++ |->vm_num2i64: ++ | fcvt.l.d CRET1, FARG1, rtz ++ | ret ++ | ++ |// uint64_t lj_vm_num2u64(double x) ++ |->vm_num2u64: ++ | fcvt.lu.d CRET1, FARG1, rtz ++ | bnez CRET1, >1 ++ | fcvt.l.d CRET1, FARG1, rtz ++ |1: ++ | ret ++ | ++ |// int32_t lj_vm_tobit(double x) ++ |->vm_tobit: ++ | lui CARG2, 0x43380 // 2^52 + 2^51. ++ | slli CARG2, CARG2, 32 ++ | fmv.d.x FARG2, CARG2 ++ | fadd.d FARG2, FARG1, FARG2 ++ | fmv.x.w CRET1, FARG2 ++ | ret ++ | ++ |//----------------------------------------------------------------------- + |//-- Miscellaneous functions -------------------------------------------- + |//----------------------------------------------------------------------- + | @@ -9585,8 +9629,10 @@ Co-authored-by: Heinrich Schuchardt + | // Check if RA or RD is a cdata. + | xori TMP0, CARG3, LJ_TCDATA + | xori TMP1, CARG4, LJ_TCDATA -+ | and TMP0, TMP0, TMP1 -+ | bxeqz TMP0, ->vmeta_equal_cd ++ | seqz TMP0, TMP0 ++ | seqz TMP1, TMP1 ++ | or TMP0, TMP0, TMP1 ++ | bxnez TMP0, ->vmeta_equal_cd + |.endif + | lui TMP3, (-(BCBIAS_J*4 >> 12)) & 0xfffff // -BCBIAS_J*4 + | decode_BC4b TMP2 @@ -9854,21 +9900,26 @@ Co-authored-by: Heinrich Schuchardt + | ld TMP0, 0(RB) + | lui TMP1, 0x80000 + | gettp CARG3, TMP0 -+ | bne CARG3, TISNUM, >1 ++ | bne CARG3, TISNUM, >2 + | sext.w TMP0, TMP0 + | bxeq TMP0, TMP1, ->vmeta_unm // Meta handler deals with -2^31. + | negw TMP0, TMP0 ++ | beqz TMP0, >3 + | zext.w TMP0, TMP0 + | settp_b TMP0, TISNUM -+ | j >2 + |1: ++ | ins_next1 ++ | sd TMP0, 0(RA) ++ | ins_next2 ++ |2: + | sltiu TMP3, CARG3, LJ_TISNUM + | slli TMP1, TMP1, 32 + | bxeqz TMP3, ->vmeta_unm + | xor TMP0, TMP0, TMP1 // sign => ~sign -+ |2: -+ | sd TMP0, 0(RA) -+ | ins_next ++ | j <1 ++ |3: ++ | slli TMP0, TMP1, 32 ++ | j <1 + break; + case BC_LEN: + | // RA = dst*8, RD = src*8