Skip to content

Conversation

@loganchien
Copy link
Contributor

This pull request adds a RISC-V JIT backend. This backend targets RV64IMAFD.

The RISC-V backend implementation is quite complete now:

  • PyPy RISC-V JIT can run on (a) SiFive Unmatched board with Ubuntu 24.04 and (b) qemu-user-static with a Ubuntu 24.04 RISC-V root file system.
  • All tests are passing or have reasonable workarounds.
  • The benchmark results can be found here.

Please check rpython/doc/riscv.rst for the instructions to cross-compile PyPy RISC-V JIT.

This commit adds minimalistic scaffold for `test_runner.py`. Most tests do not
pass because we haven't implemented the assembler yer.

Test: ./pytest.py rpython/jit/backend/riscv/test/test_runner.py
This commit adds essential build blocks (e.g. assembler, opassembler, regalloc)
to compile and run our first loop.

Test: pytest.py rpython/jit/backend/riscv/test/test_runner.py \
          -k test_compile_linear_loop
This commit supports greater range of integer immediates (64-bit).
Test: pytest.py rpython/jit/backend/riscv/test/test_runner.py \
          -k test_compile_linear_float_loop
This commit adds support for int_is_true, int_neg, int_invert, and int_is_zero.

This commit also passes test_int_operations.

Test: pytest.py rpython/jit/backend/riscv/test/test_runner.py \
          -k test_int_operations
This commit adds supports to cast_int_to_float and cast_float_to_int.

This commit also passes test_float_operations.

Test: pytest.py rpython/jit/backend/riscv/test/test_runner.py \
          -k test_float_operations
Test: pytest.py rpython/jit/backend/riscv/test/test_runner.py -k test_same_as
Test: pytest.py rpython/jit/backend/riscv/test/test_runner.py \
        -k test_cast_int_to_ptr
Test: pytest.py rpython/jit/backend/riscv/test/test_runner.py \
        -k test_cast_ptr_to_int

Progress: 27/156
Test: pytest.py rpython/jit/backend/riscv/test/test_runner.py \
        -k test_unused_result_int
Test: pytest.py rpython/jit/backend/riscv/test/test_runner.py \
        -k test_unused_result_float

Progress: 29/156
Test: pytest.py rpython/jit/backend/riscv/test/test_runner.py \
        -k test_passing_guards
Test: pytest.py rpython/jit/backend/riscv/test/test_runner.py \
        -k test_failing_guards

As a side-effect, the following test cases pass as well:

Test: pytest.py rpython/jit/backend/riscv/test/test_runner.py \
        -k test_integers_and_guards2
Test: pytest.py rpython/jit/backend/riscv/test/test_runner.py \
        -k test_integers_and_guards2_x
Test: pytest.py rpython/jit/backend/riscv/test/test_runner.py \
        -k test_integers_and_guards
Test: pytest.py rpython/jit/backend/riscv/test/test_runner.py \
        -k test_integers_and_guards_x
Test: pytest.py rpython/jit/backend/riscv/test/test_runner.py \
        -k test_integers_and_guards_uint
Test: pytest.py rpython/jit/backend/riscv/test/test_runner.py \
        -k test_integers_and_guards_uint_x
Test: pytest.py rpython/jit/backend/riscv/test/test_runner.py \
        -k test_floats_and_guards
Test: pytest.py rpython/jit/backend/riscv/test/test_runner.py \
        -k test_floats_and_guards_x
Test: pytest.py rpython/jit/backend/riscv/test/test_runner.py \
        -k test_nan_and_infinity
Test: pytest.py rpython/jit/backend/riscv/test/test_runner.py \
        -k test_guards_nongc

Progress: 46/156
This commit converts a integer comparison operation followed by a `guard_true`
or `guard_false` operation into a `BLT/BGE/BEQ/BNE` instruction.

Before this commit, int_le + guard_true is lowered into:

        slt x1, x2, x3
        bne x1, zero, cont
        ebreak  # bail out (to be patched)
    cont:

After this commit, it can be converted into:

        blt x2, x3, cont
        ebreak  # bail out (to be patched)
    cont:

Test: pytest.py rpython/jit/backend/riscv/test/test_runner.py \
        -k test_integers_and_guards
