summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Jaeheon Yi <jaeheon@google.com> 2024-01-29 15:04:33 -0800
committer Jaeheon Yi <jaeheon@google.com> 2024-02-01 05:56:04 +0000
commit015400d51ead36109591f1b459fc0bdf57f52f1c (patch)
tree8276b27196747ee90a5b37662f79416e47fd0670
parentd9f18b27494413f8a5dbc8d9a27f5c4d9ec2f30d (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.S183
-rw-r--r--runtime/interpreter/mterp/riscv64/main.S78
-rw-r--r--runtime/nterp_helpers.cc17
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: