[PATCH 3/13] Add runtime support for loongarch64
| Newsgroups | gmane.lisp.steel-bank.devel |
|---|---|
| Message-ID | <[email protected]> |
This patch adds the src/runtime support for LoongArch 64-bit in SBCL,includes a series of support files: Config.loongarch64-linux, loongarch64-arch.c, loongarch64-arch.h, loongarch64-assem.S, loongarch64-linux-os.c, loongarch64-linux-os.h, and loongarch64-lispregs.h. _______________________________________________ Sbcl-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/sbcl-devel
0003-Add-runtime-support-for-loongarch64.patch
(application/octet-stream, 20.9 KB)
From 8dd242246bc172de0496fc119334e5087b801599 Mon Sep 17 00:00:00 2001 From: ZiLong Wang <[email protected]> Date: Sat, 17 Jan 2026 10:07:06 +0800 Subject: [PATCH 03/13] Add runtime support for LoongArch --- src/runtime/Config.loongarch64-linux | 35 +++ src/runtime/loongarch64-arch.c | 321 +++++++++++++++++++++++++++ src/runtime/loongarch64-arch.h | 5 + src/runtime/loongarch64-assem.S | 61 +++++ src/runtime/loongarch64-linux-os.c | 134 +++++++++++ src/runtime/loongarch64-linux-os.h | 9 + src/runtime/loongarch64-lispregs.h | 56 +++++ 7 files changed, 621 insertions(+) create mode 100644 src/runtime/Config.loongarch64-linux create mode 100644 src/runtime/loongarch64-arch.c create mode 100644 src/runtime/loongarch64-arch.h create mode 100644 src/runtime/loongarch64-assem.S create mode 100644 src/runtime/loongarch64-linux-os.c create mode 100644 src/runtime/loongarch64-linux-os.h create mode 100644 src/runtime/loongarch64-lispregs.h diff --git a/src/runtime/Config.loongarch64-linux b/src/runtime/Config.loongarch64-linux new file mode 100644 index 000000000..c46d54631 --- /dev/null +++ b/src/runtime/Config.loongarch64-linux @@ -0,0 +1,35 @@ +# -*- makefile -*- for the C-level run-time support for SBCL + +# This software is part of the SBCL system. See the README file for +# more information. +# +# This software is derived from the CMU CL system, which was +# written at Carnegie Mellon University and released into the +# public domain. The software is in the public domain and is +# provided with absolutely no warranty. See the COPYING and CREDITS +# files for more information. + +ARCH_SRC = loongarch64-arch.c + +ASSEM_SRC = loongarch64-assem.S +ARCH_SRC = loongarch64-arch.c + +OS_SRC = linux-os.c linux-mman.c loongarch64-linux-os.c +OS_LIBS = -ldl -Wl,-no-as-needed + +ifdef LISP_FEATURE_SB_THREAD + OS_LIBS += -lpthread +endif + +ifdef LISP_FEATURE_SB_CORE_COMPRESSION + OS_LIBS += -lzstd +endif + +LINKFLAGS += -Wl,--export-dynamic +DISABLE_PIE=no + +ifdef LISP_FEATURE_LARGEFILE + CFLAGS += -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 +endif + +GC_SRC = fullcgc.c gencgc.c traceroot.c diff --git a/src/runtime/loongarch64-arch.c b/src/runtime/loongarch64-arch.c new file mode 100644 index 000000000..aafa816bb --- /dev/null +++ b/src/runtime/loongarch64-arch.c @@ -0,0 +1,321 @@ +/* + * This software is part of the SBCL system. See the README file for + * more information. + * + * This software is derived from the CMU CL system, which was + * written at Carnegie Mellon University and released into the + * public domain. The software is in the public domain and is + * provided with absolutely no warranty. See the COPYING and CREDITS + * files for more information. + */ +#include <stdio.h> + +#include "genesis/sbcl.h" +#include "runtime.h" +#include "arch.h" +#include "globals.h" +#include "validate.h" +#include "os.h" +#include "lispregs.h" +#include <signal.h> +#include "interrupt.h" +#include "interr.h" +#include "breakpoint.h" +#include "pseudo-atomic.h" +#include <unistd.h> + +#define BREAK_INST 0x002A0000 +#define BREAK_MASK 0xFFFF8000 + +os_vm_address_t +arch_get_bad_addr(int signam, siginfo_t *siginfo, os_context_t *context) +{ + return (os_vm_address_t)siginfo->si_addr; +} + +void arch_skip_instruction(os_context_t *context) +{ + uint32_t trap_instruction = *(uint32_t *)OS_CONTEXT_PC(context); + unsigned code = trap_instruction & 0xFF; + if(code == trap_FunEndBreakpoint || code == trap_Breakpoint) + OS_CONTEXT_PC(context) += 4; + else + OS_CONTEXT_PC(context) += 8; +} + +unsigned char *arch_internal_error_arguments(os_context_t *context) +{ + return (unsigned char*)(OS_CONTEXT_PC(context)); +} + +bool arch_pseudo_atomic_atomic(struct thread *thread) { + return get_pseudo_atomic_atomic(thread); +} + +void arch_set_pseudo_atomic_interrupted(struct thread *thread) { + set_pseudo_atomic_interrupted(thread); +} + +void arch_clear_pseudo_atomic_interrupted(struct thread *thread) { + clear_pseudo_atomic_interrupted(thread); +} + +unsigned int arch_install_breakpoint(void *pc) +{ + unsigned int *ptr = (unsigned int *)pc; + unsigned int result = *ptr; + + *ptr = BREAK_INST | trap_Breakpoint; + + os_flush_icache((os_vm_address_t) ptr, sizeof(unsigned int)); + return result; +} + +void arch_remove_breakpoint(void *pc, unsigned int orig_inst) +{ + *(unsigned int *) pc = orig_inst; + os_flush_icache((os_vm_address_t) pc, sizeof(unsigned int)); +} + +static int sign_extend_u(int16_t value, int n_bits) { + int shift = 32 - n_bits; + return (value << shift) >> shift; +} + +static bool is_load_store_pc_rel(unsigned int inst) { + unsigned int opcode = (inst >> 22) & 0x3FF; + switch(opcode) { + case 0b0010100000: // LD.B + case 0b0010100001: // LD.H + case 0b0010100010: // LD.W + case 0b0010100011: // LD.D + case 0b0010101000: // LD.BU + case 0b0010101001: // LD.HU + case 0b0010101010: // LD.WU + case 0b0010100100: // ST.B + case 0b0010100101: // ST.H + case 0b0010100110: // ST.W + case 0b0010100111: // ST.D + return true; + default: + return false; + } +} + +#define BREAKPOINT_TEMP_REG reg_T7 + +void arch_do_displaced_inst(os_context_t *context, unsigned int orig_inst) +{ + unsigned int *pc = (unsigned int *)OS_CONTEXT_PC(context); + unsigned int *next_pc = pc; + + if (((orig_inst >> 26) & 0x3F) >= 0b010110 && ((orig_inst >> 26) & 0x3F) <= 0b011011) { + int rj = (orig_inst >> 5) & 0x1F; + int rd = (orig_inst >> 0) & 0x1F; + int16_t imm16 = orig_inst & 0xFFFF; + int offset = sign_extend_u(imm16, 16); + + uword_t val_j = *os_context_register_addr(context, rj); + uword_t val_d = *os_context_register_addr(context, rd); + + int take_branch = 0; + switch ((orig_inst >> 26) & 0x3F) { + case 0b010110: take_branch = (val_j == val_d); break; // BEQ + case 0b010111: take_branch = (val_j != val_d); break; // BNE + case 0b011000: take_branch = ((sword_t)val_j < (sword_t)val_d); break; // BLT + case 0b011001: take_branch = ((sword_t)val_j >= (sword_t)val_d); break; // BGE + case 0b011010: take_branch = (val_j < val_d); break; // BLTU + case 0b011011: take_branch = (val_j >= val_d); break; // BGEU + default: lose("Unknown conditional branch"); + } + + if (take_branch) + next_pc += offset; + else + next_pc += 1; + } + + // JIRL + else if (((orig_inst >> 26) & 0x3F) == 0b010011) { + int rj = (orig_inst >> 5) & 0x1F; + int rd = (orig_inst >> 0) & 0x1F; + int16_t imm16 = orig_inst & 0xFFFF; + int offset = sign_extend_u(imm16, 16); + + uword_t target = *os_context_register_addr(context, rj) + offset; + + if (rd != 0) + *os_context_register_addr(context, rd) = (uword_t)(pc + 2); + next_pc = (unsigned int *)target; + } + + // Load/Store PC-relative + else if (is_load_store_pc_rel(orig_inst)) { + int rd = orig_inst & 0x1F; + int rj = (orig_inst >> 5) & 0x1F; + int16_t si12 = (orig_inst >> 10) & 0xFFF; + int offset = sign_extend_u(si12, 12); + uword_t addr = *os_context_register_addr(context, rj) + offset; + + switch ((orig_inst >> 22) & 0x3FF) { + case 0b0010100000: *os_context_register_addr(context, rd) = (sword_t)*(int8_t *)addr; break; // LD.B + case 0b0010100001: *os_context_register_addr(context, rd) = (sword_t)*(int16_t *)addr; break; // LD.H + case 0b0010100010: *os_context_register_addr(context, rd) = (sword_t)*(int32_t *)addr; break; // LD.W + case 0b0010100011: *os_context_register_addr(context, rd) = *(uword_t *)addr; break; // LD.D + case 0b0010101000: *os_context_register_addr(context, rd) = *(uint8_t *)addr; break; // LD.BU + case 0b0010101001: *os_context_register_addr(context, rd) = *(uint16_t *)addr; break; // LD.HU + case 0b0010101010: *os_context_register_addr(context, rd) = *(uint32_t *)addr; break; // LD.WU + case 0b0010100100: *(int8_t *)addr = (int8_t)*os_context_register_addr(context, rd); break; // ST.B + case 0b0010100101: *(int16_t *)addr = (int16_t)*os_context_register_addr(context, rd); break; // ST.H + case 0b0010100110: *(int32_t *)addr = (int32_t)*os_context_register_addr(context, rd); break; // ST.W + case 0b0010100111: *(uword_t *)addr = *os_context_register_addr(context, rd); break; // ST.D + default: lose("Unknown load/store instruction"); + } + + next_pc += 1; + } + + // Trampoline + else { + size_t size = getpagesize(); + struct thread *th = get_sb_vm_thread(); + unsigned int *trampoline = (unsigned int *)th->breakpoint_misc; + + if (!trampoline) { + trampoline = mmap(NULL, size, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, + -1, 0); + th->breakpoint_misc = trampoline; + } else { + os_protect((os_vm_address_t)trampoline, size, PROT_READ | PROT_WRITE); + } + + unsigned int *inst_ptr = trampoline; + + next_pc += 1; + + *inst_ptr++ = orig_inst; + + *inst_ptr++ = 0x1C000000 | BREAKPOINT_TEMP_REG; + *inst_ptr++ = 0x28C00000 | BREAKPOINT_TEMP_REG << 5 | BREAKPOINT_TEMP_REG | 12 << 10; + *inst_ptr++ = 0x4C000000 | BREAKPOINT_TEMP_REG << 5; + + *(uword_t *)inst_ptr++ = (uword_t)next_pc; + + OS_CONTEXT_PC(context) = (uword_t)trampoline; + os_flush_icache((os_vm_address_t)trampoline, (char*)inst_ptr - (char*)trampoline); + os_protect((os_vm_address_t)trampoline, size, PROT_READ | PROT_EXEC); + return; + } + + OS_CONTEXT_PC(context) = (uword_t)next_pc; +} + + +void +arch_handle_breakpoint(os_context_t *context) +{ + handle_breakpoint(context); +} + +void +arch_handle_fun_end_breakpoint(os_context_t *context) +{ + OS_CONTEXT_PC(context) = (uword_t)handle_fun_end_breakpoint(context); +} + +void +arch_handle_single_step_trap(os_context_t *context, int trap) +{ + handle_single_step_trap(context, trap, reg_LEXENV); + arch_skip_instruction(context); +} + + +static void +sigtrap_handler(int signal, siginfo_t *info, os_context_t *context) +{ + + uint32_t trap_instruction; + memcpy(&trap_instruction, (void *)OS_CONTEXT_PC(context), sizeof trap_instruction); + unsigned code; + unsigned code_1 = trap_instruction & 0xFF; + + if ((trap_instruction & BREAK_MASK) != BREAK_INST) { + lose("Unrecognized trap instruction %08x in sigtrap_handler()", + trap_instruction); + } + if (code_1 == trap_Breakpoint || code_1 == trap_FunEndBreakpoint) + code = code_1; + else + code = *((unsigned char *)(4 + OS_CONTEXT_PC(context))); + + handle_trap(context, code); +} + +void +arch_install_interrupt_handlers(void) +{ + ll_install_handler(SIGTRAP, sigtrap_handler); +} + +#define LINKAGE_TEMP_REG reg_LIP + + +void arch_write_linkage_table_entry(int index, void *target_addr, int datap) +{ + // allocate successive entries downward + char *reloc_addr = + (char*)ALIEN_LINKAGE_SPACE_END - (index + 1) * ALIEN_LINKAGE_TABLE_ENTRY_SIZE; + + if (datap) { + *(unsigned long *)reloc_addr = (unsigned long)target_addr; + return; + } + int* inst_ptr; + unsigned inst; + + inst_ptr = (int*) reloc_addr; + +#ifndef LISP_FEATURE_64_BIT + /* + lui reg, %hi(address) + jr reg, %lo(address) + */ + unsigned int addr = (unsigned int)target_addr; + unsigned int hi = ((addr + 0x800) >> 12); + int lo = addr - (hi << 12); + + inst = 0x16000000 | LINKAGE_TEMP_REG | hi << 5; + *inst_ptr++ = inst; + + inst = 0x4C000000 | LINKAGE_TEMP_REG << 5 | lo << 10; + *inst_ptr++ = inst; +#else + /* + pcaddu12i reg, 0 + load reg, 12(reg) + jirl reg + address + */ + + inst = 0x1C000000 | LINKAGE_TEMP_REG; + *inst_ptr++ = inst; + + inst = 0x28C00000 | LINKAGE_TEMP_REG << 5 | LINKAGE_TEMP_REG | 12 << 10; + *inst_ptr++ = inst; + + inst = 0x4C000000 | LINKAGE_TEMP_REG << 5; + *inst_ptr++ = inst; + + *(unsigned long *)inst_ptr++ = (unsigned long)target_addr; +#endif + + os_flush_icache((os_vm_address_t) reloc_addr, (char*) inst_ptr - reloc_addr); +} + +lispobj call_into_lisp(lispobj fun, lispobj *args, int nargs) { + return ((lispobj(*)(lispobj, lispobj *, int, struct thread*))SYMBOL(CALL_INTO_LISP)->value) + (fun, args, nargs, get_sb_vm_thread()); +} diff --git a/src/runtime/loongarch64-arch.h b/src/runtime/loongarch64-arch.h new file mode 100644 index 000000000..619ae8fa9 --- /dev/null +++ b/src/runtime/loongarch64-arch.h @@ -0,0 +1,5 @@ +#ifndef _LOONGARCH64_ARCH_H +#define _LOONGARCH64_ARCH_H + +#endif /* _LOONGARCH64_ARCH_H */ + diff --git a/src/runtime/loongarch64-assem.S b/src/runtime/loongarch64-assem.S new file mode 100644 index 000000000..1e0c5358c --- /dev/null +++ b/src/runtime/loongarch64-assem.S @@ -0,0 +1,61 @@ +/* + * very-low-level utilities for runtime support + */ + +/* + * This software is part of the SBCL system. See the README file for + * more information. + * + * This software is derived from the CMU CL system, which was + * written at Carnegie Mellon University and released into the + * public domain. The software is in the public domain and is + * provided with absolutely no warranty. See the COPYING and CREDITS + * files for more information. + */ + +#ifdef __ELF__ +// Mark the object as not requiring an executable stack. +.section .note.GNU-stack,"",%progbits +#endif + +#include "globals.h" +#include "lispregs.h" +#include "validate.h" +#include "genesis/closure.h" +#include "genesis/static-symbols.h" + + .text + .align 2 + + .global fun_end_breakpoint_guts + .type fun_end_breakpoint_guts,%function +fun_end_breakpoint_guts: + addi.d $r20, $r0, 2 # li $tmp 2 + bge $r18, $r20, multiple_value_return # bge $nargs $tmp multiple_value_return + addi.d $r14, $r23, 0 # move $ocfp $csp + addi.d $r23, $r23, N_WORD_BYTES # addi $cfp $cfp N_WORD_BYTES + addi.d $r18, $r0, (1 << N_FIXNUM_TAG_BITS) # li $nargs (1 << N_FIXNUM_TAG_BITS) + addi.d $r5, $r16, 0 # move $a1 $NULL + addi.d $r6, $r16, 0 # move $a2 $NULL + addi.d $r7, $r16, 0 # move $a3 $NULL + +multiple_value_return: + + .global fun_end_breakpoint_trap + .type fun_end_breakpoint_trap,%function + +fun_end_breakpoint_trap: + break trap_FunEndBreakpoint + + .global fun_end_breakpoint_end +fun_end_breakpoint_end: + + .align 2 + .global do_pending_interrupt + .type do_pending_interrupt,%function +do_pending_interrupt: + break trap_PendingInterrupt + .byte trap_PendingInterrupt + .align 2 + ret + diff --git a/src/runtime/loongarch64-linux-os.c b/src/runtime/loongarch64-linux-os.c new file mode 100644 index 000000000..805a90d8e --- /dev/null +++ b/src/runtime/loongarch64-linux-os.c @@ -0,0 +1,134 @@ +/* + * This is the LoongArch Linux incarnation of arch-dependent OS-dependent + * routines. See also "linux-os.c". + */ + +/* + * This software is part of the SBCL system. See the README file for + * more information. + * + * This software is derived from the CMU CL system, which was + * written at Carnegie Mellon University and released into the + * public domain. The software is in the public domain and is + * provided with absolutely no warranty. See the COPYING and CREDITS + * files for more information. + */ + +#include <stdio.h> + +#ifdef __linux__ +#else +#include <sys/cachectl.h> +#endif +#include <sys/param.h> +#include <sys/file.h> +#include "genesis/sbcl.h" +#include "os.h" +#include "arch.h" +#include "globals.h" +#include "interrupt.h" +#include "interr.h" +#include "lispregs.h" + +#include <sys/types.h> +#include <signal.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <unistd.h> +#include <errno.h> + +#include "validate.h" + +#include <stdint.h> +#include <stddef.h> +#include <sys/ucontext.h> /* for ucontext_t / mcontext_t */ +#include <asm/sigcontext.h> /* for sctx_info, FPU_CTX_MAGIC, struct fpu_context */ + + +#define EXTCTX_SCAN_MAX_BYTES 16384 + +int +arch_os_thread_init(struct thread *thread) +{ + return 1; /* success */ +} + +int +arch_os_thread_cleanup(struct thread *thread) +{ + return 1; /* success */ +} + +os_context_register_t * +os_context_register_addr(os_context_t *context, int offset) +{ + return (os_context_register_t*)&(context->uc_mcontext.__gregs[offset]); +} + +os_context_register_t * +os_context_lr_addr(os_context_t *context) +{ + return os_context_register_addr(context, reg_LIP); +} + +sigset_t * +os_context_sigmask_addr(os_context_t *context) +{ + return &context->uc_sigmask; +} + +void +os_restore_fp_control(os_context_t *context) +{ + /* FIXME: Implement. */ +} + +os_context_register_t * +os_context_float_register_addr(os_context_t *uc, int offset) +{ + if (offset < 0 || offset >= 32) return NULL; /* bounds check */ + + mcontext_t *mc = &uc->uc_mcontext; + + uint8_t *p = (uint8_t *)&mc->__extcontext[0]; + size_t remaining = EXTCTX_SCAN_MAX_BYTES; + + while (remaining >= sizeof(struct sctx_info)) { + struct sctx_info *hdr = (struct sctx_info *)p; + + if (hdr->size < sizeof(struct sctx_info)) break; + + if (hdr->magic == FPU_CTX_MAGIC) { + if (hdr->size < (sizeof(struct sctx_info) + sizeof(struct fpu_context))) + return NULL; + + uintptr_t base = ((uintptr_t)p + sizeof(struct sctx_info) + 15) & ~((uintptr_t)15); + struct fpu_context *fpu = (struct fpu_context *)base; + return (os_context_register_t *)&(fpu->regs[offset]); + } + + size_t sz = hdr->size; + if (sz > remaining) break; + p += sz; + remaining -= sz; + } + return NULL; +} + + +void +os_flush_icache(os_vm_address_t address, os_vm_size_t length) +{ + os_vm_address_t end_address + = (os_vm_address_t)(((uintptr_t) address) + length); +#ifndef LISP_FEATURE_OS_PROVIDES_FLUSH_ICACHE + __builtin___clear_cache(address, end_address); +#endif +} + +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +int _stat(const char *pathname, struct stat *sb) {return stat(pathname, sb); } +int _lstat(const char *pathname, struct stat *sb) { return lstat(pathname, sb); } +int _fstat(int fd, struct stat *sb) { return fstat(fd, sb); } diff --git a/src/runtime/loongarch64-linux-os.h b/src/runtime/loongarch64-linux-os.h new file mode 100644 index 000000000..b440ce4c4 --- /dev/null +++ b/src/runtime/loongarch64-linux-os.h @@ -0,0 +1,9 @@ +#ifndef _LOONGARCH64_LINUX_OS_H +#define _LOONGARCH64_LINUX_OS_H + +typedef ucontext_t os_context_t; +typedef long os_context_register_t; + +#define OS_CONTEXT_PC(context) ((context)->uc_mcontext.__pc) + +#endif /* _LOONGARCH64_LINUX_OS_H */ diff --git a/src/runtime/loongarch64-lispregs.h b/src/runtime/loongarch64-lispregs.h new file mode 100644 index 000000000..5d0ca0657 --- /dev/null +++ b/src/runtime/loongarch64-lispregs.h @@ -0,0 +1,56 @@ +/* + * This software is part of the SBCL system. See the README file for + * more information. + * + * This software is derived from the CMU CL system, which was + * written at Carnegie Mellon University and released into the + * public domain. The software is in the public domain and is + * provided with absolutely no warranty. See the COPYING and CREDITS + * files for more information. + */ + +#ifdef LANGUAGE_ASSEMBLY +#define REG(num) $ ## num +#else +#define REG(num) num +#endif + +#define NREGS (32) + +#define reg_ZERO REG(0) +#define REG_RA REG(1) +#define reg_RA REG(1) +#define reg_TP REG(2) +#define reg_NSP REG(3) +#define reg_A0 REG(4) +#define reg_A1 REG(5) +#define reg_NL0 REG(6) +#define reg_NL1 REG(7) +#define reg_A2 REG(8) +#define reg_NL2 REG(9) +#define reg_A3 REG(10) +#define reg_NL3 REG(11) +#define reg_LIP REG(12) +#define reg_CFP REG(13) +#define reg_OCFP REG(14) +#define reg_LEXENV REG(15) +#define reg_NULL REG(16) +#define reg_CODE REG(17) +#define reg_NARGS REG(18) +#define reg_T7 REG(19) +#define reg_T8 REG(20) +#define reg_RSV REG(21) +#define reg_NFP REG(22) +#define reg_CSP REG(23) +#define reg_A4 REG(24) +#define reg_NL4 REG(25) +#define reg_A5 REG(26) +#define reg_NL5 REG(27) +#define reg_L0 REG(28) +#define reg_L1 REG(29) +#ifdef LISP_FEATURE_SB_THREAD +#define reg_THREAD REG(30) +#else +#define reg_L2 REG(30) +#endif +#define reg_CFUNC REG(31) -- 2.20.1