Test: test_zero_array
This commit adds `enter_portal_frame`/`leave_portal_frame`.  These ops are
needed by `test_basic.py`.  It seems that implementing these two as no-op
should be fine.
This commit adds support for `keepalive` op.  We need this op to pass
`test_recursive.py`.
This commit adds shadow stack header/footer to
`_call_header`/`_call_footer`.

Test: pytest.py rpython/jit/backend/riscv/test/test_zrpy_gc.py
          -k test_compile_framework_2
      pytest.py rpython/jit/backend/riscv/test/test_zrpy_gc.py
          -k test_compile_framework_3
      pytest.py rpython/jit/backend/riscv/test/test_zrpy_gc.py
          -k test_compile_framework_4
Test: pytest.py rpython/jit/backend/riscv/test/test_zrpy_gc.py -v
      # Need to `return False` in `can_use_nursery_malloc2`
This commit adds support for `call_malloc_nursery`,
`call_malloc_nursery_varsize_frame`, `call_malloc_nursery_varsize` and
`nursery_ptr_increment`.

These four ops are necessary to pass `test_zrpy_gc.py` when malloc
inlining is enabled.

Test: pytest.py rpython/jit/backend/riscv/test/test_zrpy_gc.py
This commit adds passing `test_ztranslation_basic`,
`test_ztranslation_call_assembler`, and `test_ztranslation_jit_stats`.
This commit changes how we load large integer immediates
(`imm < -SINT32_MIN or imm > SINT32_MAX`) or float immediates.

For large integer immediates, instead of building a full 64-bit integer
with a sequence of `lui + addiw + slli + addi` (up to 8 instructions),
we simply emit `auipc + ld` when the immediate can't be represented as a
32-bit signed integer.  For 32-bit signed integer, we continue using
`lui + addiw`.

For float immediates, instead of saving the immediate value in
`MachineDataBlockWrapper`, loading address with `load_int_imm`, and
loading the final float value with a `load_float`, we emit the float
immediate in the constant pool and emit a pc-relative `load_float`.
This saves a word for address and two instructions to load address.
This also avoids pointer chasing.

Test: pytest.py rpython/jit/backend/riscv/test/test_runner.py \
        -k test_int_operations
Test: pytest.py rpython/jit/backend/riscv/test/test_runner.py \
        -k test_float_operations
This commit adds support for large frame slot offsets.  Before this
commit, the code generator assumes the frame slot offsets is always
addressable with signed 12-bit immediate.  But, it turns out 12-bit is
not enough.  `lib_python_tests.py` `test_tarfile` triggers the assertion
in the code generator.

This commit fixes the problem by emitting `load_imm + ADD` when the
immediate is too large for `ADDI`.

Test: pypy2.7 testrunner/lib_python_tests.py -v -k test_tarfile
This commit skips several micronumpy test cases that encode extra bits
in the significand bits of NaN and/or check the signbit of NaN.

RISC-V ISA doesn't preserve the NaN payload bits.  Many float
instructions may canonicalize NaN into the positive canonical NaN.
Thus, these tests don't work in RISC-V.
This commit swaps the order of `x0` and `x10` in JITFrame. In the RISC-V
calling convention, the return register is `x10`. However, some data
structure (e.g. `AbstractFailDescr`) assumes the returned value being
stored to the offset 0. This commit swaps the order so that we can avoid
the confusion.
The ResOperation order of `%(align_check)s` and
`guard_not_invalidated(descr=...)` in the RISC-V backend is different
from the order of other backends. This commit works around the test by
using a different "expected" sequence for RISC-V.

Note: There is no observable performance difference between
`logical_xor` and `logical_and` (on SiFive Unmatched), thus we should be
fine.

time reduce.logical_xor: 8.49118113518
time reduce.logical_and: 7.69018793106

Test: python testrunner/pypyjit_tests.py -v -k test_reduce_logical_and
@mattip
Copy link
Member

mattip commented Aug 13, 2024

Thanks. The benchmark results are promising. CI checks are passing. I would lean towards merging this as-is and then, if there are some regressions on other platforms, fixing where needed.

@mattip mattip merged commit d732218 into pypy:main Aug 14, 2024
@mattip
Copy link
Member

mattip commented Aug 14, 2024

Thanks @loganchien let's put this in.

@cfbolz
Copy link
Member

cfbolz commented Aug 14, 2024

@loganchien thank you so much for your work! This is really amazing!

@loganchien loganchien deleted the rv64 branch August 21, 2024 04:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants