diff options
author | 2024-01-29 15:04:33 -0800 | |
---|---|---|
committer | 2024-02-01 05:56:04 +0000 | |
commit | 015400d51ead36109591f1b459fc0bdf57f52f1c (patch) | |
tree | 8276b27196747ee90a5b37662f79416e47fd0670 | |
parent | d9f18b27494413f8a5dbc8d9a27f5c4d9ec2f30d (diff) |
riscv64: BRANCH macro and related opcodes
To exercise the OSR logic, we additionally run all flavors
on 721-osr, 570-checker-osr, and 570-checker-osr-locals.
(1) setup
lunch aosp_riscv64-trunk-userdebug
export ART_TEST_SSH_USER=ubuntu
export ART_TEST_SSH_HOST=localhost
export ART_TEST_SSH_PORT=10001
export ART_TEST_ON_VM=true
. art/tools/buildbot-utils.sh
art/tools/buildbot-build.sh --target
# Create, boot and configure the VM.
art/tools/buildbot-vm.sh create
art/tools/buildbot-vm.sh boot
art/tools/buildbot-vm.sh setup-ssh # password: 'ubuntu'
art/tools/buildbot-cleanup-device.sh
art/tools/buildbot-setup-device.sh
art/tools/buildbot-sync.sh
(2) test
art/test.py --target -r --no-prebuild --ndebug --64 -j 12 --cdex-none --interpreter
Test: Run these opcodes against all interpreter tests on a Linux RISC-V VM.
Test: Run these opcodes against all flavors on a Cuttlefish VM with these tests:
% art/test/testrunner/testrunner.py -j5 --target --64 --cdex-none --64 721-osr
% art/test/testrunner/testrunner.py -j5 --target --64 --cdex-none --64 570-checker-osr
% art/test/testrunner/testrunner.py -j5 --target --64 --cdex-none --64 570-checker-osr-locals
Test: Cuttlefish boot
Test: m check_cfi
Bug: 283082047
Change-Id: I257545c9ebae5b6829fc6ff8cc220522060bbb86
-rw-r--r-- | runtime/interpreter/mterp/riscv64/control_flow.S | 183 | ||||
-rw-r--r-- | runtime/interpreter/mterp/riscv64/main.S | 78 | ||||
-rw-r--r-- | runtime/nterp_helpers.cc | 17 |
3 files changed, 235 insertions, 43 deletions
diff --git a/runtime/interpreter/mterp/riscv64/control_flow.S b/runtime/interpreter/mterp/riscv64/control_flow.S index e41c7e037e..215439cb41 100644 --- a/runtime/interpreter/mterp/riscv64/control_flow.S +++ b/runtime/interpreter/mterp/riscv64/control_flow.S @@ -58,6 +58,54 @@ call art_quick_deliver_exception // args a0, a1 unimp +// goto +AA +// Format 10t: AA|28 +// Unconditionally jump to the indicated instruction. +// Note: The branch offset must not be 0. +%def op_goto(): + srliw t0, xINST, 8 // t0 := AA (zext) + sext.b t0, t0 // t0 := +AA (sext) + BRANCH units=t0 + +// goto/16 +AAAA +// Format 20t: 00|29 AAAA +// Unconditionally jump to the indicated instruction. +// Note: The branch offset must not be 0. +%def op_goto_16(): + FETCH t0, 1, signed=1 // t0 := +AAAA (sext) + BRANCH units=t0 + +// goto/32 +AAAAAAAA +// Format 30t: 00|2a AAAA(lo) AAAA(hi) +// Unconditionally jump to the indicated instruction. +%def op_goto_32(): + FETCH t0, 1, signed=1, width=32 // t0 := +AAAAAAAA (sext) + BRANCH units=t0 + +// packed-switch vAA, +BBBBBBBB +// Format 31t: AA|2b BBBB(lo) BBBB(hi) +// Jump to a new instruction based on the value in the given register, using a table of offsets +// corresponding to each value in a particular integral range, or fall through to the next +// instruction if there is no match. +%def op_packed_switch(is_packed=True): + srliw t0, xINST, 8 // t0 := AA + FETCH t1, count=1, signed=1, width=32 // t1 := +BBBBBBBB (sext) +% get_vreg("a1", "t0") # a1 := vAA + sh1add a0, t1, xPC // a0 := +BBBBBBBB * 2 + xPC +% if is_packed: + call NterpDoPackedSwitch // args a0 (switchData), a1 (value) +% else: + call NterpDoSparseSwitch // args a0 (switchData), a1 (value) +%#: + BRANCH units=a0 + +// sparse-switch vAA, +BBBBBBBB +// Format 31t: AA|2c BBBB(lo) BBBB(hi) +// Jump to a new instruction based on the value in the given register, using an ordered table of +// value-offset pairs, or fall through to the next instruction if there is no match. +%def op_sparse_switch(): +% op_packed_switch(is_packed=False) + // cmp-long vAA, vBB, vCC // Format 23x: AA|31 CC|BB %def op_cmp_long(): @@ -76,59 +124,120 @@ GET_INST_OPCODE t0 GOTO_OPCODE t0 -%def bincmp(condition=""): - unimp - -%def zcmp(condition=""): - unimp +// Common helper for if-test. +// Format 22t: B|A|op CCCC +%def bincmp(op): + srliw t0, xINST, 8 // t0 := B|A + srliw t1, xINST, 12 // t1 := B + andi t0, t0, 0xF // t0 := A +% get_vreg("t0", "t0") # t0 := vA +% get_vreg("t1", "t1") # t1 := vB + b${op} t0, t1, .L${opcode}_branch -%def op_goto(): - unimp - -%def op_goto_16(): - unimp + FETCH_ADVANCE_INST 2 + GET_INST_OPCODE t2 + GOTO_OPCODE t2 -%def op_goto_32(): - unimp +.L${opcode}_branch: + FETCH t2, count=1, signed=1 // t2 := +CCCC (sext) + BRANCH units=t2 +// if-eq vA, vB, +CCCC +// Format 22t: B|A|32 CCCC +// Branch to the given destination if the given two registers' values compare as specified. +// Note: The branch offset must not be 0. %def op_if_eq(): -% bincmp(condition="eq") +% bincmp(op="eq") -%def op_if_eqz(): -% zcmp(condition="eq") +// if-ne vA, vB, +CCCC +// Format 22t: B|A|33 CCCC +// Branch to the given destination if the given two registers' values compare as specified. +// Note: The branch offset must not be 0. +%def op_if_ne(): +% bincmp(op="ne") -%def op_if_ge(): -% bincmp(condition="ge") +// if-lt vA, vB, +CCCC +// Format 22t: B|A|34 CCCC +// Branch to the given destination if the given two registers' values compare as specified. +// Note: The branch offset must not be 0. +%def op_if_lt(): +% bincmp(op="lt") -%def op_if_gez(): -% zcmp(condition="ge") +// if-ge vA, vB, +CCCC +// Format 22t: B|A|35 CCCC +// Branch to the given destination if the given two registers' values compare as specified. +// Note: The branch offset must not be 0. +%def op_if_ge(): +% bincmp(op="ge") +// if-gt vA, vB, +CCCC +// Format 22t: B|A|36 CCCC +// Branch to the given destination if the given two registers' values compare as specified. +// Note: The branch offset must not be 0. %def op_if_gt(): -% bincmp(condition="gt") - -%def op_if_gtz(): -% zcmp(condition="gt") +% bincmp(op="gt") +// if-le vA, vB, +CCCC +// Format 22t: B|A|37 CCCC +// Branch to the given destination if the given two registers' values compare as specified. +// Note: The branch offset must not be 0. %def op_if_le(): -% bincmp(condition="le") +% bincmp(op="le") -%def op_if_lez(): -% zcmp(condition="le") +// Common helper for if-testz. +// Format 21t: AA|op BBBB +%def zcmp(op): + srliw t0, xINST, 8 // t0 := AA +% get_vreg("t0", "t0") # t0 := vAA + b${op} t0, .L${opcode}_branch -%def op_if_lt(): -% bincmp(condition="lt") + FETCH_ADVANCE_INST 2 + GET_INST_OPCODE t1 + GOTO_OPCODE t1 -%def op_if_ltz(): -% zcmp(condition="lt") +.L${opcode}_branch: + FETCH t1, count=1, signed=1 // t1 := +BBBB (sext) + BRANCH units=t1 -%def op_if_ne(): -% bincmp(condition="ne") +// if-eqz vAA, +BBBB +// Format 21t: AA|38 BBBB +// Branch to the given destination if the given register's value compares with 0 as specified. +// Note: The branch offset must not be 0. +%def op_if_eqz(): +% zcmp(op="eqz") +// if-nez vAA, +BBBB +// Format 21t: AA|39 BBBB +// Branch to the given destination if the given register's value compares with 0 as specified. +// Note: The branch offset must not be 0. %def op_if_nez(): -% zcmp(condition="ne") +% zcmp(op="nez") -%def op_packed_switch(func="NterpDoPackedSwitch"): - unimp +// if-ltz vAA, +BBBB +// Format 21t: AA|3a BBBB +// Branch to the given destination if the given register's value compares with 0 as specified. +// Note: The branch offset must not be 0. +%def op_if_ltz(): +% zcmp(op="ltz") + +// if-gez vAA, +BBBB +// Format 21t: AA|3b BBBB +// Branch to the given destination if the given register's value compares with 0 as specified. +// Note: The branch offset must not be 0. +%def op_if_gez(): +% zcmp(op="gez") + +// if-gtz vAA, +BBBB +// Format 21t: AA|3c BBBB +// Branch to the given destination if the given register's value compares with 0 as specified. +// Note: The branch offset must not be 0. +%def op_if_gtz(): +% zcmp(op="gtz") + +// if-lez vAA, +BBBB +// Format 21t: AA|3d BBBB +// Branch to the given destination if the given register's value compares with 0 as specified. +// Note: The branch offset must not be 0. +%def op_if_lez(): +% zcmp(op="lez") -%def op_sparse_switch(): -% op_packed_switch(func="NterpDoSparseSwitch") diff --git a/runtime/interpreter/mterp/riscv64/main.S b/runtime/interpreter/mterp/riscv64/main.S index d2be1bc81e..bfc49c0c20 100644 --- a/runtime/interpreter/mterp/riscv64/main.S +++ b/runtime/interpreter/mterp/riscv64/main.S @@ -150,7 +150,7 @@ END \name .endm .macro DO_SUSPEND_CHECK continue - lw t0, THREAD_FLAGS_OFFSET(xSELF) + lwu t0, THREAD_FLAGS_OFFSET(xSELF) andi t0, t0, THREAD_SUSPEND_OR_CHECKPOINT_REQUEST beqz t0, \continue EXPORT_PC @@ -253,30 +253,54 @@ END \name // - xSELF // Clobbers: t0 .macro CHECK_AND_UPDATE_SHARED_MEMORY_METHOD if_hot, if_not_hot - lw t0, ART_METHOD_ACCESS_FLAGS_OFFSET(a0) + lwu t0, ART_METHOD_ACCESS_FLAGS_OFFSET(a0) BRANCH_IF_BIT_CLEAR t0, t0, ART_METHOD_IS_MEMORY_SHARED_FLAG_BIT, \if_hot - lw t0, THREAD_SHARED_METHOD_HOTNESS_OFFSET(xSELF) + lwu t0, THREAD_SHARED_METHOD_HOTNESS_OFFSET(xSELF) // t0 := hotness beqz t0, \if_hot - addi t0, t0, -1 // Reduce hotness + addi t0, t0, -1 // increase hotness sw t0, THREAD_SHARED_METHOD_HOTNESS_OFFSET(xSELF) j \if_not_hot .endm +// Update xPC by \units code units. On back edges, perform hotness and suspend. +.macro BRANCH units + sh1add xPC, \units, xPC + blez \units, 2f // If branch is <= 0, increase hotness and do a suspend check. +1: + FETCH_INST + GET_INST_OPCODE t0 + GOTO_OPCODE t0 +2: + ld a0, (sp) + lhu t0, ART_METHOD_HOTNESS_COUNT_OFFSET(a0) // t0 := hotness +#if (NTERP_HOTNESS_VALUE != 0) +#error Expected 0 for hotness value +#endif + // If the counter is at zero (hot), handle it in the runtime. + beqz t0, 3f + addi t0, t0, -1 // increase hotness + sh t0, ART_METHOD_HOTNESS_COUNT_OFFSET(a0) + DO_SUSPEND_CHECK continue=1b + j 1b +3: + tail NterpHandleHotnessOverflow // arg a0 (ArtMethod*) +.endm + // Increase method hotness before starting the method. // Hardcoded: // - a0: ArtMethod* // Clobbers: t0 .macro START_EXECUTING_INSTRUCTIONS ld a0, (sp) - lhu t0, ART_METHOD_HOTNESS_COUNT_OFFSET(a0) + lhu t0, ART_METHOD_HOTNESS_COUNT_OFFSET(a0) // t0 := hotness #if (NTERP_HOTNESS_VALUE != 0) #error Expected 0 for hotness value #endif // If the counter is at zero (hot), handle it in the runtime. beqz t0, 3f - addi t0, t0, -1 + addi t0, t0, -1 // increase hotness sh t0, ART_METHOD_HOTNESS_COUNT_OFFSET(a0) 1: DO_SUSPEND_CHECK continue=2f @@ -599,6 +623,48 @@ NterpInvokePolymorphicRange: NterpInvokeCustomRange: % nterp_invoke_custom_range() +// Arg a0: ArtMethod* +NterpHandleHotnessOverflow: + CHECK_AND_UPDATE_SHARED_MEMORY_METHOD if_hot=.Lhotspill_hot, if_not_hot=.Lhotspill_suspend +.Lhotspill_hot: + mv a1, xPC + mv a2, xFP + call nterp_hot_method // args a0, a1, a2 + bnez a0, .Lhotspill_osr +.Lhotspill_advance: + FETCH_INST + GET_INST_OPCODE t0 + GOTO_OPCODE t0 +.Lhotspill_osr: + // a0 = OsrData* + // Drop most of the current nterp frame, but keep the callee-saves. + // The nterp callee-saves (count and layout) match the OSR frame's callee-saves. + ld sp, -8(xREFS) // caller's interpreted frame pointer + .cfi_def_cfa sp, NTERP_SIZE_SAVE_CALLEE_SAVES + lwu t0, OSR_DATA_FRAME_SIZE(a0) + addi t0, t0, -NTERP_SIZE_SAVE_CALLEE_SAVES // t0 := osr frame - callee saves, in bytes + mv s7, sp // Remember CFA in a callee-save register. + .cfi_def_cfa_register s7 + sub sp, sp, t0 // OSR size guaranteed to be stack aligned (16 bytes). + + addi t1, a0, OSR_DATA_MEMORY // t1 := read start + add t1, t1, t0 // t1 := read end (exclusive) + mv t2, s7 // t2 := write end (exclusive) + // t0 >= 8 (OSR places ArtMethod* at bottom of frame), so loop will terminate. +.Lhotspill_osr_copy_loop: + addi t1, t1, -8 + ld t3, (t1) + addi t2, t2, -8 + sd t3, (t2) + bne t2, sp, .Lhotspill_osr_copy_loop + + ld s8, OSR_DATA_NATIVE_PC(a0) // s8 := native PC; jump after free + call free // arg a0; release OsrData* + jr s8 // Jump to the compiled code. +.Lhotspill_suspend: + DO_SUSPEND_CHECK continue=.Lhotspill_advance + j .Lhotspill_advance + // This is the logical end of ExecuteNterpImpl, where the frame info applies. .cfi_endproc diff --git a/runtime/nterp_helpers.cc b/runtime/nterp_helpers.cc index 86f465a035..cfe5610239 100644 --- a/runtime/nterp_helpers.cc +++ b/runtime/nterp_helpers.cc @@ -283,11 +283,28 @@ bool CanMethodUseNterp(ArtMethod* method, InstructionSet isa) { case Instruction::FILLED_NEW_ARRAY_RANGE: case Instruction::FILL_ARRAY_DATA: case Instruction::THROW: + case Instruction::GOTO: + case Instruction::GOTO_16: + case Instruction::GOTO_32: + case Instruction::PACKED_SWITCH: + case Instruction::SPARSE_SWITCH: case Instruction::CMPL_FLOAT: case Instruction::CMPG_FLOAT: case Instruction::CMPL_DOUBLE: case Instruction::CMPG_DOUBLE: case Instruction::CMP_LONG: + case Instruction::IF_EQ: + case Instruction::IF_NE: + case Instruction::IF_LT: + case Instruction::IF_GE: + case Instruction::IF_GT: + case Instruction::IF_LE: + case Instruction::IF_EQZ: + case Instruction::IF_NEZ: + case Instruction::IF_LTZ: + case Instruction::IF_GEZ: + case Instruction::IF_GTZ: + case Instruction::IF_LEZ: case Instruction::AGET: case Instruction::AGET_WIDE: case Instruction::AGET_OBJECT: |