diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 16bce57..ffff574 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -72,7 +72,7 @@ jobs: strategy: matrix: build-type: [ Release, Debug ] - llvm-version: [13, 14] + llvm-version: [13, 14, 15] arch: - name: armhf system-processor: arm @@ -84,6 +84,18 @@ jobs: system-processor: aarch64 triple: aarch64-linux-gnu rtld: ld-linux-aarch64.so.1 + - name: riscv64 + system-processor: riscv64 + triple: riscv64-linux-gnu + rtld: ld-linux-riscv64-lp64d.so.1 + # lld versions prior to 15 do not support R_RISCV_ALIGN relocations + exclude: + - llvm-version: 13 + arch: + name: riscv64 + - llvm-version: 14 + arch: + name: riscv64 # Don't abort runners if a single one fails fail-fast: false runs-on: ubuntu-latest diff --git a/ANNOUNCE b/ANNOUNCE index 404e75c..80c6964 100644 --- a/ANNOUNCE +++ b/ANNOUNCE @@ -8,6 +8,11 @@ programs. Highlights of this release include: +- Initial support for RISC-V 64-bit (rv64) including an architecture-specific + objc_msgSend, and block trampoline implementation. Please note that + double-precision floating-point support (rv64d) is required for the + objc_msgSend implementation. +- Initial support for Windows on ARM64 with fast-path objc_msgSend. - Numerous improvements to the Objective-C++ exception interoperation code. The runtime now dynamically detects whether the libcxxrt, libsupc++, or libc++abi variant of the Itanium C++ Exception ABI is being used. This is @@ -18,7 +23,6 @@ Highlights of this release include: - The minimum CMake version has been bumped to 3.16, which supports Objective-C. This support is now used, simplifying the build. - Support for GC mode is gone. Apple dropped support for this a long time ago. -- Support for Windows on Arm has been added. - `objc_setUncaughtExceptionHandler` is added, which avoids consuming code needing to access a library-owned global. - The selector-table code has been rewritten in C++, improving performance of diff --git a/block_trampolines.S b/block_trampolines.S index 6107e7a..12da0ee 100644 --- a/block_trampolines.S +++ b/block_trampolines.S @@ -98,6 +98,24 @@ #define ARG1 $a1 #define ARG2 $a2 +#elif defined(__riscv) && (__riscv_xlen == 64) +//////////////////////////////////////////////////////////////////////////////// +// RISC-V trampoline +//////////////////////////////////////////////////////////////////////////////// +.macro trampoline arg0, arg1 + auipc t6, 0xFFFFF // pc + -0x1000 + mv \arg1, \arg0 + ld \arg0, 0(t6) + ld t6, 8(t6) + jr t6 +.endm + +#define ARG0 a0 +#define ARG1 a1 +#define ARG2 a2 +#define SARG0 ARG1 +#define SARG1 ARG2 + #elif defined(__ARM_ARCH_ISA_A64) //////////////////////////////////////////////////////////////////////////////// // AArch64 (ARM64) trampoline diff --git a/objc/message.h b/objc/message.h index 62aadab..b267e95 100644 --- a/objc/message.h +++ b/objc/message.h @@ -6,8 +6,11 @@ #ifndef _OBJC_MESSAGE_H_ #define _OBJC_MESSAGE_H_ -#if defined(__x86_64) || defined(__i386) || defined(__arm__) || \ - defined(__mips_n64) || defined(__mips_n32) || defined(__ARM_ARCH_ISA_A64) +#if defined(__x86_64) || defined(__i386) || defined(__arm__) || \ + defined(__mips_n64) || defined(__mips_n32) || \ + defined(__ARM_ARCH_ISA_A64) || \ + (defined(__riscv) && __riscv_xlen == 64 && \ + defined(__riscv_float_abi_double)) /** * Standard message sending function. This function must be cast to the * correct types for the function before use. The first argument is the @@ -41,7 +44,7 @@ id objc_msgSend(id self, SEL _cmd, ...); * integer) structures. */ OBJC_PUBLIC -#ifdef __cplusplus +#ifdef __cplusplus id objc_msgSend_stret(id self, SEL _cmd, ...); #else void objc_msgSend_stret(id self, SEL _cmd, ...); diff --git a/objc_msgSend.S b/objc_msgSend.S index cbce6a0..5427594 100644 --- a/objc_msgSend.S +++ b/objc_msgSend.S @@ -8,6 +8,8 @@ #include "objc_msgSend.arm.S" #elif defined(__ARM_ARCH_ISA_A64) #include "objc_msgSend.aarch64.S" +#elif defined(__riscv) && (__riscv_xlen == 64) && defined(__riscv_float_abi_double) +#include "objc_msgSend.riscv64.S" #elif defined(__mips_n64) || defined(__mips_n32) #include "objc_msgSend.mips.S" #else diff --git a/objc_msgSend.riscv64.S b/objc_msgSend.riscv64.S new file mode 100644 index 0000000..c99a4dd --- /dev/null +++ b/objc_msgSend.riscv64.S @@ -0,0 +1,141 @@ +#define ARGUMENT_SPILL_SIZE (10*8 + 8*8) + +.macro MSGSEND receiver, sel + .cfi_startproc + beqz \receiver, 3f // Skip everything if receiver is nil + + andi t0, \receiver, SMALLOBJ_MASK + bnez t0, 5f + + ld t0, 0(\receiver) // Load class into t0 +0: + ld t0, DTABLE_OFFSET(t0) // dtable -> t0 + ld t1, 0(\sel) // selector->index -> t1 + ld t2, SHIFT_OFFSET(t0) // dtable->shift -> t2 + + li t3, 8 + beq t2, t3, 1f + beqz t2, 2f + + srli t2, t1, 16-3 // Extract byte 3 of sel index and multiply by 2^3 + and t2, t2, 0x7F8 // Mask target byte + // Example: ((0xCAFEBA >> 13) & 0x7f8) == (0xCA << 3) + add t2, t0, t2 // t2 = dtable address + offset + ld t0, DATA_OFFSET(t2) // Load, adding in the data offset +1: + srli t2, t1, 8-3 // Extract byte 2 of sel index and multiply by 2^3 + and t2, t2, 0x7F8 // Mask target byte + add t2, t0, t2 // t2 = dtable address + offset + ld t0, DATA_OFFSET(t2) // Load, adding in the data offset +2: + slli t2, t1, 3 // Multiply by 2^3 + and t2, t2, 0x7F8 // Mask target byte + add t2, t0, t2 // t2 = dtable address + offset + ld t0, DATA_OFFSET(t2) // Load, adding in the data offset + // Slot pointer is now in t0 + + beqz t0, 4f // If the slot is nil, go to the C path + + ld t0, SLOT_OFFSET(t0) // Load the method from the slot + jalr zero, t0, 0 // Tail-call the method + +3: + li \receiver, 0 + li \sel, 0 + fmv.d.x fa0, zero + fmv.d.x fa1, zero + jalr zero, ra, 0 + +4: + add sp, sp, -(ARGUMENT_SPILL_SIZE) + + // Spill function arguments + sd a0, 0(sp) + sd a1, 8(sp) + sd a2, 16(sp) + sd a3, 24(sp) + sd a4, 32(sp) + sd a5, 40(sp) + sd a6, 48(sp) + sd a7, 56(sp) + + // Spill FP arguments + fsd fa0, 64(sp) + fsd fa1, 72(sp) + fsd fa2, 80(sp) + fsd fa3, 88(sp) + fsd fa4, 96(sp) + fsd fa5, 104(sp) + fsd fa6, 112(sp) + fsd fa7, 120(sp) + + sd fp, 128(sp) + sd ra, 136(sp) + + add fp, sp, 128 + add sp, sp, -16 + + sd \receiver, 0(sp) // it is convenient if \receiver is spilled at sp + + .cfi_def_cfa fp, 16 + .cfi_offset fp, -16 + .cfi_offset ra, -8 + + add a0, sp, zero // &self in first argument + call CDECL(slowMsgLookup) + + add t0, a0, zero // IMP -> t0 + + ld a0, 16(sp) + ld a1, 24(sp) + ld a2, 32(sp) + ld a3, 40(sp) + ld a4, 48(sp) + ld a5, 56(sp) + ld a6, 64(sp) + ld a7, 72(sp) + + fld fa0, 80(sp) + fld fa1, 88(sp) + fld fa2, 96(sp) + fld fa3, 104(sp) + fld fa4, 112(sp) + fld fa5, 120(sp) + fld fa6, 128(sp) + fld fa7, 136(sp) + + ld fp, 144(sp) + ld ra, 152(sp) + + ld \receiver, 0(sp) + + add sp, sp, ARGUMENT_SPILL_SIZE + add sp, sp, 16 + + jalr zero, t0, 0 // Tail-call the method + +5: + // Load address of SmallObjectClasses + auipc t1, %pcrel_hi(CDECL(SmallObjectClasses)) + addi t1, t1, %pcrel_lo(5b) + + // Calculate array offset (INDEX * 2^3) + slli t0, t0, 3 + add t0, t1, t0 + + ld t0, 0(t0) + + j 0b + .cfi_endproc +.endm + +.globl CDECL(objc_msgSend_fpret) +TYPE_DIRECTIVE(CDECL(objc_msgSend_fpret), %function) +.globl CDECL(objc_msgSend) +TYPE_DIRECTIVE(CDECL(objc_msgSend), %function) +.globl CDECL(objc_msgSend_stret) +CDECL(objc_msgSend): +CDECL(objc_msgSend_fpret): + MSGSEND a0, a1 +CDECL(objc_msgSend_stret): + MSGSEND a1, a2 // Pointer to stack frame in a0